コード例 #1
0
def plot_targets_and_imacs_fov(host,
                               camera='short',
                               offset=(0, 0),
                               clf=True,
                               **kwargs):
    """
    Plots the targets for the requested host and the IMACS FOV on top

    Parameters
    ----------
    host : NSAHost
        The host to
    camera : str
        The camera to make the FOV box for: 'short' (f/2) or 'long' (f/4)
    offset : 2-tuple
        RA/Dec offset from the host in arcmin

    kwargs are passed into `select_targets`
    """
    import targeting

    if camera == 'short':
        fov = 27.20
        fovrad = 36.4
    elif camera == 'long':
        fov = 15.46
        fovrad = None
    else:
        raise ValueError('unrecognized camera "{0}"'.format(camera))

    targets = targeting.select_targets(host, **kwargs)
    dra = (targets['ra'] - host.ra) * 60 * np.cos(np.radians(targets['dec']))
    ddec = (targets['dec'] - host.dec) * 60

    if clf:
        plt.clf()
    plt.plot(dra, ddec, '.', ms=1)

    xcorners = [offset[0] - fov / 2, offset[0] + fov / 2]
    xcorners = [
        xcorners[0], xcorners[1], xcorners[1], xcorners[0], xcorners[0]
    ]
    ycorners = [offset[1] - fov / 2, offset[1] + fov / 2]
    ycorners = [
        ycorners[1], ycorners[1], ycorners[0], ycorners[0], ycorners[1]
    ]

    plt.plot(xcorners, ycorners, 'k-')
コード例 #2
0
def count_targets(hsts,
                  verbose=True,
                  remove_cached=True,
                  rvir=300,
                  targetingkwargs={}):
    """
    Generates a count of targets for each field.

    Parameters
    ----------
    hsts
        A list of `NSAHost` objects
    verbose : bool
        Whether or not to print a message when each host is examined
    remove_cached : bool
        Whether or not to remove the cached sdss catalog for each host
        after counting. This may be necessary to prevent running out of
        memory, depending on the number of hosts involved.
    rvir : float
        "virial radius" in kpc for the arcmin transform
    targetingkwargs : dict or list of dicts
        passed into  ` targeting.select_targets` if a single dictionary, otherwise
        the targeting will

    Returns
    -------
    ntargs : astropy.Table
        a table object with the names of the hosts and the target counts.

    """
    import sys
    import collections

    from astropy import table

    if isinstance(targetingkwargs, collections.Mapping):
        colnames = ['ntarg']
        targetingkwargs = [targetingkwargs.copy()]
    else:
        colnames = [('ntarg_' + t.get('colname', str(i)))
                    for i, t in enumerate(targetingkwargs)]
        targetingkwargs = [t.copy() for t in targetingkwargs]

    for t in targetingkwargs:
        t.setdefault('outercutrad', 300)
        t.setdefault('removegama', False)
        if 'colname' in t:
            del t['colname']

    nms = []
    dists = []
    rvs = []
    cnts = [[] for t in targetingkwargs]

    for i, h in enumerate(hsts):
        if verbose:
            print 'Generating target count for', h.name, '#', i + 1, 'of', len(
                hsts)
            sys.stdout.flush()
        nms.append(h.name)
        dists.append(h.distmpc)
        rvs.append(h.physical_to_projected(300))

        for j, t in enumerate(targetingkwargs):
            if verbose:
                print 'Targeting parameters:', t
                sys.stdout.flush()

            tcat = targeting.select_targets(h, **t)
            cnts[j].append(len(tcat))

        if remove_cached:
            h._cached_sdss = None

    t = table.Table()
    t.add_column(table.Column(name='name', data=nms))
    t.add_column(table.Column(name='distmpc', data=dists, units='Mpc'))
    t.add_column(table.Column(name='rvirarcmin', data=rvs, units='arcmin'))

    for cnm, cnt in zip(colnames, cnts):
        t.add_column(table.Column(name=cnm, data=cnt))

    return t
コード例 #3
0
ファイル: bossanova.py プロジェクト: saga-survey/erik
def count_targets(hsts, verbose=True, remove_cached=True, rvir=300, targetingkwargs={}):
    """
    Generates a count of targets for each field.

    Parameters
    ----------
    hsts
        A list of `NSAHost` objects
    verbose : bool
        Whether or not to print a message when each host is examined
    remove_cached : bool
        Whether or not to remove the cached sdss catalog for each host
        after counting. This may be necessary to prevent running out of
        memory, depending on the number of hosts involved.
    rvir : float
        "virial radius" in kpc for the arcmin transform
    targetingkwargs : dict or list of dicts
        passed into  ` targeting.select_targets` if a single dictionary, otherwise
        the targeting will

    Returns
    -------
    ntargs : astropy.Table
        a table object with the names of the hosts and the target counts.

    """
    import sys
    import collections

    from astropy import table

    if isinstance(targetingkwargs, collections.Mapping):
        colnames = ['ntarg']
        targetingkwargs = [targetingkwargs.copy()]
    else:
        colnames = [('ntarg_' + t.get('colname', str(i))) for i, t in enumerate(targetingkwargs)]
        targetingkwargs = [t.copy() for t in targetingkwargs]

    for t in targetingkwargs:
        t.setdefault('outercutrad', 300)
        t.setdefault('removegama', False)
        if 'colname' in t:
            del t['colname']

    nms = []
    dists = []
    rvs = []
    cnts = [[] for t in targetingkwargs]

    for i, h in enumerate(hsts):
        if verbose:
            print 'Generating target count for', h.name, '#', i + 1, 'of', len(hsts)
            sys.stdout.flush()
        nms.append(h.name)
        dists.append(h.distmpc)
        rvs.append(h.physical_to_projected(300))

        for j, t in enumerate(targetingkwargs):
            if verbose:
                print 'Targeting parameters:', t
                sys.stdout.flush()

            tcat = targeting.select_targets(h, **t)
            cnts[j].append(len(tcat))

        if remove_cached:
            h._cached_sdss = None

    t = table.Table()
    t.add_column(table.Column(name='name', data=nms))
    t.add_column(table.Column(name='distmpc', data=dists, units='Mpc'))
    t.add_column(table.Column(name='rvirarcmin', data=rvs, units='arcmin'))

    for cnm, cnt in zip(colnames, cnts):
        t.add_column(table.Column(name=cnm, data=cnt))

    return t
コード例 #4
0
def make_catalog(host, fnout=None, targetfaintlim=(21.,22.), targetoutercutrad=300,
                 fluxrng=(17., 17.7), repeatflux=1, guidestarmagrng= (14, 15),
                 removegalsathighz=False, inclspecqsos=True, removespecstars=False,
                 fibermag_faintlimit=(22., 23.), fibermag_brightlimit=17,
                 fibermag_type='fibermag_r', useoutertargets=False):
    """
    Generates a catalog for the Hectospec "XFITFIBS" program.

    `targetfaintlim` can be just a mag to make everything "primary", or
    a 2-tuple that is (prisecboundary, faintestsec)

    `useoutertargets` means include targets beyond `targetoutercutrad`, but with
    a rank below where they would usually be

    Writes catalog to `fnout` if `fnout` is not None

    Returns the catalog as a `Table`

    `fibermag_faintlimit` is a lower limit *or* a two-tuple of limits for
    primary and secondary targets (secondary ignored if `targetfainlim` is just
    a single number) can also be None (`fibermag_brightlimit` can also be None)
    """
    fluxrank = 1
    hostrank = 2
    prisatrank = 3
    secsatrank = 5 if useoutertargets else 4 #the outer primaries get 4 if `useoutertargets` is True


    #add the host as the first entry
    rastr = Angle(host.ra, 'deg').to_string('hr', sep=':', precision=3)
    decstr = Angle(host.dec, 'deg').to_string('deg', sep=':', precision=3)
    mag = '{0:.2f}'.format(host.r + host.distmod)

    tabentries.append([rastr, decstr, host.name, str(hostrank), 'TARGET', mag])
    catlines.append('\t'.join(tabentries[-1]))

    try:
        prisecbounday, seclimit = targetfaintlim
    except TypeError:
        prisecbounday = seclimit = targetfaintlim


    if useoutertargets:
        targs = targeting.select_targets(host, faintlimit=seclimit, outercutrad=None, innercutrad=20, removegalsathighz=removegalsathighz, removespecstars=removespecstars, inclspecqsos=inclspecqsos)

        if targetoutercutrad < 0:  # arcmin
            outercutraddeg = -targetoutercutrad / 60.
        else:  # kpc
            outercutraddeg = np.degrees(targetoutercutrad / (1000 * host.distmpc))
        outermsk = targs['rhost'] > outercutraddeg #just used for counting purposes
    else:
        targs = targeting.select_targets(host, faintlimit=seclimit, outercutrad=targetoutercutrad, removegalsathighz=removegalsathighz, removespecstars=removespecstars, inclspecqsos=inclspecqsos)
        outermsk = None

    print('Found', len(targs), 'targets')
    if outermsk is not None:
        print((~outermsk).sum(), 'are in the inner zone')
    if prisecbounday != seclimit:
        print(np.sum(targs['r'] < prisecbounday), 'Primaries and',np.sum((targs['r'] > prisecbounday) & (targs['r'] < seclimit)),'Secondaries')
        if outermsk is not None:
            inrtargs = targs[~outermsk]
            print(np.sum(inrtargs['r'] < prisecbounday), 'Primaries and',np.sum((inrtargs['r'] > prisecbounday) & (inrtargs['r'] < seclimit)),'Secondaries are in the inner zone')



    if fibermag_faintlimit is not None:
        try:
            prifiberlimit, secfiberlimit = fibermag_faintlimit
        except TypeError:
            prifiberlimit = secfiberlimit = fibermag_faintlimit

        fibmag = targs[fibermag_type]

        #filter things that have faint fiber mags in the primary targets
        primsk = (targs['r'] < prisecbounday) & (fibmag < prifiberlimit)
        #filter things that have faint fiber mags in the secondart targets
        secmsk = (targs['r'] > prisecbounday) & (targs['r'] < seclimit) & (fibmag < secfiberlimit)

        print('Accepting', primsk.sum(), 'primary targets and', secmsk.sum(), 'secondaries according to fiber faint limit')
        targs = targs[primsk | secmsk]

    if fibermag_brightlimit is not None:
        msk = targs[fibermag_type] > fibermag_brightlimit
        targs = targs[msk]
        print('Filtering', msk.sum(), ' targets due to fiber bright limit')

    targranks = []
    for t in targs:
        targranks.append(prisatrank if t['r'] < prisecbounday else secsatrank)
        if t['rhost'] > outercutraddeg:
            targranks[-1] += 1
    targranks = np.array(targranks)

    return generate_catalog(host, targs, targranks, fluxrng=fluxrng,
                            repeatflux=repeatflux, fluxrank=fluxrank,
                            guidestarmagrng=guidestarmagrng, fnout=fnout)
コード例 #5
0
ファイル: nplots.py プロジェクト: saga-survey/erik
for h in hs.values():
    print 'on',h.name
    sys.stdout.flush()
    ds[h.name] = h.distmpc
    h.fnsdss = 'saga_catalogs/'+h.name+'.dat'
    #tcat=targeting.select_targets(h,outercutrad=300)
    #cnts[h.name] = len(tcat)
h
cnts={}
ds={}
for h in hs.values():
    print 'on',h.name
    sys.stdout.flush()
    ds[h.name] = h.distmpc
    h.fnsdss = 'saga_catalogs/'+h.name+'.dat'
    tcat=targeting.select_targets(h,outercutrad=300)
    cnts[h.name] = len(tcat)
h0=hs.values()[0]
h0._cached_sdss
h0._cached_sdss;
cnts={}
ds={}
for h in hs.values():
    print 'on',h.name
    sys.stdout.flush()
    ds[h.name] = h.distmpc
    h.fnsdss = 'saga_catalogs/'+h.name+'.dat'
    tcat=targeting.select_targets(h,outercutrad=300)
    cnts[h.name] = len(tcat)
    h._cached_sdss = None
cnts={}
コード例 #6
0
def construct_master_catalog(host,
                             fnout=None,
                             targetcat={},
                             fopcat=None,
                             skyradec=None,
                             faintlimit=None,
                             fibermaglimit=None,
                             orderby=None,
                             usnosdssoffsettol=0.5):
    """
    This function produces the "master" catalog for each host for WIYN/hydra
    observations. The master catalog contains lines for all the objects/sky/fops
    and potential targets.  It is laid out such that the rows can simply be
    extracted as-is for .ast files for input to WIYN's `whydra` program.

    Parameters
    ----------
    host : NSAHost
        the host object to use to construct this catalog.  Note that SDSS and
        USNO data must be already downloaded for this host.
    fnout : str or None
        The filename to save as the master catalog or None to use a
        default based on the host's name
    targetcat : str or dict
        The catalog of targets or a dict to use `targeting.select_targets`
        to generate one (the dict is used as the kwargs).
    fopcat : str or None
        The catalog of FOP stars or None to use the default options to
        generate one.
    skyradec : 2xN array or None
        (ra, dec) for locations of sky fibers or if None, will use
        `select_sky_positions` to find good sky fiber locations.
    faintlimit : float or None
        The faint-end cutoff for r-band magnitudes in the target catalog,
        or None to have no cutoff
    fibermaglimit : float or None
        The faint-end cutoff for fiber2mag_r in the target catalog,
        or None to have no cutoff
    orderby: str or list of str
        Order the target list in order by field(2) in the target catalog.  If
        prefixed with '-', it's in *decreasing* order, otherwise increasing
        (i.e., without '-', smallest number first).  The special 'lowphotz'
        means to change order to blocks of [photz<.1, nophotz, photz>.1]
    usnosdssoffsettol : float
        The number of arcseconds median offset acceptable between the SDSS and
        USNO-B frames

    Returns
    -------
    targetcat
        The catalog of targets used to generate the object list
    """
    import os
    from collections import Mapping
    from targeting import usno_vs_sdss_offset, select_targets

    if fnout is None:
        fnout = os.path.join('hydra_targets', host.name + '.cat')

    if isinstance(targetcat, Mapping):
        if faintlimit is not None:
            if 'faintlimit' in targetcat:
                raise ValueError(
                    'cannot give both faintlimit kwarg and in targetcat dict')
            targetcat['faintlimit'] = faintlimit
        targetcat = select_targets(host, **targetcat)
    elif faintlimit is not None:
        #do the faintlimit cut manually if not using `select_targets`
        targetcat = targetcat[targetcat['r'] < faintlimit]

    if fibermaglimit is not None:
        fmsk = targetcat['fiber2mag_r'] < fibermaglimit
        print('Fiber mag cut removes', np.sum(~fmsk), 'of', len(fmsk),
              'objects')
        targetcat = targetcat[fmsk]

    print(len(targetcat), 'objects')
    if fopcat is None:
        fopcat = select_fops(host)
    print(len(fopcat), 'FOPS')
    if skyradec is None:
        skyradec = select_sky_positions(host)

    if orderby:
        if isinstance(orderby, six.string_types):
            orderby = [orderby]

        for field in orderby:
            if field == 'lowphotz':
                pz = targetcat['photz']
                highz = np.where(pz > .1)[0]
                lowz = np.where((-1 < pz) & (pz < .1))[0]
                noz = np.where(pz == -1)[0]
                print('Found {0} objects at low photz, {1} at high, and {2} '
                      'without photz'.format(len(lowz), len(highz), len(noz)))
                sorti = np.concatenate([lowz, noz, highz])
            elif field.startswith('-'):
                sorti = np.argsort(targetcat[field[1:]])[::-1]
            else:
                sorti = np.argsort(targetcat[field])
            targetcat = targetcat[sorti]

    if len(targetcat) > 1999:  # 1999 because the central object also gets one
        print('whydra cannot handle > 2000 objects, truncating')
        targetcat = targetcat[:1999]
    if len(fopcat) > 2000:
        print('whydra cannot handle > 2000 FOPS, truncating')
        fopcat = fopcat[:2000]
    if len(skyradec[0]) > 2000:
        print('whydra cannot handle > 2000 sky points, truncating')
        skyradec = skyradec[0][:2000], skyradec[1][:2000]

    #determine the SDSS vs. USNO offset
    dra, ddec = usno_vs_sdss_offset(host.get_sdss_catalog(),
                                    host.get_usnob_catalog(),
                                    raiseerror=usnosdssoffsettol)
    print('USNO/SDSS offsets:', dra * 3600, ddec * 3600)

    if os.path.exists(fnout):
        raise ValueError(
            'File for generating master catalog {0} already exists!'.format(
                fnout))

    print('Constucting catalog in', fnout)
    with open(fnout, 'w') as fw:
        #first add host as center, and as object
        fw.write(
            _whydra_file_line(0001, 'Center'.format(host.nsaid), host.ra,
                              host.dec, 'C'))
        fw.write('\n')

        fw.write(
            _whydra_file_line(1000, 'NSA{0}'.format(host.nsaid), host.ra,
                              host.dec, 'O'))
        fw.write('\n')

        i = 2000
        for obj in targetcat:
            ln = _whydra_file_line(i, 'SDSS', obj['ra'], obj['dec'], 'O')
            i += 1
            fw.write(ln)
            fw.write(' # mag_r={0:.2f}'.format(obj['r']))
            fw.write('\n')

        i = 5000
        for fop in fopcat:
            ln = _whydra_file_line(i, 'USNO' + fop['id'], fop['RA'] - dra,
                                   fop['DEC'] - ddec, 'F')
            fw.write(ln)
            fw.write(' # mag_R2={0:.2f}'.format(fop['R2']))
            i += 1
            fw.write('\n')

        i = 8000
        j = 1
        for skyra, skydec in zip(*skyradec):
            ln = _whydra_file_line(i, 'sky{0}'.format(j), skyra, skydec, 'S')
            fw.write(ln)
            i += 1
            j += 1
            fw.write('\n')

    return targetcat
コード例 #7
0
for h in hs.values():
    print 'on', h.name
    sys.stdout.flush()
    ds[h.name] = h.distmpc
    h.fnsdss = 'saga_catalogs/' + h.name + '.dat'
    #tcat=targeting.select_targets(h,outercutrad=300)
    #cnts[h.name] = len(tcat)
h
cnts = {}
ds = {}
for h in hs.values():
    print 'on', h.name
    sys.stdout.flush()
    ds[h.name] = h.distmpc
    h.fnsdss = 'saga_catalogs/' + h.name + '.dat'
    tcat = targeting.select_targets(h, outercutrad=300)
    cnts[h.name] = len(tcat)
h0 = hs.values()[0]
h0._cached_sdss
h0._cached_sdss
cnts = {}
ds = {}
for h in hs.values():
    print 'on', h.name
    sys.stdout.flush()
    ds[h.name] = h.distmpc
    h.fnsdss = 'saga_catalogs/' + h.name + '.dat'
    tcat = targeting.select_targets(h, outercutrad=300)
    cnts[h.name] = len(tcat)
    h._cached_sdss = None
cnts = {}
コード例 #8
0
def build_imacs_targeting_files(host,
                                observername,
                                date=None,
                                onlygals=True,
                                refmagrange={'r': (17, 19)},
                                overwrite=False,
                                selectkws={},
                                targs=None,
                                pdecide=None,
                                weakdlimit=False,
                                inclhost=True):
    """
    Generates the target catalog and initial observation file for IMACS

    Parameters
    ----------
    host : NSAHost
        The host to target
    observername : str
        The name of the observer (for the .obs file)
    date : str
        The data of the observation - should be parsable by `astropy.time.Time`
    onlygals : bool
        If True, only generate targets that SDSS calls galaxies
    refmagrange: dict of 2-tuples.
        Magnitude range for reference stars - dict keys are band.
        http://www.lco.cl/telescopes-information/magellan/instruments/imacs/user-manual/the-imacs-user-manual#prepobs
        suggests 17 < R< 19.
    overwrite : bool
        If True, existing files will be overwritten.  Otherwise, an error will
        be raised if the files are present.
    selectkws : dict
        Keywords to be passed into `targeting.select_targets`
    targs : astropy.table.Table
        The target list to use.  If given, will override `selectkws` and
        `targeting.select_targets` will be ignored.  Format should be the same
        as `targeting.select_targets`'s output - needs to have 'objID, 'type',
        'ra', 'dec', and 'r'.  If the column 'imacs_pri' is present, that
        will be included as a priority for the object
    pdecide : float or None
        The priority to use as 'PDECIDE', or None to go with the default
    weakdlimit : bool
        If True, set the 'DLIMIT' such that only the center wl (6750) is forced
        to be on the detector.
    inclhost : bool
        If True, always add a (priority 0) target for the host.
    """
    import os
    import targeting
    from astropy.coordinates import Angle
    from astropy.time import Time
    from astropy import units as u

    if targs is None:
        targs = targeting.select_targets(host, **selectkws)
    elif selectkws:
        raise ValueError('Cannot give both `targs` and `selectkws` arguments')

    cat = host.get_sdss_catalog()  # needed for ref stars

    if onlygals:
        if 'type' in targs.colnames:
            galmsk = targs['type'] == 3
            targs = targs[galmsk]
        else:
            print(
                'onlygals was set, but no "type" in the catalog.  Not cutting.'
            )

    refmsk = np.ones(len(cat), dtype=bool)
    for band in 'gri':
        refmsk & (np.abs(cat[band] - cat['psf_' + band]) < 0.25)
    for band, rng in refmagrange.items():
        refmsk = refmsk & (min(rng) < cat['psf_' + band]) & (cat['psf_' + band]
                                                             < max(rng))

    fncat = 'imacs_targets/{0}.cat'.format(host.shortname)
    fnobs = 'imacs_targets/{0}_ini.obs'.format(host.shortname)

    if not overwrite:
        if os.path.exists(fncat):
            if os.path.exists(fnobs):
                raise IOError(
                    'Object catalog and obs file ("{0}" and "{1}") already exist!'
                    .format(fncat, fnobs))
            else:
                raise IOError(
                    'Object catalog ({0}) already exists!'.format(fncat))
        elif os.path.exists(fnobs):
            raise IOError(
                'Observation file ({0}) already exists!'.format(fnobs))

    with open(fncat, 'w') as f:
        f.write('&RADEGREE\n')

        if inclhost:
            f.write('@{0} {1} {2} -10 Pri=0\n'.format(host.name, host.ra,
                                                      host.dec))

        if 'imacs_pri' in targs.colnames:
            for t in targs:
                f.write('@{0} {1} {2} {3} Pri={4}\n'.format(
                    *[t[n] for n in 'objID,ra,dec,r,imacs_pri'.split(',')]))
        else:
            for t in targs:
                f.write('@{0} {1} {2} {3}\n'.format(
                    *[t[n] for n in 'objID,ra,dec,r'.split(',')]))

        #now the reference stars
        for t in cat[refmsk]:
            f.write('*{0} {1} {2} #r={3}\n'.format(
                *[t[n] for n in 'objID,ra,dec,r'.split(',')]))
    print('Wrote catalog to', fncat)

    hra = Angle(host.ra, u.degree)
    hdec = Angle(host.dec, u.degree)

    obsmjd = int(Time(date, scale='utc').mjd)

    obsfile = _obsfile_template.format(
        title=host.shortname + '_ini',
        cenra=hra.to_string(u.hour, precision=3, sep=':', pad=True),
        cendec=hdec.to_string(u.degree, precision=2, sep=':', pad=True),
        observer=observername,
        obsmjd=obsmjd,
        catfile=host.shortname + '.cat',
        pdecide='' if pdecide is None else
        ('\nPDECIDE {0:.2f}'.format(pdecide)),
        dlimit='\nDLIMIT 5000 7500' if weakdlimit else '')

    with open(fnobs, 'w') as f:
        f.write(obsfile)
    print('Wrote obs file to', fnobs)
コード例 #9
0
ファイル: wiyn.py プロジェクト: saga-survey/erik
def construct_master_catalog(host, fnout=None, targetcat={}, fopcat=None,
    skyradec=None, faintlimit=None, fibermaglimit=None, orderby=None,
    usnosdssoffsettol=0.5):
    """
    This function produces the "master" catalog for each host for WIYN/hydra
    observations. The master catalog contains lines for all the objects/sky/fops
    and potential targets.  It is laid out such that the rows can simply be
    extracted as-is for .ast files for input to WIYN's `whydra` program.

    Parameters
    ----------
    host : NSAHost
        the host object to use to construct this catalog.  Note that SDSS and
        USNO data must be already downloaded for this host.
    fnout : str or None
        The filename to save as the master catalog or None to use a
        default based on the host's name
    targetcat : str or dict
        The catalog of targets or a dict to use `targeting.select_targets`
        to generate one (the dict is used as the kwargs).
    fopcat : str or None
        The catalog of FOP stars or None to use the default options to
        generate one.
    skyradec : 2xN array or None
        (ra, dec) for locations of sky fibers or if None, will use
        `select_sky_positions` to find good sky fiber locations.
    faintlimit : float or None
        The faint-end cutoff for r-band magnitudes in the target catalog,
        or None to have no cutoff
    fibermaglimit : float or None
        The faint-end cutoff for fiber2mag_r in the target catalog,
        or None to have no cutoff
    orderby: str or list of str
        Order the target list in order by field(2) in the target catalog.  If
        prefixed with '-', it's in *decreasing* order, otherwise increasing
        (i.e., without '-', smallest number first).  The special 'lowphotz'
        means to change order to blocks of [photz<.1, nophotz, photz>.1]
    usnosdssoffsettol : float
        The number of arcseconds median offset acceptable between the SDSS and
        USNO-B frames

    Returns
    -------
    targetcat
        The catalog of targets used to generate the object list
    """
    import os
    from collections import Mapping
    from targeting import usno_vs_sdss_offset, select_targets

    if fnout is None:
        fnout = os.path.join('hydra_targets', host.name + '.cat')

    if isinstance(targetcat, Mapping):
        if faintlimit is not None:
            if 'faintlimit' in targetcat:
                raise ValueError('cannot give both faintlimit kwarg and in targetcat dict')
            targetcat['faintlimit'] = faintlimit
        targetcat = select_targets(host, **targetcat)
    elif faintlimit is not None:
        #do the faintlimit cut manually if not using `select_targets`
        targetcat = targetcat[targetcat['r'] < faintlimit]

    if fibermaglimit is not None:
        fmsk = targetcat['fiber2mag_r'] < fibermaglimit
        print('Fiber mag cut removes', np.sum(~fmsk), 'of', len(fmsk), 'objects')
        targetcat = targetcat[fmsk]

    print(len(targetcat), 'objects')
    if fopcat is None:
        fopcat = select_fops(host)
    print(len(fopcat), 'FOPS')
    if skyradec is None:
        skyradec = select_sky_positions(host)

    if orderby:
        if isinstance(orderby, six.string_types):
            orderby = [orderby]

        for field in orderby:
            if field=='lowphotz':
                pz = targetcat['photz']
                highz = np.where(pz > .1)[0]
                lowz = np.where((-1 < pz)&(pz < .1))[0]
                noz = np.where(pz==-1)[0]
                print('Found {0} objects at low photz, {1} at high, and {2} '
                      'without photz'.format(len(lowz), len(highz), len(noz)))
                sorti = np.concatenate([lowz, noz, highz])
            elif field.startswith('-'):
                sorti = np.argsort(targetcat[field[1:]])[::-1]
            else:
                sorti = np.argsort(targetcat[field])
            targetcat = targetcat[sorti]

    if len(targetcat) > 1999:  # 1999 because the central object also gets one
        print('whydra cannot handle > 2000 objects, truncating')
        targetcat = targetcat[:1999]
    if len(fopcat) > 2000:
        print('whydra cannot handle > 2000 FOPS, truncating')
        fopcat = fopcat[:2000]
    if len(skyradec[0]) > 2000:
        print('whydra cannot handle > 2000 sky points, truncating')
        skyradec = skyradec[0][:2000], skyradec[1][:2000]

    #determine the SDSS vs. USNO offset
    dra, ddec = usno_vs_sdss_offset(host.get_sdss_catalog(),
                                    host.get_usnob_catalog(),
                                    raiseerror=usnosdssoffsettol)
    print('USNO/SDSS offsets:', dra * 3600, ddec * 3600)

    if os.path.exists(fnout):
        raise ValueError('File for generating master catalog {0} already exists!'.format(fnout))

    print('Constucting catalog in', fnout)
    with open(fnout, 'w') as fw:
        #first add host as center, and as object
        fw.write(_whydra_file_line(0001, 'Center'.format(host.nsaid), host.ra, host.dec, 'C'))
        fw.write('\n')

        fw.write(_whydra_file_line(1000, 'NSA{0}'.format(host.nsaid), host.ra, host.dec, 'O'))
        fw.write('\n')

        i = 2000
        for obj in targetcat:
            ln = _whydra_file_line(i, 'SDSS', obj['ra'], obj['dec'], 'O')
            i += 1
            fw.write(ln)
            fw.write(' # mag_r={0:.2f}'.format(obj['r']))
            fw.write('\n')

        i = 5000
        for fop in fopcat:
            ln = _whydra_file_line(i, 'USNO' + fop['id'], fop['RA'] - dra, fop['DEC'] - ddec, 'F')
            fw.write(ln)
            fw.write(' # mag_R2={0:.2f}'.format(fop['R2']))
            i += 1
            fw.write('\n')

        i = 8000
        j = 1
        for skyra, skydec in zip(*skyradec):
            ln = _whydra_file_line(i, 'sky{0}'.format(j), skyra, skydec, 'S')
            fw.write(ln)
            i += 1
            j += 1
            fw.write('\n')

    return targetcat