def altaz2radec(az, alt, t): """ Convert Azimuth-Elevation coordinates into RA-Dec equatorial coordinates It is expected that the observer is NenuFAR in Nancay The conversion is computed for a sky set at time t Parameters ---------- az : float Azimuth in degrees alt : float Elevation in degrees t : str or Time Time at which to compute the corresponding RA Dec Returns ------- RA : float Right Ascension in radians Dec : float Declination in radians """ if not isinstance(t, Time): try: t = Time(t) except: raise ValueError( "\n\t=== Time syntax should be 'YYYY-MM-DD hh:mm:ss' ===") frame = coord.AltAz(obstime=t, location=nancay()) altaz = coord.SkyCoord(az * u.deg, alt * u.deg, frame=frame) radec = altaz.transform_to(coord.FK5(equinox='J2000')) return radec.ra.rad, radec.dec.rad
def test_frames(tmpdir): frames = [ cf.CelestialFrame(reference_frame=coord.ICRS()), cf.CelestialFrame(reference_frame=coord.FK5( equinox=time.Time('2010-01-01'))), cf.CelestialFrame(reference_frame=coord.FK4( equinox=time.Time('2010-01-01'), obstime=time.Time('2015-01-01'))), cf.CelestialFrame(reference_frame=coord.FK4NoETerms( equinox=time.Time('2010-01-01'), obstime=time.Time('2015-01-01'))), cf.CelestialFrame(reference_frame=coord.Galactic()), cf.CelestialFrame( reference_frame=coord.Galactocentric(galcen_distance=5.0 * u.m, galcen_ra=45 * u.deg, galcen_dec=1 * u.rad, z_sun=3 * u.pc, roll=3 * u.deg)), cf.CelestialFrame( reference_frame=coord.GCRS(obstime=time.Time('2010-01-01'), obsgeoloc=[1, 3, 2000] * u.pc, obsgeovel=[2, 1, 8] * (u.m / u.s))), cf.CelestialFrame(reference_frame=coord.CIRS( obstime=time.Time('2010-01-01'))), cf.CelestialFrame(reference_frame=coord.ITRS( obstime=time.Time('2022-01-03'))), cf.CelestialFrame(reference_frame=coord.PrecessedGeocentric( obstime=time.Time('2010-01-01'), obsgeoloc=[1, 3, 2000] * u.pc, obsgeovel=[2, 1, 8] * (u.m / u.s))) ] tree = {'frames': frames} helpers.assert_roundtrip_tree(tree, tmpdir)
def test1(tmpdir, ret=False): coord = coords.FK5(ra=14*u.deg, dec=-7.5*u.deg) target = Target(id='NGC 7356', coordinates=coord, name="my future Nobel target", aliases=['black hole catalog 7', "Darth Vader's home"], meta={'secret':'Top', 'coordinates':"are wrong for security purposes"}) tree = {'target': target} if ret: return target helpers.assert_roundtrip_tree(tree, tmpdir)
def reg2sl(self): """Convert a region file to a starlist.""" import pyregion import astropy.coordinates as coord import astropy.units as u print( "Converting '{input:s}' ds9 region file to '{output:s}' starlist.". format(**vars(self.opts))) with open(self.opts.input, 'r') as regionfile: regions = pyregion.parse(regionfile.read()) with open(self.opts.output, 'w') as starlist: for i, region in enumerate(regions): if region.coord_format != 'fk5': self.log.critical("Coordinate system {!r} unkown!".format( region.coord_format)) break # We only parse circles! if region.name == 'circle': # Parse the location ra, dec = region.coord_list[0:2] loc = coord.FK5(ra=ra, dec=dec, unit=(u.degree, u.degree)) if "text" in region.attr[1]: name = region.attr[1]["text"] else: name = "Target%d" % i # Handle comments comments = region.comment.split() for comment in comments: if comment.startswith("text={"): comments.remove(comment) # Write out the starlist line starlist.write( "{name:<15s} {ra:s} {dec:s} {epoch:.0f} {keywords:s}". format( name=name.strip(), ra=loc.ra.format(u.hour, sep=" ", pad=True), dec=loc.dec.format(sep=" ", alwayssign=True), epoch=loc.equinox.jyear, keywords=" ".join(comments), )) starlist.write("\n") else: self.log.warning( "Region {!r} not parsed. It is not a circle.".format( region))
def nhvals(tobs, fk5f, oloc, otmz): """ Evaluate new header values (formatted strings) from the date/time of observation ('astropy.time.Time' object), target coordinates ('astropy.coord.SkyCood' object), observatory location ('astropy.coord.EarthLocation' object) and timezone offset ('astropy.time.TimezoneInfo' object). Parameters ---------- tobs : astropy.time.Time Date/Time of observation. fk5f : astropy.coord.SkyCood Target coordinates. oloc : astropy.coord.EarthLocation Observatory location. otmz : astropy.time.TimezoneInfo Observatoty timezone offset. Returns ------- tp_str : tuple of strings Tuple of strings with UTC time (ISOT format), julian date, sidereal time, hour angle, airmass and local time of observation. """ # create altaz and fk5 precessed frames and coords altaz_frame = coord.AltAz(obstime=tobs, location=oloc) altaz_c = fk5f.transform_to(altaz_frame) fk5p_frame = coord.FK5(equinox=tobs) fk5p_c = fk5f.transform_to(fk5p_frame) # evaluate sidereal time, hour angle and local time st = tobs.sidereal_time('apparent') ha = st - fk5p_c.ra.to(u.hourangle) lt = tobs.to_datetime(timezone=otmz) # create output tuple of strings tobs_str = tobs.isot jd_str = f'{tobs.jd:.5f}' st_str = st.to_string(unit=u.hour, sep=':', precision=3) ha_str = ha.to_string(unit=u.hour, sep=':', precision=3) airmass_str = f'{altaz_c.secz:.5f}' lt_str = lt.strftime('%H:%M:%S') tp_str = (tobs_str, jd_str, st_str, ha_str, airmass_str, lt_str) return tp_str
def test_composite_frame(tmpdir): icrs = coord.ICRS() fk5 = coord.FK5() cel1 = cf.CelestialFrame(reference_frame=icrs) cel2 = cf.CelestialFrame(reference_frame=fk5) spec1 = cf.SpectralFrame(name='freq', unit=(u.Hz, ), axes_order=(2, )) spec2 = cf.SpectralFrame(name='wave', unit=(u.m, ), axes_order=(2, )) comp1 = cf.CompositeFrame([cel1, spec1]) comp2 = cf.CompositeFrame([cel2, spec2]) comp = cf.CompositeFrame( [comp1, cf.SpectralFrame(axes_order=(3, ), unit=(u.m, ))]) tree = {'comp1': comp1, 'comp2': comp2, 'comp': comp} helpers.assert_roundtrip_tree(tree, tmpdir)
def test1(tmpdir, ret=False): prop = pr_test1(None, ret=True) coord = coords.FK5(ra=14 * u.deg, dec=-7.5 * u.deg) target = ta_test1(None, ret=True) instrument = in_test1(None, ret=True) tel = tel_test1(None, ret=True) obscontext = ObsContext(telescope=tel, instrument=instrument, proposal=prop, observers=[obs1, obs2], target=target, meta={'purpose': 'travel'}) tree = {'obs_context': obscontext} if ret: return obscontext helpers.assert_roundtrip_tree(tree, tmpdir)
def _test_isc( self, isc, representation_type=coord.UnitSphericalRepresentation, ) -> None: """Runs through all the levels, testing type.""" inst = isc.transform_to(coord.FK5()) assert isinstance(inst, coord.SkyCoord) assert isinstance(inst, icoord.InterpolatedSkyCoord) assert isinstance(inst.frame, coord.FK5) assert isinstance(inst.frame.data, representation_type) assert isinstance(inst.frame.data, icoord.InterpolatedRepresentation) assert isinstance(inst.frame.data.data, representation_type)
def v_sun(mjd, ra_source, dec_source): """ LSR velocity of the sun Notes ===== The velocity of the Sun with respect to the Local Standard of Rest is the conventional value of 20.0 km/s toward RA, dec = 18h03m50.29s, +30d00'16.8 (J2000) @param mjd : int mean Julian day @param ra_source : float Right ascension in decimal hours @param dec_source : float Declination in decimal degrees @return: float the projection onto the line of sight of the velocity of the Sun with respect to the Local Standard of Rest on the date given by mjd at UT = 0h. """ LSR = APc.SkyCoord('''18h03m50.29s +30d00'16.8"''') # Precess solar motion from J2000 to epoch mjd = APt.Time(mjd, format='mjd') LSRmjd = LSR.transform_to(APc.FK5(equinox=mjd)) ra_epoch = LSRmjd.ra.radian dec_epoch = LSRmjd.dec.radian # Cartesian coordinates v_x = 20.0 * math.cos(ra_epoch) * math.cos(dec_epoch) v_y = 20.0 * math.sin(ra_epoch) * math.cos(dec_epoch) v_z = 20.0 * math.sin(dec_epoch) # Source direction in Cartesian coordinates ra = ra_source * pi / 12 dec = dec_source * pi / 180 x = math.cos(dec) * math.cos(ra) y = math.cos(dec) * math.sin(ra) z = math.sin(dec) # Dot product v_sun = -v_x * x - v_y * y - v_z * z return v_sun
def create_test_frames(): """Creates an array of frames to be used for testing.""" # Suppress warnings from astropy that are caused by having 'dubious' dates # that are too far in the future. It's not a concern for the purposes of # unit tests. See issue #5809 on the astropy GitHub for discussion. from astropy._erfa import ErfaWarning warnings.simplefilter("ignore", ErfaWarning) frames = [ cf.CelestialFrame(reference_frame=coord.ICRS()), cf.CelestialFrame(reference_frame=coord.FK5( equinox=time.Time('2010-01-01'))), cf.CelestialFrame(reference_frame=coord.FK4( equinox=time.Time('2010-01-01'), obstime=time.Time('2015-01-01'))), cf.CelestialFrame(reference_frame=coord.FK4NoETerms( equinox=time.Time('2010-01-01'), obstime=time.Time('2015-01-01'))), cf.CelestialFrame(reference_frame=coord.Galactic()), cf.CelestialFrame(reference_frame=coord.Galactocentric( # A default galcen_coord is used since none is provided here galcen_distance=5.0 * u.m, z_sun=3 * u.pc, roll=3 * u.deg)), cf.CelestialFrame( reference_frame=coord.GCRS(obstime=time.Time('2010-01-01'), obsgeoloc=[1, 3, 2000] * u.pc, obsgeovel=[2, 1, 8] * (u.m / u.s))), cf.CelestialFrame(reference_frame=coord.CIRS( obstime=time.Time('2010-01-01'))), cf.CelestialFrame(reference_frame=coord.ITRS( obstime=time.Time('2022-01-03'))), cf.CelestialFrame(reference_frame=coord.PrecessedGeocentric( obstime=time.Time('2010-01-01'), obsgeoloc=[1, 3, 2000] * u.pc, obsgeovel=[2, 1, 8] * (u.m / u.s))), cf.StokesFrame(), cf.TemporalFrame(time.Time("2011-01-01")) ] return frames
def J2000_to_CIRS(MJD, UT, ra2000, dec2000): """ CIRS right ascension and declination from J2000 @param MJD : int mean Julian day @param UT : float decimal hours @param ra2000 : float radians @param dec2000 : float radians @return: tuple (hour, deg) """ t = APt.Time(MJD + UT / 24., format='mjd') coords = APc.SkyCoord(ra=ra2000 * u.rad, dec=dec2000 * u.rad, frame='icrs') logger.debug("J2000_to_CIRS: J2000 position = %s", coords) c = coords.transform_to(APc.FK5(equinox=t)) logger.debug("J2000_to_CIRS: CIRS position = %s", c) return c.ra.hourangle, c.dec.deg
from astropy import coordinates as coord from astropy.tests.helper import pytest from .. import coordinate_frames as cf from .. import wcs coord_frames = coord.builtin_frames.__all__[:] # Need to write a better test, using a dict {coord_frame: input_parameters} # For now remove OffsetFrame, issue #55 try: coord_frames.remove("SkyOffsetFrame") except ValueError as ve: pass icrs = coord.ICRS() fk5 = coord.FK5() cel1 = cf.CelestialFrame(reference_frame=icrs) cel2 = cf.CelestialFrame(reference_frame=fk5) spec1 = cf.SpectralFrame(name='freq', unit=[ u.Hz, ], axes_order=(2, )) spec2 = cf.SpectralFrame(name='wave', unit=[ u.m, ], axes_order=(2, )) comp1 = cf.CompositeFrame([cel1, spec1]) comp2 = cf.CompositeFrame([cel2, spec2]) comp = cf.CompositeFrame( [comp1, cf.SpectralFrame(axes_order=(3, ), unit=(u.m, ))])
def findsources(image,cube,check=False,output='.',spectra=False,helio=0,nsig=2., minarea=10.,regmask=None,clean=True,outspec='Spectra',marz=False, rphot=False, sname='MUSE'): """ Take a detection image (collapse of a cube), or median of an RGB, or whatever you want (but aligned to the cube) and run sourceextractor Use SEP utilities http://sep.readthedocs.org/en/stable/ image -> fits file of image to process check -> if true write a bunch of check mages output -> where to dump the output cube -> the cube used to extract spectra spectra -> if True, extract spectra in VACUUM wave!! helio -> pass additional heliocentric correction nsig -> number of skyrms used for source id minarea -> minimum area for extraction regmask -> ds9 region file (image) of regions to be masked before extraction [e.g. edges] clean -> clean souces outspec -> where to store output spectra marz -> write spectra in also marz format (spectra needs to be true). If set to numerical value, this is used as an r-band magnitude limit. rphot -> perform r-band aperture photometry and add r-band magnitudes to the catalogue sname -> prefix for the source names. Default = MUSE """ import sep from astropy.io import fits from astropy import wcs from astropy import coordinates from astropy import units as u from astropy import table import numpy as np import os try: from mypython.ifu import muse_utils as utl from mypython.fits import pyregmask as msk except ImportError: from mypython import ifu from ifu import muse_utils as utl from mypython import fits from fits import pyregmask as msk from astropy.io import fits from shutil import copyfile import glob #open image img=fits.open(image) try: header=img[1].header except: header= img[0].header imgwcs = wcs.WCS(header) try: #this is ok for narrow band images data=img[1].data except: #white cubex images data=img[0].data data=data.byteswap(True).newbyteorder() #grab effective dimension nex,ney=data.shape #close fits img.close() #create bad pixel mask if(regmask): Mask=msk.PyMask(ney,nex,regmask,header=img[0].header) for ii in range(Mask.nreg): Mask.fillmask(ii) if(ii == 0): badmask=Mask.mask else: badmask+=Mask.mask badmask=1.*badmask else: badmask=np.zeros((nex,ney)) if(check): print('Dumping badmask') hdumain = fits.PrimaryHDU(badmask,header=header) hdulist = fits.HDUList([hdumain]) hdulist.writeto(output+"/badmask.fits",overwrite=True) #check background level, but do not subtract it print('Checking background levels') bkg = sep.Background(data,mask=badmask) print('Residual background level ', bkg.globalback) print('Residual background rms ', bkg.globalrms) if(check): print('Dumping sky...') #dump sky properties back = bkg.back() rms = bkg.rms() hdumain = fits.PrimaryHDU(back,header=header) hdubk = fits.ImageHDU(back) hdurms = fits.ImageHDU(rms) hdulist = fits.HDUList([hdumain,hdubk,hdurms]) hdulist.writeto(output+"/skyprop.fits",overwrite=True) #extracting sources at nsigma thresh = nsig * bkg.globalrms # segmap = np.zeros((header["NAXIS1"],header["NAXIS2"])) objects, segmap=sep.extract(data,thresh,segmentation_map=True, minarea=minarea,clean=clean,mask=badmask,deblend_cont=0.0001) print("Extracted {} objects... ".format(len(objects))) if(spectra): if not os.path.exists(outspec): os.makedirs(outspec) if((check) | (spectra)): #create a detection mask alla cubex srcmask=np.zeros((1,data.shape[0],data.shape[1])) nbj=1 print('Generating spectra...') #loop over detections for obj in objects: #init mask tmpmask=np.zeros((data.shape[0],data.shape[1]),dtype=np.bool) tmpmask3d=np.zeros((1,data.shape[0],data.shape[1]),dtype=np.bool) #fill this mask sep.mask_ellipse(tmpmask,obj['x'],obj['y'],obj['a'],obj['b'],obj['theta'],r=2) tmpmask3d[0,:,:]=tmpmask[:,:] srcmask=srcmask+tmpmask3d*nbj if(spectra): savename="{}/id{}.fits".format(outspec,nbj) if not os.path.exists(savename): utl.cube2spec(cube,obj['x'],obj['y'],None,write=savename, shape='mask',helio=helio,mask=tmpmask3d,tovac=True) else: print("{} already exists. Skipping it...".format(savename)) #go to next nbj=nbj+1 if(check): print('Dumping source mask...') hdumain = fits.PrimaryHDU(srcmask,header=header) hdubk = fits.ImageHDU(srcmask) hdulist = fits.HDUList([hdumain,hdubk]) hdulist.writeto(output+"/source.fits",overwrite=True) print('Dumping segmentation map') hdumain = fits.PrimaryHDU(segmap,header=header) hdubk = fits.ImageHDU(segmap) hdulist = fits.HDUList([hdumain,hdubk]) hdulist.writeto(output+"/segmap.fits",overwrite=True) #Generate source names using coordinates and name prefix ra, dec = imgwcs.wcs_pix2world(objects['x'], objects['y'],0) coord = coordinates.FK5(ra*u.degree, dec*u.degree) rastr = coord.ra.to_string(u.hour, precision=2, sep='') decstr = coord.dec.to_string(u.degree, precision=1, sep='', alwayssign=True) name = [sname+'J{0}{1}'.format(rastr[k], decstr[k]) for k in range(len(rastr))] ids = np.arange(len(name)) #write source catalogue print('Writing catalogue..') tab = table.Table(objects) tab.add_column(table.Column(name),0,name='name') tab.add_column(table.Column(ids),0,name='ID') tab.write(output+'/catalogue.fits',overwrite=True) #cols = fits.ColDefs(objects) #cols.add_col(fits.Column(name, format='A')) #tbhdu = fits.BinTableHDU.from_columns(cols) #tbhdu.writeto(output+'/catalogue.fits',clobber=True) #rband photometry if (rphot): if not os.path.exists(output+'/Image_R.fits'): rimg, rvar, rwcsimg = utl.cube2img(cube, filt=129, write=output+'/Image_R.fits') phot_r = sourcephot(output+'/catalogue.fits', output+'/Image_R.fits', output+'/segmap.fits', image) phot_r.add_column(table.Column(name),1,name='name') tbhdu = fits.open(output+'/catalogue.fits')[1] tbhdu2 = fits.BinTableHDU(phot_r) hdulist = fits.HDUList([fits.PrimaryHDU(), tbhdu, tbhdu2]) hdulist.writeto(output+'/catalogue.fits',overwrite=True) if((marz) & (spectra)): #if marz is True but no magnitude limit set, create marz file for whole catalogue if marz==True: marz_file(image, output+'/catalogue.fits', outspec, output) else: #create folder and catalogue with just sources brighter than mag limit if os.path.exists(output + '/spectra_r' + str(marz)): files = glob.glob(output + '/spectra_r' + str(marz) +'/*') for f in files: os.remove(f) else: os.mkdir(output + '/spectra_r' + str(marz)) mag = phot_r['MAGSEG'] #add in x y pixels from original catalogue x, y = tbhdu.data['x'], tbhdu.data['y'] phot_r['x'], phot_r['y'] = x, y #add in ra,dec img = fits.open(image) mywcs = wcs.WCS(img[0].header) ra, dec = mywcs.all_pix2world(x,y,0) phot_r['RA'] = ra phot_r['dec'] = dec for i in range(len(mag)): if mag[i] < marz: copyfile((output + '/spectra/id' + str(i+1) + '.fits'), (output + '/spectra_r' + str(marz) + '/id' + str(i+1) + '.fits')) #Write photometry catalog with objects below magnitude limit excluded phot_r.remove_rows(phot_r['MAGSEG'] > marz) catalogue_lim_name = (output + '/catalogue_r' + str(marz) +'.fits') if os.path.exists(catalogue_lim_name): os.remove(catalogue_lim_name) phot_r.write(catalogue_lim_name) outspec = output + '/spectra_r' + str(marz) marz_file(image, output+'/catalogue_r' + str(marz) +'.fits', outspec, output, r_lim=marz) print('All done') return objects
DECSTRNG = header['DECSTRNG'] naxis1 = header['NAXIS1'] naxis2 = header['NAXIS2'] RA = coord.Angle(RASTRNG, unit=u.hour) DEC = coord.Angle(DECSTRNG, unit=u.degree) EQUINOX = header['EQUINOX'] INSTRUMENT = header['INSTRUME'] OBJECT = header['OBJECT'] EXPTIME = header['EXPTIME'] # process coordinates to J2000 epoch c = SkyCoord(ra=RA.deg * u.degree, dec=DEC.degree * u.degree, frame='fk5', equinox='J' + str(EQUINOX)) #print 'original coords = ',c c2000 = c.transform_to(coord.FK5(equinox='J2000.0')) #print 'J2000 coords = ',c2000 # trying to clear header and add the min info required header.clear() header.append(card=('FILTER', FILTER, 'FILTER')) header.append(card=('DATASEC', ccdsec, 'DATA SECTION')) header.append(card=('INSTRUME', INSTRUMENT, 'INSTRUMENT')) if 'CRVAL1' in header: header['CRVAL1'] = (c.ra.value, 'RA of reference point') else: header.append(card=('CRVAL1', c.ra.value, 'RA of reference point')) if 'CRVAL2' in header: header['CRVAL2'] = (c.dec.value, 'DEC of reference point') else: header.append(card=('CRVAL2', c.dec.value, 'DEC of reference point'))
#magblower=float(sys.argv[4]) #magbupper=float(sys.argv[5]) #this seems to successfully transform the j2000 to mag coordinates #c = coord.FK5(ra=256*u.deg, dec=9*u.deg) #ms = c.transform_to(MagellanicStreamNidever08) #print(ms) importreg = pd.read_csv(regfileloc, delim_whitespace=True, header=None) regfile = importreg.to_numpy(dtype='str') clippedarray = np.zeros(11) #convert from gal to mag and check elements for i in range(0, len(regfile)): #load in coordinates as j2000, references one of the tile vertices i think,should change to centre where the text is placed c = coord.FK5(ra=float(regfile[i, 1]) * u.deg, dec=float(regfile[i, 2]) * u.deg) #convert to mag coords ms = c.transform_to(MagellanicStreamNidever08) #take coord elements sep = Angle([ms.L, ms.B]) #output coord elements as decimal strings decimal = sep.to_string(decimal=True) #clip elements based on specified bounds if float(decimal[0]) < maglupper: if float(decimal[0]) > magllower: if float(decimal[1]) < magbupper: if float(decimal[1]) > magblower: clippedarray = np.vstack((clippedarray, regfile[i])) clippedarray = clippedarray[1:, ]
def findsources(image, cube, varima=None, check=False, output='./', spectra=False, helio=0, nsig=2., minarea=10., deblend_cont=0.0001, regmask=None, invregmask=False, fitsmask=None, clean=True, outspec='Spectra', marz=False, rphot=False, detphot=False, sname='MUSE'): """ Take a detection image (collapse of a cube), or median of an RGB, or whatever you want (but aligned to the cube) and run sourceextractor Use SEP utilities http://sep.readthedocs.org/en/stable/ image -> fits file of image to process cube -> the cube used to extract spectra varima -> the noise image corresponding to the science image (std), optional check -> if true write a bunch of check mages output -> where to dump the output spectra -> if True, extract spectra in VACUUM wave!! helio -> pass additional heliocentric correction nsig -> number of skyrms used for source id minarea -> minimum area for extraction regmask -> ds9 region file (image) of regions to be masked before extraction [e.g. edges] invregmask -> if True invert the mask (region defines good area) fitsmask -> Fits file with good mask, overrides regmask clean -> clean souces outspec -> where to store output spectra marz -> write spectra in also marz format (spectra needs to be true). If set to numerical value, this is used as an r-band magnitude limit. detphot -> perform aperture phtometry on the detection image and add magnitues to the catalogue rphot -> perform r-band aperture photometry and add r-band magnitudes to the catalogue sname -> prefix for the source names. Default = MUSE """ import sep from astropy.io import fits from astropy import wcs from astropy import coordinates from astropy import units as u from astropy import table import numpy as np import os from mypython.ifu import muse_utils as utl from mypython.fits import pyregmask as msk from shutil import copyfile import glob #open image img = fits.open(image) header = img[0].header imgwcs = wcs.WCS(header) try: #this is ok for narrow band images data = img[1].data except: #white cubex images data = img[0].data data = data.byteswap(True).newbyteorder() #grab effective dimension nex, ney = data.shape #close fits img.close() if (varima): var = fits.open(varima) try: datavar = var[1].data except: datavar = var[0].data datavar = datavar.byteswap(True).newbyteorder() #grab effective dimension stdx, stdy = datavar.shape #close fits var.close() if (stdx != nex) or (stdy != ney): print( "The noise image does not have the same dimensions as the science image" ) return -1 #create bad pixel mask if (fitsmask): print("Using FITS image for badmask") hdumsk = fits.open(fitsmask) try: badmask = hdumsk[1].data except: badmask = hdumsk[0].data badmask = badmask.byteswap(True).newbyteorder() elif (regmask): print("Using region file for badmask") Mask = msk.PyMask(ney, nex, regmask, header=img[0].header) for ii in range(Mask.nreg): Mask.fillmask(ii) if (ii == 0): badmask = Mask.mask else: badmask += Mask.mask badmask = 1. * badmask else: badmask = np.zeros((nex, ney)) if (regmask) and (invregmask) and not (fitsmask): badmask = 1 - badmask if (check): print('Dumping badmask') hdumain = fits.PrimaryHDU(badmask, header=header) hdulist = fits.HDUList([hdumain]) hdulist.writeto(output + "/badmask.fits", overwrite=True) #check background level, but do not subtract it print('Checking background levels') bkg = sep.Background(data, mask=badmask) print('Residual background level ', bkg.globalback) print('Residual background rms ', bkg.globalrms) if (check): print('Dumping sky...') #dump sky properties back = bkg.back() rms = bkg.rms() hdumain = fits.PrimaryHDU(back, header=header) hdubk = fits.ImageHDU(back) hdurms = fits.ImageHDU(rms) hdulist = fits.HDUList([hdumain, hdubk, hdurms]) hdulist.writeto(output + "/skyprop.fits", overwrite=True) if (varima): #Use nsigma threshold and a pixel by pixel effective treshold based on variance map thresh = nsig objects, segmap = sep.extract(data, thresh, var=datavar, segmentation_map=True, minarea=minarea, clean=clean, mask=badmask, deblend_cont=deblend_cont, deblend_nthresh=32) else: #extracting sources at nsigma, use constant threshold thresh = nsig * bkg.globalrms objects, segmap = sep.extract(data, thresh, segmentation_map=True, minarea=minarea, clean=clean, mask=badmask, deblend_cont=deblend_cont, deblend_nthresh=32) print("Extracted {} objects... ".format(len(objects))) ids = np.arange(len(objects)) + 1 if (spectra): if not os.path.exists(outspec): os.makedirs(outspec) if ((check) | (spectra)): #create a detection mask a'la cubex srcmask = np.zeros((data.shape[0], data.shape[1])) print('Generating spectra...') #loop over detections for nbj in ids: obj = objects[nbj - 1] #init mask tmpmask = np.zeros((data.shape[0], data.shape[1]), dtype=np.bool) #fill this mask sep.mask_ellipse(tmpmask, obj['x'], obj['y'], obj['a'], obj['b'], obj['theta'], r=2) #add in global mask srcmask = srcmask + tmpmask * nbj #verify conflicts, resolve using segmentation map if np.nanmax(srcmask) > nbj: blended = (srcmask > nbj) srcmask[blended] = segmap[blended] #Now loop again and extract spectra if required if (spectra): #Verify that the source mask has the same number of objects as the object list if not len(np.unique(srcmask[srcmask > 0])) == len(objects): print( "Mismatch between number of objects and number of spectra to extract." ) for nbj in ids: savename = "{}/id{}.fits".format(outspec, nbj) tmpmask3d = np.zeros((1, data.shape[0], data.shape[1])) tmpmask3d[0, :, :] = srcmask[:, :] tmpmask3d[tmpmask3d != nbj] = 0 tmpmask3d[tmpmask3d > 0] = 1 tmpmask3d = np.array(tmpmask3d, dtype=np.bool) utl.cube2spec(cube, None, None, None, write=savename, shape='mask', helio=helio, mask=tmpmask3d, tovac=True) if (check): print('Dumping source mask...') hdumain = fits.PrimaryHDU(srcmask, header=header) hdubk = fits.ImageHDU(srcmask) hdulist = fits.HDUList([hdumain, hdubk]) hdulist.writeto(output + "/source.fits", overwrite=True) print('Dumping segmentation map') hdumain = fits.PrimaryHDU(segmap, header=header) hdubk = fits.ImageHDU(segmap) hdulist = fits.HDUList([hdumain, hdubk]) hdulist.writeto(output + "/segmap.fits", overwrite=True) #Generate source names using coordinates and name prefix ra, dec = imgwcs.wcs_pix2world(objects['x'], objects['y'], 0) coord = coordinates.FK5(ra * u.degree, dec * u.degree) rastr = coord.ra.to_string(u.hour, precision=2, sep='', pad=True) decstr = coord.dec.to_string(u.degree, precision=1, sep='', alwayssign=True, pad=True) name = [ sname + 'J{0}{1}'.format(rastr[k], decstr[k]) for k in range(len(rastr)) ] #Generate a column to be used to flag the sources to be used in the analysis #True for all sources at this point use_source = np.ones_like(name, dtype=bool) #write source catalogue print('Writing catalogue..') tab = table.Table(objects) tab.add_column(table.Column(dec), 0, name='DEC') tab.add_column(table.Column(ra), 0, name='RA') tab.add_column(table.Column(name), 0, name='name') tab.add_column(table.Column(ids), 0, name='ID') tab.add_column(table.Column(use_source), name='use_source') tab.write(output + '/catalogue.fits', overwrite=True) if (detphot): #Run source photometry on the extraction image whiteimg, whitevar, whitewcsimg = utl.cube2img(cube, write=output + '/Image_white.fits') phot_det = sourcephot(output + '/catalogue.fits', output + '/Image_white.fits', output + '/segmap.fits', image, zpab=28.35665) phot_det.add_column(table.Column(name), 1, name='name') tbhdu = fits.open(output + '/catalogue.fits') tbhdu.append(fits.BinTableHDU(phot_det)) tbhdu[-1].header['PHOTBAND'] = 'Detection' tbhdu.writeto(output + '/catalogue.fits', overwrite=True) #rband photometry if (rphot): rimg, rvar, rwcsimg = utl.cube2img(cube, filt=129, write=output + '/Image_R.fits') phot_r = sourcephot(output + '/catalogue.fits', output + '/Image_R.fits', output + '/segmap.fits', image) phot_r.add_column(table.Column(name), 1, name='name') tbhdu = fits.open(output + '/catalogue.fits') tbhdu.append(fits.BinTableHDU(phot_r)) tbhdu[-1].header['PHOTBAND'] = 'SDSS_r' tbhdu.writeto(output + '/catalogue.fits', overwrite=True) if ((marz) & (spectra)): #if marz is True but no magnitude limit set, create marz file for whole catalogue if marz > 10 and (rphot): #Requires testing hdu = fits.open(output + '/catalogue.fits') hdu[1].data['use_source'][hdu[2].data['MAGAP'] > marz] = False hdu.writeto(output + '/catalogue.fits', overwrite=True) marz_file(output + '/catalogue.fits', outspec, output, r_lim=marz) else: marz_file(output + '/catalogue.fits', outspec, output) print('All done') return objects
def corrections(lon, lat, alt, ra, dec, mjd): """ Calculate the heliocentric radial velocity corrections for an astronomical source. Parameters ---------- lon : `~astropy.coordinates.Longitude` or float Earth longitude of the observatory (western direction is positive). Can be anything that initialises an `~astropy.coordinates.Angle` object (if float, in degrees). lat : `~astropy.coordinates.Latitude` or float Earth latitude of observatory. Can be anything that initialises an `~astropy.coordinates.Latitude` object (if float, in degrees). alt : `~astropy.units.Quantity` or float Altitude of the observatory (if float, in meters). ra : `~astropy.coordinates.Angle` or float Right ascension of the object for epoch J2000 (if float, in degrees). dec : `~astropy.coordinates.Angle` or float Declination of the object for epoch J2000 (if float, in degrees). mjd : float The modified Julian date for the middle of exposure. Returns ------- barycorr : `~astropy.units.Quantity` The barycentric velocity correction. helcorr : `~astropy.units.Quantity` The heliocentric velocity correction. """ if not isinstance(lon, coord.Longitude): lon = coord.Longitude(lon * u.deg) if not isinstance(lat, coord.Latitude): lat = coord.Latitude(lat * u.deg) if not isinstance(alt, u.Quantity): alt *= u.m if not isinstance(ra, u.Quantity): ra *= u.deg if not isinstance(dec, u.Quantity): dec *= u.deg # Here we specify the location so that we can easily calculate the mean # local siderial time later on time = Time(2.4e6 + mjd, format="jd", location=(lon, lat, alt)) epoch = time.datetime.year + time.datetime.month/12. \ + time.datetime.day/365. # Precess the coordinates to the current epoch coordinate = coord.SkyCoord(ra, dec, frame="fk5").transform_to( coord.FK5(equinox="J%s" % (epoch))) # Convert geodetic latitude into geocentric latitude to correct for rotation # of the Earth dlat = ((-11. * 60. + 32.743) * np.sin(2 * lat) + 1.1633 * np.sin(4 * lat) \ - 0.0026 * np.sin(6 * lat)) * u.degree geocentric_lat = lat + dlat / 3600. # Calculate distance of observer from Earth center r = alt + 6378160.0 * u.m * (0.998327073 \ + 0.001676438 * np.cos(2 * geocentric_lat) \ - 0.000003510 * np.cos(4 * geocentric_lat) \ + 0.000000008 * np.cos(6 * geocentric_lat)) # Calculate rotational velocity perpendicular to the radius vector # Note: 23.934469591229 is the siderial day in hours for 1986 v = 2 * np.pi * r / (23.934469591229 * 3600 * u.second) # Calculate vdiurnal velocity time.delta_ut1_utc = 0 #we get error otherwise. No big dela for this application vdiurnal = v * np.cos(lat) * np.cos(coordinate.dec) \ * np.sin(coordinate.ra - time.sidereal_time("mean")) # Calculate baricentric and heliocentric velocities vh, vb = baryvel(time) # Project along the line of sight projection = np.array([ np.cos(coordinate.dec) * np.cos(coordinate.ra), np.cos(coordinate.dec) * np.sin(coordinate.ra), np.sin(coordinate.dec) ]) vbar = (vb * projection).sum() vhel = (vh * projection).sum() # Using baricentric velocity for correction vbar_correction = vdiurnal + vbar vhel_correction = vdiurnal + vhel # [TODO] it may be useful to return other components of velocity or extra # information about the transforms (e.g., gmst, ut, lmst, dlat, lat, vbar, # vhel, etc) return (vbar_correction, vhel_correction)
from astropy.tests.helper import pytest import astropy.coordinates as coord import astropy.units as u from astropy.table import Table import sys import os import re import requests from ...exceptions import TableParseError from distutils.version import LooseVersion is_python3 = (sys.version_info >= (3, )) GALACTIC_COORDS = coord.Galactic(l=-67.02084, b=-29.75447, unit=(u.deg, u.deg)) ICRS_COORDS = coord.ICRS("05h35m17.3s -05h23m28s") FK4_COORDS = coord.FK4(ra=84.90759, dec=-80.89403, unit=(u.deg, u.deg)) FK5_COORDS = coord.FK5(ra=83.82207, dec=-80.86667, unit=(u.deg, u.deg)) DATA_FILES = { 'id': 'query_id.data', 'coo': 'query_coo.data', 'cat': 'query_cat.data', 'bibobj': 'query_bibobj.data', 'bibcode': 'query_bibcode.data', 'error': 'query_error.data', 'sample': 'query_sample.data', 'region': 'query_sample_region.data', } class MockResponseSimbad(MockResponse): query_regex = re.compile(r'query\s+([a-z]+)\s+')
def ixonslicer(fname, cardtkey='DATE-OBS', after2015=True, hextfile=None): """ Slice FITS image obtained with CCD iXon in kinetic mode. Parameters ---------- fname : str FITS file (full path) to be sliced. cardtkey : str, optional Keyword of the time of observation card. Default: 'DATE-OBS' after2015 : bool, optional Flag if the FITS file was observed after 2015 (i.e., the header is complete). Default: True hextfile : str or None, optional ASCII file (full path) with new cards (keyword and values), to be included in the headers of the output files. Default: None """ finbase = os.path.basename(os.path.abspath(fname)) finrad = os.path.splitext(finbase)[0] findir = os.path.dirname(os.path.abspath(fname)) + '/' listfilesout = [] click.secho(f'\nOPD/LNA iXon FITS slicer (kinetic mode)', bold=True) click.secho(f'{("-" * 72)}', bold=True) click.echo( click.style(f'FITS file: ', bold=True) + click.style(f'{finbase}', bold=False)) click.echo( click.style(f'Card time key: ', bold=True) + click.style(f'{cardtkey}', bold=False)) after2015_str = click.style(f'After 2015? ', bold=True) if after2015: after2015_str += click.style(f'YES', bold=False) else: after2015_str += click.style(f'NO (header incomplete)', bold=False) click.echo(after2015_str) # open fname with fits.open(fname) as hdul_in: click.secho(f'Reading FITS file header...', fg='red', bold=False) hdr = hdul_in[0].header click.secho(f'Reading FITS file data...', fg='red', bold=False) data = hdul_in[0].data try: if data.ndim == 3: n_ima = data.shape[0] opd_info = obs_info('-22:32:04', '-45:34:57', 1864, -3) click.echo( click.style(f'Number of images: ', bold=True) + click.style(f'{n_ima:d}', bold=False)) click.echo( click.style(f'Sliced images directory: ', bold=True) + click.style(f'{findir}', bold=False)) # get header fields values h_t0 = Time(hdr[cardtkey], format='isot', scale='utc', location=opd_info[0]) h_kct = TimeDelta(hdr['KCT'], format='sec') if after2015: # new ixon fits file (after 2015) hdr_test = [hdr['RA'], hdr['DEC'], hdr['EPOCH']] hdr_bool = [ bool(not ss or ss.isspace()) for ss in hdr_test ] if np.any(hdr_bool): click.secho(f'No values in RA, DEC or EPOCH cards.', bold=False) else: click.secho(f'Valid RA, DEC and EPOCH cards.', bold=False) h_ra = coord.Angle(hdr['RA'], unit=u.hourangle) h_dec = coord.Angle(hdr['DEC'], unit=u.deg) h_eqx = Time(float(hdr['EPOCH']), format='jyear') # create fk5 coord object fk5_frame = coord.FK5(equinox=h_eqx) fk5_c = coord.SkyCoord(ra=h_ra, dec=h_dec, frame=fk5_frame) else: # old ixon fits file (before 2015) # check if an ascii file with new header info is # being used if hextfile: hdrext = fits.Header.fromtextfile(hextfile) hextfbase = os.path.basename(os.path.abspath(hextfile)) click.echo( click.style(f'Header file: ', bold=True) + click.style(f'{hextfbase}', bold=False)) click.secho(f'Cards included (keywords):', bold=True) for hkwd in list(hdrext.keys()): hdr[hkwd] = (hdrext[hkwd], hdrext.comments[hkwd]) print(f'{hkwd}') h_ra = coord.Angle(hdr['RA'], unit=u.hourangle) h_dec = coord.Angle(hdr['DEC'], unit=u.deg) h_eqx = Time(float(hdr['EPOCH']), format='jyear') # create fk5 coord object fk5_frame = coord.FK5(equinox=h_eqx) fk5_c = coord.SkyCoord(ra=h_ra, dec=h_dec, frame=fk5_frame) else: click.echo( click.style(f'Header file: ', bold=True) + click.style(f'None', bold=False)) # configure tqdm progress bar bfmt = '{l_bar}{bar}| {n_fmt}/{total_fmt}' bdes = f'Slicing' for i in trange(n_ima, ncols=70, desc=bdes, bar_format=bfmt): # calculate respective image date-obs time_obs = h_t0 + i * h_kct # check if the header is complete or an ascii file # with new header info is being used if (after2015 and (not np.any(hdr_bool)) or (not after2015) and hextfile): # update header fields (if the header is complete or # an ASCII file is being used) nhv = nhvals(time_obs, fk5_c, *opd_info) hdr['DATE-OBS'] = nhv[0] if (not after2015) and hextfile: # create EXPTIME card if it does not exist hdr.set('EXPTIME', f'{hdr["EXPOSURE"]:.5f}', 'Total Exposure Time', after='DATE-OBS') hdr['JD'] = nhv[1] hdr['ST'] = nhv[2] hdr['HA'] = nhv[3] hdr['AIRMASS'] = nhv[4] else: # update header field hdr['DATE-OBS'] = time_obs.isot if (not after2015) and (not hextfile): # create EXPTIME card if it does not exist hdr.set('EXPTIME', f'{hdr["EXPOSURE"]:.5f}', 'Total Exposure Time', after='DATE-OBS') # select output FITS data data_out = data[i] # save updated header + selected data hdu_out = fits.PrimaryHDU(data_out, header=hdr) hdul_out = fits.HDUList([hdu_out]) fileoutdir = f'{findir}{finrad}_{i:04d}.fits' hdul_out.writeto(fileoutdir) fileout = f'{finrad}_{i:04d}.fits' listfilesout.append(fileout) hdul_out.close() else: raise ValueError(f'The FITS data has' f'{data.ndim:d} dimensions.') except ValueError as ve: print(ve) except OSError as oe: print(oe) else: click.secho(f'Sucess!', fg='green', bold=True) listout = f'{findir}list{finrad.upper()}' np.savetxt(listout, listfilesout, fmt='%s') click.echo( click.style(f'Images names list: ', bold=True) + click.style(f'list{finrad.upper()}', bold=False)) click.secho(f'{("-" * 72)}\n', bold=True)
def test_highlevel_api(): J2001 = time.Time('J2001') # <--------------------------"High-level" class--------------------------------> # The "high-level" class is intended to wrap the lower-level classes in such a # way that they can be round-tripped, as well as providing a variety of # convenience functionality. This document is not intended to show *all* of the # possible high-level functionality, rather how the high-level classes are # initialized and interact with the low-level classes # this creates an object that contains an `ICRS` low-level class, initialized # identically to the first ICRS example further up. sc = coords.SkyCoord(coords.SphericalRepresentation(lon=8 * u.hour, lat=5 * u.deg, distance=1 * u.kpc), frame='icrs') # Other representations and `system` keywords delegate to the appropriate # low-level class. The already-existing registry for user-defined coordinates # will be used by `SkyCoordinate` to figure out what various the `system` # keyword actually means. sc = coords.SkyCoord(ra=8 * u.hour, dec=5 * u.deg, frame='icrs') sc = coords.SkyCoord(l=120 * u.deg, b=5 * u.deg, frame='galactic') # High-level classes can also be initialized directly from low-level objects sc = coords.SkyCoord(coords.ICRS(ra=8 * u.hour, dec=5 * u.deg)) # The next example raises an error because the high-level class must always # have position data. with pytest.raises(ValueError): sc = coords.SkyCoord(coords.FK5(equinox=J2001)) # raises ValueError # similarly, the low-level object can always be accessed # this is how it's supposed to look, but sometimes the numbers get rounded in # funny ways # assert repr(sc.frame) == '<ICRS Coordinate: ra=120.0 deg, dec=5.0 deg>' rscf = repr(sc.frame) assert rscf.startswith('<ICRS Coordinate: (ra, dec) in deg') # and the string representation will be inherited from the low-level class. # same deal, should loook like this, but different archituectures/ python # versions may round the numbers differently # assert repr(sc) == '<SkyCoord (ICRS): ra=120.0 deg, dec=5.0 deg>' rsc = repr(sc) assert rsc.startswith('<SkyCoord (ICRS): (ra, dec) in deg') # Supports a variety of possible complex string formats sc = coords.SkyCoord('8h00m00s +5d00m00.0s', frame='icrs') # In the next example, the unit is only needed b/c units are ambiguous. In # general, we *never* accept ambiguity sc = coords.SkyCoord('8:00:00 +5:00:00.0', unit=(u.hour, u.deg), frame='icrs') # The next one would yield length-2 array coordinates, because of the comma sc = coords.SkyCoord(['8h 5d', '2°2′3″ 0.3rad'], frame='icrs') # It should also interpret common designation styles as a coordinate # NOT YET # sc = coords.SkyCoord('SDSS J123456.89-012345.6', frame='icrs') # but it should also be possible to provide formats for outputting to strings, # similar to `Time`. This can be added right away or at a later date. # transformation is done the same as for low-level classes, which it delegates to sc_fk5_j2001 = sc.transform_to(coords.FK5(equinox=J2001)) assert sc_fk5_j2001.equinox == J2001 # The key difference is that the high-level class remembers frame information # necessary for round-tripping, unlike the low-level classes: sc1 = coords.SkyCoord(ra=8 * u.hour, dec=5 * u.deg, equinox=J2001, frame='fk5') sc2 = sc1.transform_to('icrs') # The next assertion succeeds, but it doesn't mean anything for ICRS, as ICRS # isn't defined in terms of an equinox assert sc2.equinox == J2001 # But it *is* necessary once we transform to FK5 sc3 = sc2.transform_to('fk5') assert sc3.equinox == J2001 assert_allclose(sc1.ra, sc3.ra) # `SkyCoord` will also include the attribute-style access that is in the # v0.2/0.3 coordinate objects. This will *not* be in the low-level classes sc = coords.SkyCoord(ra=8 * u.hour, dec=5 * u.deg, frame='icrs') scgal = sc.galactic assert str(scgal).startswith('<SkyCoord (Galactic): (l, b)') # the existing `from_name` and `match_to_catalog_*` methods will be moved to the # high-level class as convenience functionality. # in remote-data test below! # m31icrs = coords.SkyCoord.from_name('M31', frame='icrs') # assert str(m31icrs) == '<SkyCoord (ICRS) RA=10.68471 deg, Dec=41.26875 deg>' if HAS_SCIPY: cat1 = coords.SkyCoord(ra=[1, 2] * u.hr, dec=[3, 4.01] * u.deg, distance=[5, 6] * u.kpc, frame='icrs') cat2 = coords.SkyCoord(ra=[1, 2, 2.01] * u.hr, dec=[3, 4, 5] * u.deg, distance=[5, 200, 6] * u.kpc, frame='icrs') idx1, sep2d1, dist3d1 = cat1.match_to_catalog_sky(cat2) idx2, sep2d2, dist3d2 = cat1.match_to_catalog_3d(cat2) assert np.any(idx1 != idx2)
def time_init_scalar(self): coords.FK5(self.scalar_ra, self.scalar_dec)
# The low-level classes have a dual role: they act as specifiers of coordinate # frames and they *may* also contain data as one of the representation objects, # in which case they are the actual coordinate objects themselves. # They can always accept a representation as a first argument icrs = coords.ICRS(coords.SphericalRepresentation(lon=8*u.hour, lat=5*u.deg)) # which is stored as the `data` attribute assert icrs.data.lat == 5*u.deg assert icrs.data.lon == 8*u.hour # Frames that require additional information like equinoxs or obstimes get them # as keyword parameters to the frame constructor. Where sensible, defaults are # used. E.g., FK5 is almost always J2000 equinox fk5 = coords.FK5(coords.SphericalRepresentation(lon=8*u.hour, lat=5*u.deg)) J2000 = astropy.time.Time('J2000',scale='utc') fk5_2000 = coords.FK5(coords.SphericalRepresentation(lon=8*u.hour, lat=5*u.deg), equinox=J2000) assert fk5.equinox == fk5_2000.equionx # the information required to specify the frame is immutable fk5.equinox = J2001 # raises AttributeError # As is the representation data. with raises(AttributeError): fk5.data = ... # There is also a class-level attribute that lists the attributes needed to # identify the frame. These include attributes like `equinox` shown above. assert FK5.frame_attr_names == ('equinox', 'obstime') # defined on the *class* assert fk5.frame_attr_names == ('equinox', 'obstime') # and hence also in the objects
def time_init_nodata(self): coords.FK5()
def _convertRaDecToDegrees(self, raStr, decStr): here = coords.FK5(ra=coords.Angle(raStr, unit='hourangle'), dec=coords.Angle(decStr, unit='deg')) return here.ra.deg, here.dec.deg
def time_init_array(self): coords.FK5(self.array_ra, self.array_dec)
def fitswcs_to_gwcs(hdr): """ Create and return a gWCS object from a FITS header. If it can't construct one, it should quietly return None. """ # Type of CoordinateFrame to construct for a FITS keyword frame_mapping = {'WAVE': cf.SpectralFrame} # coordinate names for CelestialFrame coordinate_outputs = {'alpha_C', 'delta_C'} # transform = gw.make_fitswcs_transform(hdr) try: transform = make_fitswcs_transform(hdr) except Exception as e: return None outputs = transform.outputs wcs_info = read_wcs_from_header(hdr) naxes = transform.n_inputs axes_names = ('x', 'y', 'z', 'u', 'v', 'w')[:naxes] in_frame = cf.CoordinateFrame(naxes=naxes, axes_type=['SPATIAL'] * naxes, axes_order=tuple(range(naxes)), name="pixels", axes_names=axes_names, unit=[u.pix] * naxes) out_frames = [] for i, output in enumerate(outputs): unit_name = wcs_info["CUNIT"][i] try: unit = u.Unit(unit_name) except TypeError: unit = None try: frame = frame_mapping[output[:4].upper()](axes_order=(i, ), unit=unit, axes_names=(output, ), name=output) except KeyError: if output in coordinate_outputs: continue frame = cf.CoordinateFrame(naxes=1, axes_type=("SPATIAL", ), axes_order=(i, ), unit=unit, axes_names=(output, ), name=output) out_frames.append(frame) if coordinate_outputs.issubset(outputs): frame_name = wcs_info["RADESYS"] # FK5, etc. axes_names = None try: ref_frame = getattr(coord, frame_name)() # TODO? Work out how to stuff EQUINOX and OBS-TIME into the frame except (AttributeError, TypeError): # TODO: Replace quick fix as gWCS doesn't recognize GAPPT if frame_name == "GAPPT": ref_frame = coord.FK5() else: ref_frame = None axes_names = ('lon', 'lat') axes_order = (outputs.index('alpha_C'), outputs.index('delta_C')) # Call it 'world' if there are no other axes, otherwise 'sky' name = 'SKY' if len(outputs) > 2 else 'world' cel_frame = cf.CelestialFrame(reference_frame=ref_frame, name=name, axes_names=axes_names, axes_order=axes_order) out_frames.append(cel_frame) out_frame = (out_frames[0] if len(out_frames) == 1 else cf.CompositeFrame( out_frames, name='world')) return gWCS([(in_frame, transform), (out_frame, None)])
ICRSCoordGenerator = lambda *args, **kwargs: coord.SkyCoord( *args, frame='icrs', **kwargs) GalacticCoordGenerator = lambda *args, **kwargs: coord.SkyCoord( *args, frame='galactic', **kwargs) FK5CoordGenerator = lambda *args, **kwargs: coord.SkyCoord( *args, frame='fk5', **kwargs) FK4CoordGenerator = lambda *args, **kwargs: coord.SkyCoord( *args, frame='fk4', **kwargs) ICRSCoord = coord.SkyCoord CoordClasses = (coord.SkyCoord, BaseCoordinateFrame) except ImportError: from astropy.coordinates import SphericalCoordinatesBase as BaseCoordinateFrame ICRSCoordGenerator = lambda *args, **kwargs: coord.ICRS(*args, **kwargs) GalacticCoordGenerator = lambda *args, **kwargs: coord.Galactic( *args, **kwargs) FK5CoordGenerator = lambda *args, **kwargs: coord.FK5(*args, **kwargs) FK4CoordGenerator = lambda *args, **kwargs: coord.FK4(*args, **kwargs) ICRSCoord = coord.ICRS CoordClasses = (coord.SphericalCoordinatesBase, ) from ..exceptions import TimeoutError from .. import version __all__ = [ 'send_request', 'parse_coordinates', 'parse_radius', 'TableList', 'suppress_vo_warnings', 'validate_email' ] def send_request(url, data,
def corrections(lon, lat, alt, ra, dec, mjd, bcv_shift=None): """ Calculate the heliocentric radial velocity corrections for an astronomical source. :param lon: Earth longitude of the observatory (western direction is positive). Can be anything that initialises an `~astropy.coordinates.Angle` object (if float, in degrees). :type lon: :class:`~astropy.coordinates.Longitude` or float :param lat: Earth latitude of observatory. Can be anything that initialises an `~astropy.coordinates.Latitude` object (if float, in degrees). :type lat: :class:`~astropy.coordinates.Latitude` or float :param alt: Altitude of the observatory (if float, in meters). :type alt: :class:`~astropy.units.Quantity` or float :param ra: Right ascension of the object for epoch J2000 (if float, in degrees). :type ra: :class:`~astropy.coordinates.Angle` or float :param dec: Declination of the object for epoch J2000 (if float, in degrees). :type dec: :class:`~astropy.coordinates.Angle` or float :param mjd: The modified Julian date for the middle of exposure. :type mjd: float :returns: A two-length tuple containing the barycentric velocity correction and the heliocentric velocity correction. Both velocity corrections are given as :class:`~astropy.units.Quantity` objects. """ if not isinstance(lon, coord.Longitude): lon = coord.Longitude(lon * u.deg) if not isinstance(lat, coord.Latitude): lat = coord.Latitude(lat * u.deg) if not isinstance(alt, u.Quantity): alt *= u.m if not isinstance(ra, u.Quantity): ra *= u.deg if not isinstance(dec, u.Quantity): dec *= u.deg # Here we specify the location so that we can easily calculate the mean # local siderial time later on time = Time(2.4e6 + mjd, format="jd", location=(lon, lat, alt)) epoch = time.datetime.year + time.datetime.month/12. \ + time.datetime.day/365. # Precess the coordinates to the current epoch coordinate = coord.SkyCoord(ra, dec, frame="fk5").transform_to( coord.FK5(equinox="J{}".format(epoch))) # Convert geodetic latitude into geocentric latitude to correct for rotation # of the Earth dlat = ((-11. * 60. + 32.743) * np.sin(2 * lat) + 1.1633 * np.sin(4 * lat) \ - 0.0026 * np.sin(6 * lat)) * u.degree geocentric_lat = lat + dlat / 3600. # Calculate distance of observer from Earth center r = alt + 6378160.0 * u.m * (0.998327073 \ + 0.001676438 * np.cos(2 * geocentric_lat) \ - 0.000003510 * np.cos(4 * geocentric_lat) \ + 0.000000008 * np.cos(6 * geocentric_lat)) # Calculate rotational velocity perpendicular to the radius vector # Note: 23.934469591229 is the siderial day in hours for 1986 v = 2 * np.pi * r / (23.934469591229 * 3600 * u.second) # Calculate vdiurnal velocity try: vdiurnal = v * np.cos(lat) * np.cos(coordinate.dec) \ * np.sin(coordinate.ra - time.sidereal_time("mean")) except: logging.exception("exception in calculating vdirunal velocity") # Try again with decreased precision. time.delta_ut1_utc = 0.0 vdiurnal = v * np.cos(lat) * np.cos(coordinate.dec) \ * np.sin(coordinate.ra - time.sidereal_time("mean")) logging.warn("Explicitly set delta_ut1_utc = 0") # Calculate baricentric and heliocentric velocities vh, vb = celestial_velocities(time) # Project along the line of sight projection = np.array([ np.cos(coordinate.dec) * np.cos(coordinate.ra), np.cos(coordinate.dec) * np.sin(coordinate.ra), np.sin(coordinate.dec) ]) vbar = (vb * projection).sum() vhel = (vh * projection).sum() # Using baricentric velocity for correction # --------------------------------------------------------------------- # E. Holmbeck put this if statement in if bcv_shift != None: vbar_correction = bcv_shift else: vbar_correction = vdiurnal + vbar # --------------------------------------------------------------------- vhel_correction = vdiurnal + vhel # [TODO] it may be useful to return other components of velocity or extra # information about the transforms (e.g., gmst, ut, lmst, dlat, lat, vbar, # vhel, etc) return (vbar_correction, vhel_correction)
def metadata(filename, header=fits.PrimaryHDU().header, clear=True): """ Function metadata populates a FITS header (creating a new one if necessary) with important information about an observation. Input: 1. filename string to be called with astropy.io.fits.open(filename) Optional Input: 1. header fits header object to append to (default = create new) Output: header fits header with additional data The main calculation in this routine is for the parallactic angle; other parameters are trivially computed or simply copied from the original FITS header. Entries that cannot be computed or found in the original header will be given values of NaN or 'unavailable'. Times and parallactic angles are computed for the mean time of exposure, i.e, halfway between the time of the first and last reads. """ if clear: header.clear() header.append(('comment', ''), end=True) header.append(('comment', '*' * 60), end=True) header.append( ('comment', '*' * 18 + ' Time and Pointing Data ' + '*' * 18), end=True) header.append(('comment', '*' * 60), end=True) header.append(('comment', ''), end=True) try: origname = re.sub('.*CRSA', '', re.sub('.fits', '', filename)) header.append(('origname', origname, 'Original file ID number'), end=True) except: pass #################################################################### # Attempt to get the mean time of the exposure. Try three things: # 1. The mean of mjd-str and mjd-end in the main header (HDU 0) # 2. mjd in the main header (HDU 0) # 3. The mean acquisition time in the headers of the individual # reads, computed as acqtime in HDU 1 plus 1.48s/2*nreads #################################################################### mjd_ok = True try: head = fits.open(filename)[0].header try: mean_mjd = 0.5 * (head['mjd-str'] + head['mjd-end']) except: try: mean_mjd = head['mjd'] + 1.48 * 0.5 * len( fits.open(filename)) / 86400 except: ######################################################## # Note: acqtime is unreliable--doesn't always update. ######################################################## #head1 = fits.open(filename)[1].header #mean_mjd = head1['acqtime'] - 2400000.5 #mean_mjd += 1.48*0.5*len(fits.open(filename))/86400 ######################################################## # This is pretty bad: use the checksum time of the # middle read as the time stamp of last resort. ######################################################## head1 = fits.open(filename)[len(fits.open(filename)) // 2].header t = head1.comments['checksum'].split()[-1] t = Time(t, format='isot') t.format = 'mjd' mean_mjd = float(str(t)) except: mjd_ok = False mean_mjd = np.nan utc_date = 'unavailable' utc_time = 'unavailable' pos_ok = True #################################################################### # Need RA and Dec to compute parallactic angle #################################################################### try: head = fits.open(filename)[0].header ra, dec = [head['ra'], head['dec']] except: #ra, dec = ['05:02:27.5438', '+07:27:39.265'] #ra, dec = ['04:37:36.182', '-02:28:25.87'] pos_ok = False if mjd_ok: ################################################################ # Subaru's coordinates in degrees ################################################################ lng, lat = [-155.4760187, 19.825504] subaru = (str(lng) + 'd', str(lat) + 'd') t = Time(mean_mjd, format='mjd', location=subaru) if pos_ok: ############################################################ # Precess from J2000 to the appropriate epoch ############################################################ c = coord.SkyCoord(ra=ra, dec=dec, unit=(u.hourangle, u.deg), frame='fk5') equinox = 'J%.5f' % (2000 + (mean_mjd - 51544.5) / 365.25) c = c.transform_to(coord.FK5(equinox=equinox)) ################################################################ # Compute hour angle to get parallactic angle ################################################################ ha = (t.sidereal_time('apparent') - c.ra).rad lat = lat * np.pi / 180 pa = -np.arctan2( -np.sin(ha), np.cos(c.dec.rad) * np.tan(lat) - np.sin(c.dec.rad) * np.cos(ha)) pa = float(pa % (2 * np.pi)) else: pa = np.nan t.format = 'isot' utc_date = str(t).split('T')[0] utc_time = str(t).split('T')[1] else: pa = np.nan if not np.isfinite(mean_mjd): mean_mjd = utc_date = utc_time = 'unavailable' header['mjd'] = (mean_mjd, 'Mean MJD of exposure') header['utc-date'] = (utc_date, 'UTC date of exposure') header['utc-time'] = (utc_time, 'Mean UTC time of exposure') #################################################################### # Attempt to fetch useful/important keywords from the original # file's FITS header #################################################################### header.append(_fetch('ra', filename, comment='RA of telescope pointing')) header.append(_fetch('dec', filename, comment='DEC of telescope pointing')) if np.isfinite(pa): header['parang'] = (pa * 180 / np.pi, 'Mean parallactic angle (degrees)') else: header['parang'] = ('unavailable', 'Mean parallactic angle (degrees)') header.append( _fetch('d_imrpap', filename, comment='Image rotator pupil position angle (degrees)')) header.append( _fetch('HIERARCH CHARIS.FILTER.NAME', filename, comment='CHARIS filter name', newkey='filtname')) header.append( _fetch('HIERARCH CHARIS.FILTER.SLOT', filename, comment='CHARIS filter slot', newkey='filtpos')) header.append( _fetch('HIERARCH CHARIS.SHUTTER', filename, comment='CHARIS shutter position', newkey='shutter')) return header
def interlace_combine_acs(root='jbhm19010', view=True, use_error=True, make_undistorted=False, pad=120, NGROW=0, ddx=0, ddy=0, growx=1, growy=1, auto_offsets=False, chip=1, filter='F814W', outroot='UDS-19', center=None, file_ext='flc'): """ Combine four dithered ACS exposures in an interlaced image, but use the same native ACS pixel grid since the dither offsets don't evenly sample the ACS pixel. This also simplifies the image distortions to following the native definitions. Input is a visit ID which will read the attached ASN table. FLT images should be destriped and CTE corrected and run through [Multi/Astro]Drizzle to flag CRs (DQ=4096). Provide `filter` and `outroot` keywords to rename the visit name to something more meaningful. Make separate images for each chip, note [SCI,1] is CCDCHIP=2, [SCI,2] is CCDCHIP=1. """ # from pyraf import iraf # #from iraf import iraf # from iraf import dither # import threedhst.prep_flt_files import unicorn.reduce as red chip_ext = {1: 2, 2: 1} ### Chip1 is SCI,2 and Chip2 is SCI,1 extensions if unicorn.hostname().startswith('uni'): view = False if view: import threedhst.dq ds9 = threedhst.dq.myDS9() # asn = threedhst.utils.ASNFile(root + '_asn.fits') flt = pyfits.open(asn.exposures[0] + '_%s.fits' % (file_ext)) ### Works for WFC3/G280 if flt[0].header['INSTRUME'] == 'WFC3': NX, NY = 4096, 2051 im = pyfits.open(os.getenv('iref') + 'UVIS%dwfc3_map.fits' % (chip)) PAM = im[1].data im.close() else: NX, NY = 4096, 2048 im = pyfits.open(os.getenv('jref') + 'wfc%d_pam.fits' % (chip)) PAM = im[0].data im.close() #PAM = flt[1].data*0.+1 if os.path.exists(root + '.run'): run = threedhst.prep_flt_files.MultidrizzleRun(root) #run.blot_back(ii=0, copy_new=True) scl = np.float(run.scl) xsh, ysh = threedhst.utils.xyrot( np.array(run.xsh) * scl, np.array(run.ysh) * scl, run.rot[0]) else: xsh, ysh = np.zeros(len(asn.exposures)), np.zeros(len(asn.exposures)) yi, xi = np.indices((NY, NX)) #pad += NGROW*4 N = np.zeros((NY * growy + pad + growy * 2 * NGROW, NX * growx + pad + growx * 2 * NGROW), dtype=np.int) inter_sci = np.zeros((NY * growy + pad + growy * 2 * NGROW, NX * growx + pad + growx * 2 * NGROW)) inter_weight = np.zeros((NY * growy + pad + growy * 2 * NGROW, NX * growx + pad + growx * 2 * NGROW)) # if use_error: # inter_err = np.zeros((NY*growy+pad+growy*2*NGROW, NX*growx+pad+growx*2*NGROW)) xi += pad / (2 * growx) + NGROW yi += pad / (2 * growy) + NGROW #### Interlaced offsets dxs, dys = unicorn.reduce.acs_interlace_offsets(root + '_asn.fits', growx=growx, growy=growy, path_to_flt='./', chip=chip) dxs, dys = np.append(dxs, dxs), np.append(dys, dys) if center is not None: if not isinstance(center, list): center_coords = [ flt['SCI', chip_ext[chip]].header['CRVAL1'], flt['SCI', chip_ext[chip]].header['CRVAL2'] ] else: center_coords = center from drizzlepac import skytopix import astropy.coordinates as co import astropy.units as u ext = [None, 4, 1][chip] xpix, ypix = np.zeros(len(asn.exposures)), np.zeros(len(asn.exposures)) for i, exp in enumerate(asn.exposures): rd = co.FK5(ra=center_coords[0], dec=center_coords[1], unit=(u.deg, u.deg)) rdst = str(rd.to_string()).replace('s', '') for r in 'hdm': rdst = rdst.replace(r, ':') # xpix[i], ypix[i] = skytopix.rd2xy('%s_%s.fits[sci,%d]' % (exp, file_ext, chip_ext[chip]), rdst.split()[0], rdst.split()[1], verbose=False) # dxs = -np.cast[int](np.round(xpix - xpix[0])) dys = -np.cast[int](np.round(ypix - ypix[0])) dxs += ddx dys += ddy print 'Offsets:', dxs, dys, '\n' #### Find hot pixels, which are flagged as cosmic #### rays at the same location in each of the flt files. Ignore others. hot_pix = np.zeros((NY, NX), dtype='int') for flt in asn.exposures: im = pyfits.open(flt + '_%s.fits' % (file_ext)) hot_pix += (im['DQ', chip_ext[chip]].data & 4096) / 4096 hot_pix = hot_pix > (len(asn.exposures) - 2) for i, flt in enumerate(asn.exposures): print flt #flt = run.flt[i] im = pyfits.open(flt + '_%s.fits' % (file_ext)) #ds9.frame(i+1); ds9.view(im[1].data); ds9.scale(0,5) #### Use the pixel area map correction im['SCI', chip_ext[chip]].data *= PAM #### Divide by 4 to conserve surface brightness with smaller output pixels im['SCI', chip_ext[chip]].data /= 1. * (growx * growy) im['ERR', chip_ext[chip]].data /= 1. * (growx * growy) ### Mask cosmic rays if i == 0: h0 = im[0].header h1 = im['SCI', chip_ext[chip]].header #header = red.scale_header_wcs(h1.copy(), factor=2, growx=growx, growy=growy, pad=pad) header = h1.copy() header_wht = header.copy() if pyfits.__version__ < '3.3': header.update('EXTNAME', 'SCI') #header.update('PAD',pad) header.update('REFIMAGE', '', comment='Source detection image') header_wht.update('EXTNAME', 'ERR') else: header['EXTNAME'] = 'SCI' #header.update('PAD',pad) header['REFIMAGE'] = ('', 'Source detection image') header_wht['EXTNAME'] = 'ERR' dx = np.int(np.round((xsh[i] - xsh[0]) * growx)) dy = np.int(np.round((ysh[i] - ysh[0]) * growy)) # dx = dxs[i] dy = dys[i] # #use = ((im[3].data & 4096) == 0) & ((im[3].data & 4) == 0) #& (xi > np.abs(dx/2)) & (xi < (1014-np.abs(dx/2))) & (yi > np.abs(dy/2)) & (yi < (1014-np.abs(dy/2))) # use = ((im['DQ', chip_ext[chip]].data & (4 + 32 + 16 + 128 + 1024 + 2048 + 4096)) == 0) & (~hot_pix) #### preparation step doesn't subtract background im['SCI', chip_ext[chip]].data -= np.median(im['SCI', chip_ext[chip]].data[use]) im['SCI', chip_ext[chip]].data /= im[0].header['EXPTIME'] im['ERR', chip_ext[chip]].data /= im[0].header['EXPTIME'] #inter_sci[yi[use]*growy+dy,xi[use]*growx+dx] += im['SCI',chip_ext[chip]].data[use] inter_sci[yi[use] * growy + dy, xi[use] * growx + dx] += im['SCI', chip_ext[chip]].data[use] / im[ 'ERR', chip_ext[chip]].data[use]**2 inter_weight[yi[use] * growy + dy, xi[use] * growx + dx] += 1. / im['ERR', chip_ext[chip]].data[use]**2 N[yi[use] * growy + dy, xi[use] * growx + dx] += 1 # if use_error: # inter_err[yi[use]*growy+dy,xi[use]*growx+dx] += im['ERR',chip_ext[chip]].data[use]**2 if view: ds9.view_array(inter_sci / np.maximum(N, 1), header=header) ds9.scale(-0.1, 5) #### Average for case when dither positions overlap, e.g. CANDELS SN fields # inter_sci /= np.maximum(N,1) # inter_err = np.sqrt(inter_err) / np.maximum(N, 1) # inter_err[N == 0] = 0. inter_weight[inter_weight == 0] = 1 inter_sci = inter_sci / inter_weight inter_err = np.sqrt(1. / inter_weight) inter_err[N == 0] = 0. if use_error: h0.update('WHTERROR', True, comment='WHT extension is FLT[err,1]') else: h0.update('WHTERROR', False, comment='WHT extension is 0/1 flagged bad pix') ### Clip back to native size # inter_sci = inter_sci[pad/2:-pad/2, pad/2:-pad/2] # inter_err = inter_err[pad/2:-pad/2, pad/2:-pad/2] # N = N[pad/2:-pad/2, pad/2:-pad/2] header['PAD'] = pad header['GROWX'] = growx header['GROWY'] = growy header['NGROW'] = NGROW header['CRPIX1'] += pad / 2. header['CRPIX2'] += pad / 2. #print pad, inter_sci.shape hdu = pyfits.PrimaryHDU(header=h0) sci = pyfits.ImageHDU(data=np.cast[np.float32](inter_sci), header=header) if use_error: wht = pyfits.ImageHDU(data=np.cast[np.float32](inter_err), header=header_wht) else: wht = pyfits.ImageHDU(data=np.cast[np.int](N), header=header_wht) image = pyfits.HDUList([hdu, sci, wht]) if 'EXTEND' not in hdu.header.keys(): hdu.header.update('EXTEND', True, after='NAXIS') image[0].header.update('FILTER', filter) if outroot is None: outroot = root image.writeto('%s-chip%d-%s_inter.fits' % (outroot, chip, filter), clobber=True) if 0: flt = pyfits.open('jbhj07yfq_flt.fits') inter = pyfits.open('GOODS-S-7-chip2-F814W_inter.fits') ds9.view(flt[1].data / flt[0].header['EXPTIME'] - inter[1].data[pad:-pad, pad:-pad])