Ejemplo n.º 1
0
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
Ejemplo n.º 2
0
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)
Ejemplo n.º 4
0
    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))
Ejemplo n.º 5
0
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
Ejemplo n.º 6
0
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)
Ejemplo n.º 7
0
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)
Ejemplo n.º 9
0
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
Ejemplo n.º 10
0
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
Ejemplo n.º 11
0
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
Ejemplo n.º 12
0
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, ))])
Ejemplo n.º 13
0
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
Ejemplo n.º 14
0
    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'))
Ejemplo n.º 15
0
#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:, ]
Ejemplo n.º 16
0
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
Ejemplo n.º 17
0
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)
Ejemplo n.º 18
0
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+')
Ejemplo n.º 19
0
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)
Ejemplo n.º 20
0
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)
Ejemplo n.º 21
0
 def time_init_scalar(self):
     coords.FK5(self.scalar_ra, self.scalar_dec)
Ejemplo n.º 22
0
# 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
Ejemplo n.º 23
0
 def time_init_nodata(self):
     coords.FK5()
Ejemplo n.º 24
0
 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
Ejemplo n.º 25
0
 def time_init_array(self):
     coords.FK5(self.array_ra, self.array_dec)
Ejemplo n.º 26
0
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)])
Ejemplo n.º 27
0
    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,
Ejemplo n.º 28
0
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)
Ejemplo n.º 29
0
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
Ejemplo n.º 30
0
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])