def read_beamfits(self, filename, run_check=True, check_extra=True, run_check_acceptability=True): """ Read the data from a beamfits file. Args: filename: The beamfits file to write to. run_check: Option to check for the existence and proper shapes of required parameters after reading in the file. Default is True. check_extra: Option to check optional parameters as well as required ones. Default is True. run_check_acceptability: Option to check acceptability of the values of required parameters after reading in the file. Default is True. """ F = fits.open(filename) primary_hdu = F[0] primary_header = primary_hdu.header.copy() hdunames = uvutils.fits_indexhdus(F) # find the rest of the tables data = primary_hdu.data # only support simple antenna_types for now. # support for phased arrays should be added self.set_simple() self.beam_type = primary_header.pop('BTYPE', None) if self.beam_type is not None: self.beam_type = self.beam_type.lower() else: bunit = primary_header.pop('BUNIT', None) if bunit is not None and bunit.lower().strip() == 'jy/beam': self.beam_type = 'power' if self.beam_type == 'intensity': self.beam_type = 'power' n_dimensions = primary_header.pop('NAXIS') ctypes = [primary_header[ctype] for ctype in (key for key in primary_header if 'ctype' in key.lower())] self.pixel_coordinate_system = primary_header.pop('COORDSYS', None) if self.pixel_coordinate_system is None: if ctypes[0] == 'Pix_Ind': self.pixel_coordinate_system = 'healpix' else: for cs, coords in self.coordinate_system_dict.iteritems(): if coords == ctypes[0:2]: coord_list = ctypes[0:2] self.pixel_coordinate_system = cs else: if self.pixel_coordinate_system == 'healpix': if ctypes[0] != 'Pix_Ind': raise ValueError('First axis must be "Pix_Ind" for healpix beams') else: coord_list = ctypes[0:2] if coord_list != self.coordinate_system_dict[self.pixel_coordinate_system]: raise ValueError('Coordinate axis list does not match coordinate system') if self.pixel_coordinate_system == 'healpix': # get pixel values out of HPX_IND extension hpx_hdu = F[hdunames['HPX_INDS']] self.Npixels = hpx_hdu.header['NAXIS2'] hpx_data = hpx_hdu.data self.pixel_array = hpx_data['hpx_inds'] ax_nums = hpx_primary_ax_nums self.nside = primary_header.pop('NSIDE', None) self.ordering = primary_header.pop('ORDERING', None) data_Npixels = primary_header.pop('NAXIS' + str(ax_nums['pixel'])) if data_Npixels != self.Npixels: raise ValueError('Number of pixels in HPX_IND extension does ' 'not match number of pixels in data array') else: ax_nums = reg_primary_ax_nums self.Naxes1 = primary_header.pop('NAXIS' + str(ax_nums['img_ax1'])) self.Naxes2 = primary_header.pop('NAXIS' + str(ax_nums['img_ax2'])) self.axis1_array = uvutils.fits_gethduaxis(primary_hdu, ax_nums['img_ax1']) self.axis2_array = uvutils.fits_gethduaxis(primary_hdu, ax_nums['img_ax2']) n_efield_dims = max([ax_nums[key] for key in ax_nums]) if self.beam_type == 'power': self.data_array = data if primary_header.pop('CTYPE' + str(ax_nums['feed_pol'])).lower().strip() == 'stokes': self.Npols = primary_header.pop('NAXIS' + str(ax_nums['feed_pol'])) self.polarization_array = np.int32(uvutils.fits_gethduaxis(primary_hdu, ax_nums['feed_pol'])) self.set_power() elif self.beam_type == 'efield': self.set_efield() if n_dimensions < n_efield_dims: raise (ValueError, 'beam_type is efield and data dimensionality is too low') complex_arrs = np.split(data, 2, axis=0) self.data_array = np.squeeze(complex_arrs[0] + 1j * complex_arrs[1], axis=0) if primary_header.pop('CTYPE' + str(ax_nums['feed_pol'])).lower().strip() == 'feedind': self.Nfeeds = primary_header.pop('NAXIS' + str(ax_nums['feed_pol'])) feedlist = primary_header.pop('FEEDLIST', None) if feedlist is not None: self.feed_array = np.array(feedlist[1:-1].split(', ')) else: raise ValueError('Unknown beam_type: {type}, beam_type should be ' '"efield" or "power".'.format(type=self.beam_type)) self.data_normalization = primary_header.pop('NORMSTD', None) self.telescope_name = primary_header.pop('TELESCOP') self.feed_name = primary_header.pop('FEED', None) self.feed_version = primary_header.pop('FEEDVER', None) self.model_name = primary_header.pop('MODEL', None) self.model_version = primary_header.pop('MODELVER', None) # shapes if primary_header.pop('CTYPE' + str(ax_nums['freq'])).lower().strip() == 'freq': self.Nfreqs = primary_header.pop('NAXIS' + str(ax_nums['freq'])) if n_dimensions > ax_nums['spw'] - 1: if primary_header.pop('CTYPE' + str(ax_nums['spw'])).lower().strip() == 'if': self.Nspws = primary_header.pop('NAXIS' + str(ax_nums['spw']), None) # subtract 1 to be zero-indexed self.spw_array = uvutils.fits_gethduaxis(primary_hdu, ax_nums['spw']) - 1 if n_dimensions > ax_nums['basisvec'] - 1: if primary_header.pop('CTYPE' + str(ax_nums['basisvec'])).lower().strip() == 'vecind': self.Naxes_vec = primary_header.pop('NAXIS' + str(ax_nums['basisvec']), None) if (self.Nspws is None or self.Naxes_vec is None) and self.beam_type == 'power': if self.Nspws is None: self.Nspws = 1 self.spw_array = np.array([0]) if self.Naxes_vec is None: self.Naxes_vec = 1 # add extra empty dimensions to data_array as appropriate while len(self.data_array.shape) < n_efield_dims - 1: self.data_array = np.expand_dims(self.data_array, axis=0) self.freq_array = uvutils.fits_gethduaxis(primary_hdu, ax_nums['freq']) self.freq_array.shape = (self.Nspws,) + self.freq_array.shape self.history = str(primary_header.get('HISTORY', '')) if not uvutils.check_history_version(self.history, self.pyuvdata_version_str): self.history += self.pyuvdata_version_str while 'HISTORY' in primary_header.keys(): primary_header.remove('HISTORY') # remove standard FITS header items that are still around std_fits_substrings = ['SIMPLE', 'BITPIX', 'EXTEND', 'BLOCKED', 'GROUPS', 'PCOUNT', 'BSCALE', 'BZERO', 'NAXIS', 'PTYPE', 'PSCAL', 'PZERO', 'CTYPE', 'CRVAL', 'CRPIX', 'CDELT', 'CROTA', 'CUNIT'] for key in primary_header.keys(): for sub in std_fits_substrings: if key.find(sub) > -1: primary_header.remove(key) # find all the remaining header items and keep them as extra_keywords for key in primary_header: if key == 'COMMENT': self.extra_keywords[key] = str(primary_header.get(key)) elif key != '': self.extra_keywords[key] = primary_header.get(key) if self.beam_type == 'efield': # read BASISVEC HDU basisvec_hdu = F[hdunames['BASISVEC']] self.basis_vector_array = basisvec_hdu.data basisvec_header = basisvec_hdu.header if self.pixel_coordinate_system == 'healpix': basisvec_ax_nums = hxp_basisvec_ax_nums if basisvec_header['CTYPE' + str(basisvec_ax_nums['pixel'])] != 'Pix_Ind': raise ValueError('First axis in BASISVEC HDU must be "Pix_Ind" for healpix beams') basisvec_Npixels = basisvec_header.pop('NAXIS' + str(basisvec_ax_nums['pixel'])) if basisvec_Npixels != self.Npixels: raise ValueError('Number of pixels in BASISVEC HDU does not match ' 'primary HDU') else: basisvec_ax_nums = reg_basisvec_ax_nums basisvec_coord_list = [basisvec_header['CTYPE' + str(basisvec_ax_nums['img_ax1'])], basisvec_header['CTYPE' + str(basisvec_ax_nums['img_ax2'])]] basisvec_axis1_array = uvutils.fits_gethduaxis(basisvec_hdu, basisvec_ax_nums['img_ax1']) basisvec_axis2_array = uvutils.fits_gethduaxis(basisvec_hdu, basisvec_ax_nums['img_ax2']) if not np.all(basisvec_axis1_array == self.axis1_array): raise ValueError('First image axis in BASISVEC HDU does not match ' 'primary HDU') if not np.all(basisvec_axis2_array == self.axis2_array): raise ValueError('Second image axis in BASISVEC HDU does not ' 'match primary HDU') if basisvec_coord_list != coord_list: raise ValueError('Pixel coordinate list in BASISVEC HDU does not ' 'match primary HDU') basisvec_Naxes_vec = basisvec_header['NAXIS' + str(basisvec_ax_nums['basisvec'])] basisvec_cs = basisvec_header['COORDSYS'] if basisvec_cs != self.pixel_coordinate_system: raise ValueError('Pixel coordinate system in BASISVEC HDU does ' 'not match primary HDU') if basisvec_Naxes_vec != self.Naxes_vec: raise ValueError('Number of vector coordinate axes in BASISVEC ' 'HDU does not match primary HDU') # check to see if BANDPARM HDU exists and read it out if it does if 'BANDPARM' in hdunames: bandpass_hdu = F[hdunames['BANDPARM']] bandpass_header = bandpass_hdu.header.copy() self.reference_input_impedance = bandpass_header.pop('refzin', None) self.reference_output_impedance = bandpass_header.pop('refzout', None) freq_data = bandpass_hdu.data columns = [c.name for c in freq_data.columns] self.bandpass_array = freq_data['bandpass'] self.bandpass_array = self.bandpass_array[np.newaxis, :] if 'rx_temp' in columns: self.receiver_temperature_array = freq_data['rx_temp'] self.receiver_temperature_array = self.receiver_temperature_array[np.newaxis, :] if 'loss' in columns: self.loss_array = freq_data['loss'] self.loss_array = self.loss_array[np.newaxis, :] if 'mismatch' in columns: self.mismatch_array = freq_data['mismatch'] self.mismatch_array = self.mismatch_array[np.newaxis, :] if 's11' in columns: s11 = freq_data['s11'] s12 = freq_data['s12'] s21 = freq_data['s21'] s22 = freq_data['s22'] self.s_parameters = np.zeros((4, 1, len(s11))) self.s_parameters[0, 0, :] = s11 self.s_parameters[1, 0, :] = s12 self.s_parameters[2, 0, :] = s21 self.s_parameters[3, 0, :] = s22 else: # no bandpass information, set it to an array of ones self.bandpass_array = np.zeros((self.Nspws, self.Nfreqs)) + 1. if run_check: self.check(check_extra=check_extra, run_check_acceptability=run_check_acceptability)
def read_calfits(self, filename, run_check=True, check_extra=True, run_check_acceptability=True, strict_fits=False): """ Read data from a calfits file. Args: filename: The calfits file to read to. run_check: Option to check for the existence and proper shapes of parameters after reading in the file. Default is True. check_extra: Option to check optional parameters as well as required ones. Default is True. run_check_acceptability: Option to check acceptable range of the values of parameters after reading in the file. Default is True. strict_fits: boolean If True, require that the data axes have cooresponding NAXIS, CRVAL, CDELT and CRPIX keywords. If False, allow CRPIX to be missing and set it equal to zero and allow the CRVAL for the spw directions to be missing and set it to zero. This keyword exists to support old calfits files that were missing many CRPIX and CRVAL keywords. Default is False. """ F = fits.open(filename) data = F[0].data hdr = F[0].header.copy() hdunames = uvutils.fits_indexhdus(F) anthdu = F[hdunames['ANTENNAS']] self.Nants_telescope = anthdu.header['NAXIS2'] antdata = anthdu.data self.antenna_names = map(str, antdata['ANTNAME']) self.antenna_numbers = map(int, antdata['ANTINDEX']) self.ant_array = np.array(map(int, antdata['ANTARR'])) if np.min(self.ant_array) < 0: # ant_array was shorter than the other columns, so it was padded with -1s. # Remove the padded entries. self.ant_array = self.ant_array[np.where(self.ant_array >= 0)[0]] self.channel_width = hdr.pop('CHWIDTH') self.integration_time = hdr.pop('INTTIME') self.telescope_name = hdr.pop('TELESCOP') self.history = str(hdr.get('HISTORY', '')) if not uvutils.check_history_version(self.history, self.pyuvdata_version_str): if self.history.endswith('\n'): self.history += self.pyuvdata_version_str else: self.history += '\n' + self.pyuvdata_version_str while 'HISTORY' in hdr.keys(): hdr.remove('HISTORY') self.time_range = map(float, hdr.pop('TMERANGE').split(',')) self.gain_convention = hdr.pop('GNCONVEN') self.x_orientation = hdr.pop('XORIENT') self.cal_type = hdr.pop('CALTYPE') if self.cal_type == 'delay': self.freq_range = map(float, hdr.pop('FRQRANGE').split(',')) else: if 'FRQRANGE' in hdr: self.freq_range = map(float, hdr.pop('FRQRANGE').split(',')) if 'OBSERVER' in hdr: self.observer = hdr.pop('OBSERVER') if 'ORIGCAL' in hdr: self.git_origin_cal = hdr.pop('ORIGCAL') if 'HASHCAL' in hdr: self.git_hash_cal = hdr.pop('HASHCAL') # generate polarization and time array for either cal_type. self.Njones = hdr.pop('NAXIS2') self.jones_array = uvutils.fits_gethduaxis(F[0], 2, strict_fits=strict_fits) self.Ntimes = hdr.pop('NAXIS3') self.time_array = uvutils.fits_gethduaxis(F[0], 3, strict_fits=strict_fits) # get data. if self.cal_type == 'gain': self.set_gain() self.gain_array = data[:, :, :, :, :, 0] + 1j * data[:, :, :, :, :, 1] self.flag_array = data[:, :, :, :, :, 2].astype('bool') if hdr.pop('NAXIS1') == 5: self.input_flag_array = data[:, :, :, :, :, 3].astype('bool') self.quality_array = data[:, :, :, :, :, 4] else: self.quality_array = data[:, :, :, :, :, 3] self.Nants_data = hdr.pop('NAXIS6') self.Nspws = hdr.pop('NAXIS5') # add this for backwards compatibility when the spw CRVAL wasn't recorded try: spw_array = uvutils.fits_gethduaxis( F[0], 5, strict_fits=strict_fits) - 1 if spw_array[0] == 0: # XXX: backwards compatibility: if array is already (erroneously) zero- # indexed, do nothing self.spw_array = spw_array else: # subtract 1 to be zero-indexed self.spw_array = uvutils.fits_gethduaxis( F[0], 5, strict_fits=strict_fits) - 1 except (KeyError): if not strict_fits: _warn_oldcalfits(filename) self.spw_array = np.array([0]) else: raise # generate frequency array from primary data unit. self.Nfreqs = hdr.pop('NAXIS4') self.freq_array = uvutils.fits_gethduaxis(F[0], 4, strict_fits=strict_fits) self.freq_array.shape = (self.Nspws, ) + self.freq_array.shape if self.cal_type == 'delay': self.set_delay() try: # delay-style should have the same number of axes as gains self.Nants_data = hdr.pop('NAXIS6') self.Nspws = hdr.pop('NAXIS5') ax_spw = 5 old_delay = False except (KeyError): _warn_olddelay(filename) self.Nants_data = hdr.pop('NAXIS5') self.Nspws = hdr.pop('NAXIS4') ax_spw = 4 old_delay = True if old_delay: self.delay_array = data[:, :, np.newaxis, :, :, 0] self.quality_array = data[:, :, np.newaxis, :, :, 1] else: self.delay_array = data[:, :, :, :, :, 0] self.quality_array = data[:, :, :, :, :, 1] sechdu = F[hdunames['FLAGS']] flag_data = sechdu.data flag_hdr = sechdu.header if sechdu.header['NAXIS1'] == 2: self.flag_array = flag_data[:, :, :, :, :, 0].astype('bool') self.input_flag_array = flag_data[:, :, :, :, :, 1].astype('bool') else: self.flag_array = flag_data[:, :, :, :, :, 0].astype('bool') # add this for backwards compatibility when the spw CRVAL wasn't recorded try: spw_array = uvutils.fits_gethduaxis(F[0], ax_spw, strict_fits=strict_fits) if spw_array[0] == 0: # XXX: backwards compatibility: if array is already (erroneously) zero- # indexed, do nothing self.spw_array = spw_array else: # subtract 1 to be zero-indexed self.spw_array = spw_array - 1 except (KeyError): if not strict_fits: _warn_oldcalfits(filename) self.spw_array = np.array([0]) else: raise # generate frequency array from flag data unit (no freq axis in primary). self.Nfreqs = sechdu.header['NAXIS4'] self.freq_array = uvutils.fits_gethduaxis(sechdu, 4, strict_fits=strict_fits) self.freq_array.shape = (self.Nspws, ) + self.freq_array.shape # add this for backwards compatibility when the spw CRVAL wasn't recorded try: spw_array = uvutils.fits_gethduaxis( sechdu, 5, strict_fits=strict_fits) - 1 except (KeyError): if not strict_fits: _warn_oldcalfits(filename) spw_array = np.array([0]) else: raise if not np.allclose(spw_array, self.spw_array): raise ValueError( 'Spectral window values are different in FLAGS HDU than in primary HDU' ) time_array = uvutils.fits_gethduaxis(sechdu, 3, strict_fits=strict_fits) if not np.allclose(time_array, self.time_array, rtol=self._time_array.tols[0], atol=self._time_array.tols[0]): raise ValueError( 'Time values are different in FLAGS HDU than in primary HDU' ) jones_array = uvutils.fits_gethduaxis(sechdu, 2, strict_fits=strict_fits) if not np.allclose(jones_array, self.jones_array, rtol=self._jones_array.tols[0], atol=self._jones_array.tols[0]): raise ValueError( 'Jones values are different in FLAGS HDU than in primary HDU' ) # remove standard FITS header items that are still around std_fits_substrings = [ 'SIMPLE', 'BITPIX', 'EXTEND', 'BLOCKED', 'GROUPS', 'PCOUNT', 'BSCALE', 'BZERO', 'NAXIS', 'PTYPE', 'PSCAL', 'PZERO', 'CTYPE', 'CRVAL', 'CRPIX', 'CDELT', 'CROTA', 'CUNIT' ] for key in hdr.keys(): for sub in std_fits_substrings: if key.find(sub) > -1: hdr.remove(key) # find all the remaining header items and keep them as extra_keywords for key in hdr: if key == 'COMMENT': self.extra_keywords[key] = str(hdr.get(key)) elif key != '': self.extra_keywords[key] = hdr.get(key) # get total quality array if present if 'TOTQLTY' in hdunames: totqualhdu = F[hdunames['TOTQLTY']] self.total_quality_array = totqualhdu.data # add this for backwards compatibility when the spw CRVAL wasn't recorded try: spw_array = uvutils.fits_gethduaxis( totqualhdu, 4, strict_fits=strict_fits) - 1 except (KeyError): if not strict_fits: _warn_oldcalfits(filename) spw_array = np.array([0]) else: raise if not np.allclose(spw_array, self.spw_array): raise ValueError( 'Spectral window values are different in TOTQLTY HDU than in primary HDU' ) if self.cal_type != 'delay': # delay-type files won't have a freq_array freq_array = uvutils.fits_gethduaxis(totqualhdu, 3, strict_fits=strict_fits) freq_array.shape = (self.Nspws, ) + freq_array.shape if not np.allclose(freq_array, self.freq_array, rtol=self._freq_array.tols[0], atol=self._freq_array.tols[0]): raise ValueError( 'Frequency values are different in TOTQLTY HDU than in primary HDU' ) time_array = uvutils.fits_gethduaxis(totqualhdu, 2, strict_fits=strict_fits) if not np.allclose(time_array, self.time_array, rtol=self._time_array.tols[0], atol=self._time_array.tols[0]): raise ValueError( 'Time values are different in TOTQLTY HDU than in primary HDU' ) jones_array = uvutils.fits_gethduaxis(totqualhdu, 1, strict_fits=strict_fits) if not np.allclose(jones_array, self.jones_array, rtol=self._jones_array.tols[0], atol=self._jones_array.tols[0]): raise ValueError( 'Jones values are different in TOTQLTY HDU than in primary HDU' ) else: self.total_quality_array = None if run_check: self.check(check_extra=check_extra, run_check_acceptability=run_check_acceptability)
def read_uvfits(self, filename, antenna_nums=None, antenna_names=None, ant_str=None, ant_pairs_nums=None, frequencies=None, freq_chans=None, times=None, polarizations=None, blt_inds=None, read_data=True, read_metadata=True, run_check=True, check_extra=True, run_check_acceptability=True): """ Read in header, metadata and data from a uvfits file. Supports reading only selected portions of the data. Args: filename: The uvfits file to read from. antenna_nums: The antennas numbers to include when reading data into the object (antenna positions and names for the excluded antennas will be retained). This cannot be provided if antenna_names is also provided. Ignored if read_data is False. antenna_names: The antennas names to include when reading data into the object (antenna positions and names for the excluded antennas will be retained). This cannot be provided if antenna_nums is also provided. Ignored if read_data is False. ant_pairs_nums: A list of antenna number tuples (e.g. [(0,1), (3,2)]) specifying baselines to include when reading data into the object. Ordering of the numbers within the tuple does not matter. Ignored if read_data is False. ant_str: A string containing information about what antenna numbers and polarizations to include when reading data into the object. Can be 'auto', 'cross', 'all', or combinations of antenna numbers and polarizations (e.g. '1', '1_2', '1x_2y'). See tutorial for more examples of valid strings and the behavior of different forms for ant_str. If '1x_2y,2y_3y' is passed, both polarizations 'xy' and 'yy' will be kept for both baselines (1,2) and (2,3) to return a valid pyuvdata object. An ant_str cannot be passed in addition to any of the above antenna args or the polarizations arg. Ignored if read_data is False. frequencies: The frequencies to include when reading data into the object. Ignored if read_data is False. freq_chans: The frequency channel numbers to include when reading data into the object. Ignored if read_data is False. times: The times to include when reading data into the object. Ignored if read_data is False. polarizations: The polarizations to include when reading data into the object. Ignored if read_data is False. blt_inds: The baseline-time indices to include when reading data into the object. This is not commonly used. Ignored if read_data is False. read_data: Read in the visibility and flag data. If set to false, only the basic header info and metadata (if read_metadata is True) will be read in. Results in an incompletely defined object (check will not pass). Default True. read_metadata: Read in metadata (times, baselines, uvws) as well as basic header info. Only used if read_data is False (metadata will be read if data is read). If both read_data and read_metadata are false, only basic header info is read in. Default True. run_check: Option to check for the existence and proper shapes of parameters after reading in the file. Default is True. Ignored if read_data is False. check_extra: Option to check optional parameters as well as required ones. Default is True. Ignored if read_data is False. run_check_acceptability: Option to check acceptable range of the values of parameters after reading in the file. Default is True. Ignored if read_data is False. """ if not read_data: run_check = False hdu_list = fits.open(filename, memmap=True) vis_hdu = hdu_list[ 0] # assumes the visibilities are in the primary hdu vis_hdr = vis_hdu.header.copy() hdunames = uvutils.fits_indexhdus( hdu_list) # find the rest of the tables # First get everything we can out of the header. self.set_phased() # check if we have an spw dimension if vis_hdr['NAXIS'] == 7: if vis_hdr['NAXIS5'] > 1: raise ValueError('Sorry. Files with more than one spectral' 'window (spw) are not yet supported. A ' 'great project for the interested student!') self.Nspws = vis_hdr.pop('NAXIS5') self.spw_array = np.int32(uvutils.fits_gethduaxis(vis_hdu, 5)) - 1 # the axis number for phase center depends on if the spw exists self.phase_center_ra_degrees = np.float(vis_hdr.pop('CRVAL6')) self.phase_center_dec_degrees = np.float(vis_hdr.pop('CRVAL7')) else: self.Nspws = 1 self.spw_array = np.array([0]) # the axis number for phase center depends on if the spw exists self.phase_center_ra_degrees = np.float(vis_hdr.pop('CRVAL5')) self.phase_center_dec_degrees = np.float(vis_hdr.pop('CRVAL6')) # get shapes self.Nfreqs = vis_hdr.pop('NAXIS4') self.Npols = vis_hdr.pop('NAXIS3') self.Nblts = vis_hdr.pop('GCOUNT') self.freq_array = uvutils.fits_gethduaxis(vis_hdu, 4) self.freq_array.shape = (self.Nspws, ) + self.freq_array.shape self.channel_width = vis_hdr.pop('CDELT4') self.polarization_array = np.int32(uvutils.fits_gethduaxis(vis_hdu, 3)) # other info -- not required but frequently used self.object_name = vis_hdr.pop('OBJECT', None) self.telescope_name = vis_hdr.pop('TELESCOP', None) self.instrument = vis_hdr.pop('INSTRUME', None) latitude_degrees = vis_hdr.pop('LAT', None) longitude_degrees = vis_hdr.pop('LON', None) altitude = vis_hdr.pop('ALT', None) self.x_orientation = vis_hdr.pop('XORIENT', None) self.history = str(vis_hdr.get('HISTORY', '')) if not uvutils.check_history_version(self.history, self.pyuvdata_version_str): self.history += self.pyuvdata_version_str while 'HISTORY' in vis_hdr.keys(): vis_hdr.remove('HISTORY') self.vis_units = vis_hdr.pop('BUNIT', 'UNCALIB') self.phase_center_epoch = vis_hdr.pop('EPOCH', None) # remove standard FITS header items that are still around std_fits_substrings = [ 'SIMPLE', 'BITPIX', 'EXTEND', 'BLOCKED', 'GROUPS', 'PCOUNT', 'BSCALE', 'BZERO', 'NAXIS', 'PTYPE', 'PSCAL', 'PZERO', 'CTYPE', 'CRVAL', 'CRPIX', 'CDELT', 'CROTA', 'CUNIT', 'DATE-OBS' ] for key in vis_hdr.keys(): for sub in std_fits_substrings: if key.find(sub) > -1: vis_hdr.remove(key) # find all the remaining header items and keep them as extra_keywords for key in vis_hdr: if key == 'COMMENT': self.extra_keywords[key] = str(vis_hdr.get(key)) elif key != '': self.extra_keywords[key] = vis_hdr.get(key) # Next read the antenna table ant_hdu = hdu_list[hdunames['AIPS AN']] # stuff in the header if self.telescope_name is None: self.telescope_name = ant_hdu.header['ARRNAM'] self.gst0 = ant_hdu.header['GSTIA0'] self.rdate = ant_hdu.header['RDATE'] self.earth_omega = ant_hdu.header['DEGPDY'] self.dut1 = ant_hdu.header['UT1UTC'] if 'TIMESYS' in ant_hdu.header.keys(): self.timesys = ant_hdu.header['TIMESYS'] else: # CASA misspells this one self.timesys = ant_hdu.header['TIMSYS'] if 'FRAME' in ant_hdu.header.keys(): xyz_telescope_frame = ant_hdu.header['FRAME'] else: warnings.warn('Required Antenna frame keyword not set, ' 'setting to ????') xyz_telescope_frame = '????' # get telescope location and antenna positions. # VLA incorrectly sets ARRAYX/ARRAYY/ARRAYZ to 0, and puts array center # in the antenna positions themselves if (np.isclose(ant_hdu.header['ARRAYX'], 0) and np.isclose(ant_hdu.header['ARRAYY'], 0) and np.isclose(ant_hdu.header['ARRAYZ'], 0)): x_telescope = np.mean(ant_hdu.data['STABXYZ'][:, 0]) y_telescope = np.mean(ant_hdu.data['STABXYZ'][:, 1]) z_telescope = np.mean(ant_hdu.data['STABXYZ'][:, 2]) self.antenna_positions = ( ant_hdu.data.field('STABXYZ') - np.array([x_telescope, y_telescope, z_telescope])) else: x_telescope = ant_hdu.header['ARRAYX'] y_telescope = ant_hdu.header['ARRAYY'] z_telescope = ant_hdu.header['ARRAYZ'] # AIPS memo #117 says that antenna_positions should be relative to # the array center, but in a rotated ECEF frame so that the x-axis # goes through the local meridian. rot_ecef_positions = ant_hdu.data.field('STABXYZ') latitude, longitude, altitude = \ uvutils.LatLonAlt_from_XYZ(np.array([x_telescope, y_telescope, z_telescope])) self.antenna_positions = uvutils.ECEF_from_rotECEF( rot_ecef_positions, longitude) if xyz_telescope_frame == 'ITRF': self.telescope_location = np.array( [x_telescope, y_telescope, z_telescope]) else: if latitude_degrees is not None and longitude_degrees is not None and altitude is not None: self.telescope_location_lat_lon_alt_degrees = ( latitude_degrees, longitude_degrees, altitude) # stuff in columns ant_names = ant_hdu.data.field('ANNAME').tolist() self.antenna_names = [] for name in ant_names: self.antenna_names.append(name.replace('\x00!', '')) # subtract one to get to 0-indexed values rather than 1-indexed values self.antenna_numbers = ant_hdu.data.field('NOSTA') - 1 self.Nants_telescope = len(self.antenna_numbers) if 'DIAMETER' in ant_hdu.columns.names: self.antenna_diameters = ant_hdu.data.field('DIAMETER') try: self.set_telescope_params() except ValueError, ve: warnings.warn(str(ve))
def read_uvfits(self, filename, run_check=True, check_extra=True, run_check_acceptability=True): """ Read in data from a uvfits file. Args: filename: The uvfits file to read from. run_check: Option to check for the existence and proper shapes of parameters after reading in the file. Default is True. check_extra: Option to check optional parameters as well as required ones. Default is True. run_check_acceptability: Option to check acceptable range of the values of parameters after reading in the file. Default is True. """ F = fits.open(filename) D = F[0] # assumes the visibilities are in the primary hdu hdr = D.header.copy() hdunames = uvutils.fits_indexhdus(F) # find the rest of the tables # astropy.io fits reader scales date according to relevant PZER0 (?) time0_array = D.data['DATE'] try: # uvfits standard is to have 2 DATE parameters, both floats: # DATE (full day) and _DATE (fractional day) time1_array = D.data['_DATE'] self.time_array = (time0_array.astype(np.double) + time1_array.astype(np.double)) except (KeyError): # cotter uvfits files have one DATE that is a double self.time_array = time0_array if np.finfo(time0_array[0]).precision < 5: raise ValueError('JDs in this file are not precise to ' 'better than a second.') if (np.finfo(time0_array[0]).precision > 5 and np.finfo(time0_array[0]).precision < 8): warnings.warn('The JDs in this file have sub-second ' 'precision, but not sub-millisecond. ' 'Use with caution.') self.Ntimes = len(np.unique(self.time_array)) # if antenna arrays are present, use them. otherwise use baseline array try: # Note: uvfits antennas are 1 indexed, # need to subtract one to get to 0-indexed self.ant_1_array = np.int32(D.data.field('ANTENNA1')) - 1 self.ant_2_array = np.int32(D.data.field('ANTENNA2')) - 1 subarray = np.int32(D.data.field('SUBARRAY')) - 1 # error on files with multiple subarrays if len(set(subarray)) > 1: raise ValueError('This file appears to have multiple subarray ' 'values; only files with one subarray are ' 'supported.') except (KeyError): # cannot set this to be the baseline array because it uses the # 256 convention, not our 2048 convention bl_input_array = np.int64(D.data.field('BASELINE')) # get antenna arrays based on uvfits baseline array self.ant_1_array, self.ant_2_array = \ self.baseline_to_antnums(bl_input_array) # check for multi source files try: source = D.data.field('SOURCE') if len(set(source)) > 1: raise ValueError('This file has multiple sources. Only single ' 'source observations are supported.') except (KeyError): pass # get self.baseline_array using our convention self.baseline_array = \ self.antnums_to_baseline(self.ant_1_array, self.ant_2_array) self.Nbls = len(np.unique(self.baseline_array)) # initialize internal variables based on the antenna lists self.Nants_data = int( len( np.unique(self.ant_1_array.tolist() + self.ant_2_array.tolist()))) self.set_phased() # check if we have an spw dimension if hdr.pop('NAXIS') == 7: if hdr['NAXIS5'] > 1: raise ValueError('Sorry. Files with more than one spectral' + 'window (spw) are not yet supported. A ' + 'great project for the interested student!') self.data_array = (D.data.field('DATA')[:, 0, 0, :, :, :, 0] + 1j * D.data.field('DATA')[:, 0, 0, :, :, :, 1]) self.flag_array = (D.data.field('DATA')[:, 0, 0, :, :, :, 2] <= 0) self.nsample_array = np.abs( D.data.field('DATA')[:, 0, 0, :, :, :, 2]) self.Nspws = hdr.pop('NAXIS5') assert (self.Nspws == self.data_array.shape[1]) # the axis number for phase center depends on if the spw exists # subtract 1 to be zero-indexed self.spw_array = np.int32(uvutils.fits_gethduaxis(D, 5)) - 1 self.phase_center_ra_degrees = np.float(hdr.pop('CRVAL6')) self.phase_center_dec_degrees = np.float(hdr.pop('CRVAL7')) else: # in many uvfits files the spw axis is left out, # here we put it back in so the dimensionality stays the same self.data_array = (D.data.field('DATA')[:, 0, 0, :, :, 0] + 1j * D.data.field('DATA')[:, 0, 0, :, :, 1]) self.data_array = self.data_array[:, np.newaxis, :, :] self.flag_array = (D.data.field('DATA')[:, 0, 0, :, :, 2] <= 0) self.flag_array = self.flag_array[:, np.newaxis, :, :] self.nsample_array = np.abs(D.data.field('DATA')[:, 0, 0, :, :, 2]) self.nsample_array = (self.nsample_array[:, np.newaxis, :, :]) # the axis number for phase center depends on if the spw exists self.Nspws = 1 self.spw_array = np.array([0]) self.phase_center_ra_degrees = np.float(hdr.pop('CRVAL5')) self.phase_center_dec_degrees = np.float(hdr.pop('CRVAL6')) # get shapes self.Nfreqs = hdr.pop('NAXIS4') self.Npols = hdr.pop('NAXIS3') self.Nblts = hdr.pop('GCOUNT') # read baseline vectors in units of seconds, return in meters self.uvw_array = (np.array( np.stack( (D.data.field('UU'), D.data.field('VV'), D.data.field('WW')))) * const.c.to('m/s').value).T self.freq_array = uvutils.fits_gethduaxis(D, 4) self.channel_width = hdr.pop('CDELT4') try: self.integration_time = float(D.data.field('INTTIM')[0]) except (KeyError): if self.Ntimes > 1: self.integration_time = \ float(np.diff(np.sort(list(set(self.time_array)))) [0]) * 86400 else: raise ValueError('integration time not specified and only ' 'one time present') self.freq_array.shape = (self.Nspws, ) + self.freq_array.shape self.polarization_array = np.int32(uvutils.fits_gethduaxis(D, 3)) # other info -- not required but frequently used self.object_name = hdr.pop('OBJECT', None) self.telescope_name = hdr.pop('TELESCOP', None) self.instrument = hdr.pop('INSTRUME', None) latitude_degrees = hdr.pop('LAT', None) longitude_degrees = hdr.pop('LON', None) altitude = hdr.pop('ALT', None) self.x_orientation = hdr.pop('XORIENT', None) self.history = str(hdr.get('HISTORY', '')) if not uvutils.check_history_version(self.history, self.pyuvdata_version_str): self.history += self.pyuvdata_version_str while 'HISTORY' in hdr.keys(): hdr.remove('HISTORY') # if 'CASAHIST' in hdr.keys(): # self.casa_history=hdr.pop('CASAHIST',None) self.vis_units = hdr.pop('BUNIT', 'UNCALIB') self.phase_center_epoch = hdr.pop('EPOCH', None) # remove standard FITS header items that are still around std_fits_substrings = [ 'SIMPLE', 'BITPIX', 'EXTEND', 'BLOCKED', 'GROUPS', 'PCOUNT', 'BSCALE', 'BZERO', 'NAXIS', 'PTYPE', 'PSCAL', 'PZERO', 'CTYPE', 'CRVAL', 'CRPIX', 'CDELT', 'CROTA', 'CUNIT', 'DATE-OBS' ] for key in hdr.keys(): for sub in std_fits_substrings: if key.find(sub) > -1: hdr.remove(key) # find all the remaining header items and keep them as extra_keywords for key in hdr: if key == 'COMMENT': self.extra_keywords[key] = str(hdr.get(key)) elif key != '': self.extra_keywords[key] = hdr.get(key) # READ the antenna table ant_hdu = F[hdunames['AIPS AN']] # stuff in the header if self.telescope_name is None: self.telescope_name = ant_hdu.header['ARRNAM'] self.gst0 = ant_hdu.header['GSTIA0'] self.rdate = ant_hdu.header['RDATE'] self.earth_omega = ant_hdu.header['DEGPDY'] self.dut1 = ant_hdu.header['UT1UTC'] try: self.timesys = ant_hdu.header['TIMESYS'] except (KeyError): # CASA misspells this one self.timesys = ant_hdu.header['TIMSYS'] try: xyz_telescope_frame = ant_hdu.header['FRAME'] except (KeyError): warnings.warn('Required Antenna frame keyword not set, ' 'setting to ????') xyz_telescope_frame = '????' # get telescope location and antenna positions. # VLA incorrectly sets ARRAYX/ARRAYY/ARRAYZ to 0, and puts array center # in the antenna positions themselves if (np.isclose(ant_hdu.header['ARRAYX'], 0) and np.isclose(ant_hdu.header['ARRAYY'], 0) and np.isclose(ant_hdu.header['ARRAYZ'], 0)): x_telescope = np.mean(ant_hdu.data['STABXYZ'][:, 0]) y_telescope = np.mean(ant_hdu.data['STABXYZ'][:, 1]) z_telescope = np.mean(ant_hdu.data['STABXYZ'][:, 2]) self.antenna_positions = ( ant_hdu.data.field('STABXYZ') - np.array([x_telescope, y_telescope, z_telescope])) else: x_telescope = ant_hdu.header['ARRAYX'] y_telescope = ant_hdu.header['ARRAYY'] z_telescope = ant_hdu.header['ARRAYZ'] # AIPS memo #117 says that antenna_positions should be relative to # the array center, but in a rotated ECEF frame so that the x-axis # goes through the local meridian. rot_ecef_positions = ant_hdu.data.field('STABXYZ') latitude, longitude, altitude = \ uvutils.LatLonAlt_from_XYZ(np.array([x_telescope, y_telescope, z_telescope])) self.antenna_positions = uvutils.ECEF_from_rotECEF( rot_ecef_positions, longitude) if xyz_telescope_frame == 'ITRF': self.telescope_location = np.array( [x_telescope, y_telescope, z_telescope]) else: if latitude_degrees is not None and longitude_degrees is not None and altitude is not None: self.telescope_location_lat_lon_alt_degrees = ( latitude_degrees, longitude_degrees, altitude) # stuff in columns ant_names = ant_hdu.data.field('ANNAME').tolist() self.antenna_names = [] for name in ant_names: self.antenna_names.append(name.replace('\x00!', '')) # subtract one to get to 0-indexed values rather than 1-indexed values self.antenna_numbers = ant_hdu.data.field('NOSTA') - 1 self.Nants_telescope = len(self.antenna_numbers) try: self.antenna_diameters = ant_hdu.data.field('DIAMETER') except (KeyError): pass del (D) try: self.set_telescope_params() except ValueError, ve: warnings.warn(str(ve))