def test_identical_headers(self): ha = Header([('A', 1), ('B', 2), ('C', 3)]) hb = ha.copy() assert HeaderDiff(ha, hb).identical assert HeaderDiff(ha.tostring(), hb.tostring()).identical with pytest.raises(TypeError): HeaderDiff(1, 2)
def test_different_keyword_values_with_duplicate(self): ha = Header([('A', 1), ('B', 2), ('C', 3)]) hb = ha.copy() ha.append(('C', 4)) hb.append(('C', 5)) diff = HeaderDiff(ha, hb) assert not diff.identical assert diff.diff_keyword_values == {'C': [None, (4, 5)]}
def _density_radiation_field_header(self): '''Common header items in the density and radiation field FITS files''' self._density.header.pop('RATIO') self._radiation_field.header.pop('RATIO') # note: must use to_string() here or astropy.io.fits.Card complains # about the value being a Unit. Oddly it doesn't complain for the # data units. Go figure. utils.setkey("BUNIT",self.density_unit.to_string(),self._density) utils.comment("Best-fit H2 volume density",self._density) utils.setkey("BUNIT",self.radiation_field_unit.to_string(),self._radiation_field) utils.comment("Best-fit interstellar radiation field",self._radiation_field) self._makehistory(self._density) self._makehistory(self._radiation_field) # convert from OrderedDict to astropy.io.fits.header.Header self._density.header = Header(self._density.header) self._radiation_field.header = Header(self._radiation_field.header) self._density._identifier = "H2 Volume Density" self._radiation_field._identifier = "Radiation Field"
def test_different_keyword_comments(self): ha = Header([('A', 1), ('B', 2), ('C', 3, 'comment 1')]) hb = ha.copy() hb.comments['C'] = 'comment 2' diff = HeaderDiff(ha, hb) assert not diff.identical assert (diff.diff_keyword_comments == { 'C': [('comment 1', 'comment 2')] })
def test_ignore_blank_cards(self, differ): """Test for https://aeon.stsci.edu/ssb/trac/pyfits/ticket/152 Ignore blank cards. """ ha = Header([('A', 1), ('B', 2), ('C', 3)]) hb = Header([('A', 1), ('', ''), ('B', 2), ('', ''), ('C', 3)]) hc = ha.copy() if differ is HeaderDiff: hc.append() hc.append() else: # Ensure blanks are not at the end as they are stripped by HDUs hc.add_blank(after=-2) hc.add_blank(after=-2) if differ in (HDUDiff, FITSDiff): # wrap it in a PrimaryHDU ha, hb, hc = (PrimaryHDU(np.arange(10), h) for h in (ha, hb, hc)) hc_header = hc.header if differ is FITSDiff: # wrap it in a HDUList ha, hb, hc = (HDUList([h]) for h in (ha, hb, hc)) hc_header = hc[0].header # We now have a header with interleaved blanks, and a header with end # blanks, both of which should ignore the blanks assert differ(ha, hb).identical assert differ(ha, hc).identical assert differ(hb, hc).identical assert not differ(ha, hb, ignore_blank_cards=False).identical assert not differ(ha, hc, ignore_blank_cards=False).identical # Both hb and hc have the same number of blank cards; since order is # currently ignored, these should still be identical even if blank # cards are not ignored assert differ(hb, hc, ignore_blank_cards=False).identical if differ is HeaderDiff: hc.append() else: # Ensure blanks are not at the end as they are stripped by HDUs hc_header.add_blank(after=-2) # But now there are different numbers of blanks, so they should not be # ignored: assert not differ(hb, hc, ignore_blank_cards=False).identical
def test_different_keywords(self): ha = Header([('A', 1), ('B', 2), ('C', 3)]) hb = ha.copy() hb['C'] = 4 hb['D'] = (5, 'Comment') ha['E'] = (6, 'Comment') ha['F'] = (7, 'Comment') diff = HeaderDiff(ha, hb) assert not diff.identical assert diff.diff_keywords == (['E', 'F'], ['D'])
def test_different_keyword_count(self): ha = Header([('A', 1), ('B', 2), ('C', 3)]) hb = ha.copy() del hb['B'] diff = HeaderDiff(ha, hb) assert not diff.identical assert diff.diff_keyword_count == (3, 2) # But make sure the common keywords are at least correct assert diff.common_keywords == ['A', 'C']
def test_file_output_from_path_string(self): outpath = self.temp('diff_output.txt') ha = Header([('A', 1), ('B', 2), ('C', 3)]) hb = ha.copy() hb['C'] = 4 diffobj = HeaderDiff(ha, hb) diffobj.report(fileobj=outpath) report_as_string = diffobj.report() with open(outpath) as fout: assert fout.read() == report_as_string
def test_file_output_overwrite_safety(self): outpath = self.temp('diff_output.txt') ha = Header([('A', 1), ('B', 2), ('C', 3)]) hb = ha.copy() hb['C'] = 4 diffobj = HeaderDiff(ha, hb) diffobj.report(fileobj=outpath) with pytest.raises(OSError): diffobj.report(fileobj=outpath)
def test_file_output_overwrite_success(self): outpath = self.temp('diff_output.txt') ha = Header([('A', 1), ('B', 2), ('C', 3)]) hb = ha.copy() hb['C'] = 4 diffobj = HeaderDiff(ha, hb) diffobj.report(fileobj=outpath) report_as_string = diffobj.report() diffobj.report(fileobj=outpath, overwrite=True) with open(outpath) as fout: assert fout.read() == report_as_string, ( "overwritten output file is not identical to report string")
def fromhdulist(cls, hdulist, compress=False): """ Creates a new FitsHDU from a given HDUList object. Parameters ---------- hdulist : HDUList A valid Headerlet object. compress : bool, optional Gzip compress the FITS file """ fileobj = bs = io.BytesIO() if compress: if hasattr(hdulist, '_file'): name = fileobj_name(hdulist._file) else: name = None fileobj = gzip.GzipFile(name, mode='wb', fileobj=bs) hdulist.writeto(fileobj) if compress: fileobj.close() # A proper HDUList should still be padded out to a multiple of 2880 # technically speaking padding = (_pad_length(bs.tell()) * cls._padding_byte).encode('ascii') bs.write(padding) bs.seek(0) cards = [ ('XTENSION', cls._extension, 'FITS extension'), ('BITPIX', 8, 'array data type'), ('NAXIS', 1, 'number of array dimensions'), ('NAXIS1', len(bs.getvalue()), 'Axis length'), ('PCOUNT', 0, 'number of parameters'), ('GCOUNT', 1, 'number of groups'), ] # Add the XINDn keywords proposed by Perry, though nothing is done with # these at the moment if len(hdulist) > 1: for idx, hdu in enumerate(hdulist[1:]): cards.append(('XIND' + str(idx + 1), hdu._header_offset, f'byte offset of extension {idx + 1}')) cards.append(('COMPRESS', compress, 'Uses gzip compression')) header = Header(cards) return cls._readfrom_internal(_File(bs), header=header)
def test_floating_point_rtol(self): ha = Header([('A', 1), ('B', 2.00001), ('C', 3.000001)]) hb = ha.copy() hb['B'] = 2.00002 hb['C'] = 3.000002 diff = HeaderDiff(ha, hb) assert not diff.identical assert (diff.diff_keyword_values == {'B': [(2.00001, 2.00002)], 'C': [(3.000001, 3.000002)]}) diff = HeaderDiff(ha, hb, rtol=1e-6) assert not diff.identical assert diff.diff_keyword_values == {'B': [(2.00001, 2.00002)]} diff = HeaderDiff(ha, hb, rtol=1e-5) assert diff.identical
def test_file_output_overwrite_vs_clobber(self): """Verify uses of clobber and overwrite.""" outpath = self.temp('diff_output.txt') ha = Header([('A', 1), ('B', 2), ('C', 3)]) hb = ha.copy() hb['C'] = 4 diffobj = HeaderDiff(ha, hb) diffobj.report(fileobj=outpath) with catch_warnings(AstropyDeprecationWarning) as warning_lines: diffobj.report(fileobj=outpath, clobber=True) assert warning_lines[0].category == AstropyDeprecationWarning assert (str(warning_lines[0].message) == '"clobber" was ' 'deprecated in version 2.0 and will be removed in a ' 'future version. Use argument "overwrite" instead.')
def test_file_output_overwrite_vs_clobber(self): """Verify uses of clobber and overwrite.""" outpath = self.temp('diff_output.txt') ha = Header([('A', 1), ('B', 2), ('C', 3)]) hb = ha.copy() hb['C'] = 4 diffobj = HeaderDiff(ha, hb) diffobj.report(fileobj=outpath) with pytest.warns( AstropyDeprecationWarning, match=r'"clobber" was ' r'deprecated in version 2\.0 and will be removed in a ' r'future version\. Use argument "overwrite" instead\.'): diffobj.report(fileobj=outpath, clobber=True)
def test_ignore_blanks(self): with fits.conf.set_temp('strip_header_whitespace', False): ha = Header([('A', 1), ('B', 2), ('C', 'A ')]) hb = ha.copy() hb['C'] = 'A' assert ha['C'] != hb['C'] diff = HeaderDiff(ha, hb) # Trailing blanks are ignored by default assert diff.identical assert diff.diff_keyword_values == {} # Don't ignore blanks diff = HeaderDiff(ha, hb, ignore_blanks=False) assert not diff.identical assert diff.diff_keyword_values == {'C': [('A ', 'A')]}
def test_asymmetric_duplicate_keywords(self): ha = Header([('A', 1), ('B', 2), ('C', 3)]) hb = ha.copy() ha.append(('A', 2, 'comment 1')) ha.append(('A', 3, 'comment 2')) hb.append(('B', 4, 'comment 3')) hb.append(('C', 5, 'comment 4')) diff = HeaderDiff(ha, hb) assert not diff.identical assert diff.diff_keyword_values == {} assert (diff.diff_duplicate_keywords == {'A': (3, 1), 'B': (1, 2), 'C': (1, 2)}) report = diff.report() assert ("Inconsistent duplicates of keyword 'A' :\n" " Occurs 3 time(s) in a, 1 times in (b)") in report
def test_ignore_keyword_values(self): ha = Header([('A', 1), ('B', 2), ('C', 3)]) hb = ha.copy() hb['B'] = 4 hb['C'] = 5 diff = HeaderDiff(ha, hb, ignore_keywords=['*']) assert diff.identical diff = HeaderDiff(ha, hb, ignore_keywords=['B']) assert not diff.identical assert diff.diff_keyword_values == {'C': [(3, 5)]} report = diff.report() assert 'Keyword B has different values' not in report assert 'Keyword C has different values' in report # Test case-insensitivity diff = HeaderDiff(ha, hb, ignore_keywords=['b']) assert not diff.identical assert diff.diff_keyword_values == {'C': [(3, 5)]}
def test_ignore_keyword_comments(self): ha = Header([('A', 1, 'A'), ('B', 2, 'B'), ('C', 3, 'C')]) hb = ha.copy() hb.comments['B'] = 'D' hb.comments['C'] = 'E' diff = HeaderDiff(ha, hb, ignore_comments=['*']) assert diff.identical diff = HeaderDiff(ha, hb, ignore_comments=['B']) assert not diff.identical assert diff.diff_keyword_comments == {'C': [('C', 'E')]} report = diff.report() assert 'Keyword B has different comments' not in report assert 'Keyword C has different comments' in report # Test case-insensitivity diff = HeaderDiff(ha, hb, ignore_comments=['b']) assert not diff.identical assert diff.diff_keyword_comments == {'C': [('C', 'E')]}
def _compute_chisq(self): '''Compute the chi-squared values from observed ratios and models''' if self.ratiocount < 2 : raise Exception("Not enough ratios to compute chisq. Need 2, got %d"%self.ratiocount) sumary = sum((self._residual[r]._data**2 for r in self._residual)) self._dof = len(self._residual) - 1 k = utils.firstkey(self._residual) _wcs = deepcopy(self._residual[k].wcs) _meta = deepcopy(self._residual[k].meta) self._chisq = CCDData(sumary,unit='adu',wcs=_wcs,meta=_meta) self._reduced_chisq = self._chisq.divide(self._dof) # must make a copy here otherwise the header is an OrderDict # instead of astropy.io.fits.header.Header self._reduced_chisq.header = Header(deepcopy(self._chisq.header)) self._fixheader(self._chisq) self._fixheader(self._reduced_chisq) utils.comment("Chi-squared",self._chisq) utils.comment(("Reduced Chi-squared (DOF=%d)"%self._dof),self._reduced_chisq) self._makehistory(self._chisq) self._makehistory(self._reduced_chisq)
def test_ignore_hdus(self): a = np.arange(100).reshape(10, 10) b = a.copy() ha = Header([('A', 1), ('B', 2), ('C', 3)]) xa = np.array([(1.0, 1), (3.0, 4)], dtype=[('x', float), ('y', int)]) xb = np.array([(1.0, 2), (3.0, 5)], dtype=[('x', float), ('y', int)]) phdu = PrimaryHDU(header=ha) ihdua = ImageHDU(data=a, name='SCI') ihdub = ImageHDU(data=b, name='SCI') bhdu1 = BinTableHDU(data=xa, name='ASDF') bhdu2 = BinTableHDU(data=xb, name='ASDF') hdula = HDUList([phdu, ihdua, bhdu1]) hdulb = HDUList([phdu, ihdub, bhdu2]) # ASDF extension should be different diff = FITSDiff(hdula, hdulb) assert not diff.identical assert diff.diff_hdus[0][0] == 2 # ASDF extension should be ignored diff = FITSDiff(hdula, hdulb, ignore_hdus=['ASDF']) assert diff.identical, diff.report() diff = FITSDiff(hdula, hdulb, ignore_hdus=['ASD*']) assert diff.identical, diff.report() # SCI extension should be different hdulb['SCI'].data += 1 diff = FITSDiff(hdula, hdulb, ignore_hdus=['ASDF']) assert not diff.identical # SCI and ASDF extensions should be ignored diff = FITSDiff(hdula, hdulb, ignore_hdus=['SCI', 'ASDF']) assert diff.identical, diff.report() # All EXTVER of SCI should be ignored ihduc = ImageHDU(data=a, name='SCI', ver=2) hdulb.append(ihduc) diff = FITSDiff(hdula, hdulb, ignore_hdus=['SCI', 'ASDF']) assert not any(diff.diff_hdus), diff.report() assert any(diff.diff_hdu_count), diff.report()
def test_WFC3WFC3_magnitude_zpt_reader(): h = Header() h['TELESCOP'] = 'HST' h['INSTRUME'] = 'WFC3 ' h['PHOTPLAM'] = 3 h['PHOTFLAM'] = 2 exp_result = -2.5 * np.log10(2) - 21.10 - 5 * np.log10(3) + 18.692 assert np.isclose(utils.WFC3_magnitude_zpt_reader(h), exp_result) h['TELESCOP'] = 'WST' with pytest.raises(AssertionError): utils.WFC3_magnitude_zpt_reader(h) h['TELESCOP'] = 'HST' h['INSTRUME'] = 'WFC4 ' with pytest.raises(AssertionError): utils.WFC3_magnitude_zpt_reader(h) h['INSTRUME'] = 'WFC3 ' h.pop('PHOTPLAM') with pytest.raises(KeyError): utils.WFC3_magnitude_zpt_reader(h)
def from_buff(cls, buff, compress=False, **kwargs): """ Creates a new _AsdfHDU from a given AsdfFile object. Parameters ---------- buff : io.BytesIO A buffer containing an ASDF metadata tree compress : bool, optional Gzip compress the contents of the ASDF HDU """ if compress: buff = gzip.GzipFile(fileobj=buff, mode='wb') # A proper HDU should still be padded out to a multiple of 2880 # technically speaking data_length = buff.tell() padding = (_pad_length(data_length) * cls._padding_byte).encode('ascii') buff.write(padding) buff.seek(0) cards = [ ('XTENSION', cls._extension, 'ASDF extension'), ('BITPIX', 8, 'array data type'), ('NAXIS', 1, 'number of array dimensions'), ('NAXIS1', data_length, 'Axis length'), ('PCOUNT', 0, 'number of parameters'), ('GCOUNT', 1, 'number of groups'), ('COMPRESS', compress, 'Uses gzip compression'), ('EXTNAME', cls._extension, 'Name of ASDF extension'), ] header = Header(cards) return cls._readfrom_internal(_File(buff), header=header)
def add_fffits_metadata(ff_filename, config, platepars_recalibrated, fallback_platepar): """ Add FITS metadata and WCS to FF files generated by RMS Args: ff_filename (str): full or relative path to FF file config (RMS.Config): config instance platepars_recalibrated (dict): dictionary with recalibrated platepars fallback_platepar (RMS.Platepar): platepar with fitted stars Returns: None """ ff_basename = os.path.basename(ff_filename) platepar_recalibrated = Platepar() try: platepar_data = platepars_recalibrated[ff_basename] with open("platepar_tmp.cal", "w") as f: json.dump(platepar_data, f) platepar_recalibrated.read("platepar_tmp.cal") except (FileNotFoundError, KeyError): platepar_recalibrated = fallback_platepar logger.warning(f"Using non-recalibrated platepar for {ff_basename}") fftime = getMiddleTimeFF(ff_basename, config.fps) fit_xy = np.array(fallback_platepar.star_list)[:, 1:3] _, fit_ra, fit_dec, _ = xyToRaDecPP([fftime] * len(fit_xy), fit_xy[:, 0], fit_xy[:, 1], [1] * len(fit_xy), platepar_recalibrated, extinction_correction=False) x0 = platepar_recalibrated.X_res / 2 y0 = platepar_recalibrated.Y_res / 2 _, ra0, dec0, _ = xyToRaDecPP([fftime], [x0], [y0], [1], platepar_recalibrated, extinction_correction=False) w = fit_wcs(fit_xy[:, 0], fit_xy[:, 1], fit_ra, fit_dec, x0, y0, ra0[0], dec0[0], 5, projection="ZEA") hdu_list = fits.open(ff_filename, scale_back=True) obstime = Time(filenameToDatetime(ff_basename)) header_meta = {} header_meta["OBSERVER"] = config.stationID.strip() header_meta["INSTRUME"] = "Global Meteor Network" header_meta["MJD-OBS"] = obstime.mjd header_meta["DATE-OBS"] = obstime.fits header_meta["NFRAMES"] = 256 header_meta["EXPTIME"] = 256 / config.fps header_meta["SITELONG"] = round(config.longitude, 2) header_meta["SITELAT"] = round(config.latitude, 2) for hdu in hdu_list: if hdu.header[ "NAXIS"] == 0: # First header is not an image so should not get WCS new_header = Header() else: new_header = w.to_fits(relax=True)[0].header for key, value in header_meta.items(): new_header.append((key, value)) for key, value in new_header.items(): if key in hdu.header: continue hdu.header[key] = value hdu_list.writeto(ff_filename, overwrite=True)
def __init__(self, data=None, header=None, do_not_scale_image_data=False, uint=True, scale_back=False, ignore_blank=False, **kwargs): from .groups import GroupsHDU super().__init__(data=data, header=header) if data is DELAYED: # Presumably if data is DELAYED then this HDU is coming from an # open file, and was not created in memory if header is None: # this should never happen raise ValueError('No header to setup HDU.') else: # TODO: Some of this card manipulation should go into the # PrimaryHDU and GroupsHDU subclasses # construct a list of cards of minimal header if isinstance(self, ExtensionHDU): c0 = ('XTENSION', 'IMAGE', self.standard_keyword_comments['XTENSION']) else: c0 = ('SIMPLE', True, self.standard_keyword_comments['SIMPLE']) cards = [ c0, ('BITPIX', 8, self.standard_keyword_comments['BITPIX']), ('NAXIS', 0, self.standard_keyword_comments['NAXIS']) ] if isinstance(self, GroupsHDU): cards.append( ('GROUPS', True, self.standard_keyword_comments['GROUPS'])) if isinstance(self, (ExtensionHDU, GroupsHDU)): cards.append( ('PCOUNT', 0, self.standard_keyword_comments['PCOUNT'])) cards.append( ('GCOUNT', 1, self.standard_keyword_comments['GCOUNT'])) if header is not None: orig = header.copy() header = Header(cards) header.extend(orig, strip=True, update=True, end=True) else: header = Header(cards) self._header = header self._do_not_scale_image_data = do_not_scale_image_data self._uint = uint self._scale_back = scale_back # Keep track of whether BZERO/BSCALE were set from the header so that # values for self._orig_bzero and self._orig_bscale can be set # properly, if necessary, once the data has been set. bzero_in_header = 'BZERO' in self._header bscale_in_header = 'BSCALE' in self._header self._bzero = self._header.get('BZERO', 0) self._bscale = self._header.get('BSCALE', 1) # Save off other important values from the header needed to interpret # the image data self._axes = [ self._header.get('NAXIS' + str(axis + 1), 0) for axis in range(self._header.get('NAXIS', 0)) ] # Not supplying a default for BITPIX makes sense because BITPIX # is either in the header or should be determined from the dtype of # the data (which occurs when the data is set). self._bitpix = self._header.get('BITPIX') self._gcount = self._header.get('GCOUNT', 1) self._pcount = self._header.get('PCOUNT', 0) self._blank = None if ignore_blank else self._header.get('BLANK') self._verify_blank() self._orig_bitpix = self._bitpix self._orig_blank = self._header.get('BLANK') # These get set again below, but need to be set to sensible defaults # here. self._orig_bzero = self._bzero self._orig_bscale = self._bscale # Set the name attribute if it was provided (if this is an ImageHDU # this will result in setting the EXTNAME keyword of the header as # well) if 'name' in kwargs and kwargs['name']: self.name = kwargs['name'] if 'ver' in kwargs and kwargs['ver']: self.ver = kwargs['ver'] # Set to True if the data or header is replaced, indicating that # update_header should be called self._modified = False if data is DELAYED: if (not do_not_scale_image_data and (self._bscale != 1 or self._bzero != 0)): # This indicates that when the data is accessed or written out # to a new file it will need to be rescaled self._data_needs_rescale = True return else: # Setting data will update the header and set _bitpix, _bzero, # and _bscale to the appropriate BITPIX for the data, and always # sets _bzero=0 and _bscale=1. self.data = data # Check again for BITPIX/BSCALE/BZERO in case they changed when the # data was assigned. This can happen, for example, if the input # data is an unsigned int numpy array. self._bitpix = self._header.get('BITPIX') # Do not provide default values for BZERO and BSCALE here because # the keywords will have been deleted in the header if appropriate # after scaling. We do not want to put them back in if they # should not be there. self._bzero = self._header.get('BZERO') self._bscale = self._header.get('BSCALE') # Handle case where there was no BZERO/BSCALE in the initial header # but there should be a BSCALE/BZERO now that the data has been set. if not bzero_in_header: self._orig_bzero = self._bzero if not bscale_in_header: self._orig_bscale = self._bscale
def test_slightly_different_headers(self): ha = Header([('A', 1), ('B', 2), ('C', 3)]) hb = ha.copy() hb['C'] = 4 assert not HeaderDiff(ha, hb).identical
def test_common_keywords(self): ha = Header([('A', 1), ('B', 2), ('C', 3)]) hb = ha.copy() hb['C'] = 4 hb['D'] = (5, 'Comment') assert HeaderDiff(ha, hb).common_keywords == ['A', 'B', 'C']