예제 #1
0
def get_data_2d():
    stars0 = stars[0]

    q0 = Quat([0, 0, 45])

    yags = []
    zags = []
    for ra, dec in stars0:
        yag, zag = radec2yagzag(ra, dec, q0)
        yags.append(yag * 3600)
        zags.append(zag * 3600)

    yags_obs_list = []
    zags_obs_list = []
    times = np.linspace(0, 1000, 10)
    rolls = np.sin(2 * np.pi * times / 666) * 100 / 3600
    pitches = np.sin(2 * np.pi * times / 1000) * 50 / 3600
    yaws = np.sin(2 * np.pi * times / 707) * 30 / 3600

    qs = []
    for roll, pitch, yaw in zip(rolls, pitches, yaws):
        dq = Quat([yaw, -pitch, roll])
        q0_offset = q0 * dq
        qs.append(q0_offset)

        yags_obs = []
        zags_obs = []
        for ra, dec in stars0:
            yag, zag = radec2yagzag(ra, dec, q0_offset)
            yags_obs.append(yag * 3600)
            zags_obs.append(zag * 3600)
        yags_obs_list.append(yags_obs)
        zags_obs_list.append(zags_obs)

    return q0, rolls, pitches, yaws, qs, yags, zags, yags_obs_list, zags_obs_list
예제 #2
0
def test_calc_roll_pitch_yaw(stars, pitch, yaw, roll):
    roll /= 3600
    pitch /= 3600
    yaw /= 3600

    q0 = Quat([0, 0, 45])
    dq = Quat([yaw, -pitch, roll])
    assert np.isclose(dq.roll, roll)
    assert np.isclose(dq.pitch, pitch)
    assert np.isclose(dq.yaw, yaw)

    q0_offset = q0 * dq
    assert np.isclose(q0_offset.roll, q0.roll + roll)

    yags = []
    zags = []
    yags_obs = []
    zags_obs = []
    for ra, dec in stars:
        yag, zag = radec2yagzag(ra, dec, q0)
        yags.append(yag * 3600)
        zags.append(zag * 3600)

        yag, zag = radec2yagzag(ra, dec, q0_offset)
        yags_obs.append(yag * 3600)
        zags_obs.append(zag * 3600)

    sigma = np.arange(len(yags)) + 1
    out_roll, out_pitch, out_yaw = calc_roll_pitch_yaw(yags, zags, yags_obs,
                                                       zags_obs, sigma)
    # Computed pitch, yaw, roll within 0.5 arcsec in roll, 0.02 arcsec pitch/yaw
    assert np.isclose(roll, out_roll, atol=0.5 / 3600, rtol=0.0)
    assert np.isclose(pitch, out_pitch, atol=0.02 / 3600, rtol=0.0)
    assert np.isclose(yaw, out_yaw, atol=0.02 / 3600, rtol=0.0)
예제 #3
0
def test_calc_roll(roll):
    stars = [
        (-1, 1),  # ra, dec in deg
        (1, 1),
        (0, 0),
        (1, -1)
    ]

    q0 = Quat([0, 0, 45])
    dq = Quat([0, 0, roll])
    assert np.isclose(dq.roll, roll)
    q0_roll = q0 * dq
    assert np.isclose(q0_roll.roll, q0.roll + roll)

    yags = []
    zags = []
    yags_obs = []
    zags_obs = []
    for ra, dec in stars:
        yag, zag = radec2yagzag(ra, dec, q0)
        yags.append(yag)
        zags.append(zag)

        yag, zag = radec2yagzag(ra, dec, q0_roll)
        yags_obs.append(yag)
        zags_obs.append(zag)

    out_roll = calc_roll(yags, zags, yags_obs, zags_obs)
    # Computed roll is within 0.1% of actual roll
    assert np.isclose(roll, out_roll, atol=0.0, rtol=0.001)
예제 #4
0
def get_trak_cat_from_telem(start, stop, cmd_quat):
    start = DateTime(start)
    stop = DateTime(stop)
    msids = [
        "{}{}".format(m, i)
        for m in ['AOACYAN', 'AOACZAN', 'AOACFID', 'AOIMAGE', 'AOACFCT']
        for i in range(0, 8)
    ]
    telem = fetch.MSIDset(
        ['AOACASEQ', 'CORADMEN', 'AOPCADMD', 'AONSTARS', 'AOKALSTR'] + msids,
        start, stop)
    att = fetch.MSIDset(['AOATTQT{}'.format(i) for i in [1, 2, 3, 4]], start,
                        stop)
    cat = {}
    for slot in range(0, 8):
        track = telem['AOACFCT{}'.format(slot)].vals == 'TRAK'
        fid = telem['AOACFID{}'.format(slot)].vals == 'FID '
        star = telem['AOIMAGE{}'.format(slot)].vals == 'STAR'
        n = 30
        if np.count_nonzero(track) < n:
            continue
        if np.any(fid & track):
            cat[slot] = {
                'type': 'FID',
                'yag': telem['AOACYAN{}'.format(slot)].vals[fid & track][0],
                'zag': telem['AOACZAN{}'.format(slot)].vals[fid & track][0]
            }
        else:
            n_samples = np.count_nonzero(track & star)
            if n_samples < (n + 4):
                continue
            # If there is tracked data with a star, let's try to get our n samples from about
            # the middle of the range
            mid_point = int(n_samples / 2.)
            yags = []
            zags = []
            for sample in range(mid_point - int(n / 2.),
                                mid_point + int(n / 2.)):
                qref = Quat(
                    normalize([
                        att['AOATTQT{}'.format(i)].vals[track & star][sample]
                        for i in [1, 2, 3, 4]
                    ]))
                ra, dec = yagzag2radec(
                    telem['AOACYAN{}'.format(slot)].vals[track & star][sample]
                    / 3600.,
                    telem['AOACZAN{}'.format(slot)].vals[track & star][sample]
                    / 3600., qref)
                yag, zag = radec2yagzag(ra, dec, cmd_quat)
                yags.append(yag)
                zags.append(zag)
            # This doesn't detect MON just yet
            cat[slot] = {
                'type': 'STAR',
                'yag': np.median(yags) * 3600.,
                'zag': np.median(zags) * 3600.
            }
    return cat, telem
예제 #5
0
def make_plots_for_obsid(obsid, ra, dec, roll, starcat_time, catalog, outdir, red_mag_lim=10.7):
    """
    Make standard starcheck plots for obsid and save as pngs with standard names.
    Writes out to stars_{obsid}.png and star_view_{obsid}.png in supplied outdir.

    :param obsid:  Obsid used for file names
    :param ra: RA in degrees
    :param dec: Dec in degrees
    :param roll: Roll in degrees

    :param catalog: list of dicts or other astropy.table compatible structure with conventional
                    starcheck catalog parameters for a set of ACQ/BOT/GUI/FID/MON items.
    :param outdir: output directory for png plot files
    :param red_mag_lim: faint limit
    """

    # explicitly float convert these, as we may be receiving this from Perl passing strings
    ra = float(ra)
    dec = float(dec)
    roll = float(roll)

    # get the agasc field once and then use it for both plots that have stars
    stars = agasc.get_agasc_cone(ra, dec,
                                 radius=1.5,
                                 date=starcat_time)
    # We use the full star list for both the field plot and the main "catalog" plot
    # and we can save looking up the yang/zang positions twice if we add that content
    # to the stars in this wrapper
    yags, zags = radec2yagzag(stars['RA_PMCORR'], stars['DEC_PMCORR'],
                              Quaternion.Quat([ra, dec, roll]))
    stars['yang'] = yags * 3600
    stars['zang'] = zags * 3600

    bad_stars = bad_acq_stars(stars)

    f_plot = plot_stars(attitude=[ra, dec, roll], catalog=None, stars=stars,
                        title=None, starcat_time=starcat_time,
                        bad_stars=bad_stars, red_mag_lim=None)
    f_plot.savefig(os.path.join(outdir, 'star_view_{}.png'.format(obsid)), dpi=80)
    plt.close(f_plot)
    cat_plot = plot_stars(attitude=[ra, dec, roll], catalog=catalog, stars=stars,
                          title="RA=%.6f Dec=%.6f Roll=%.6f" % (ra, dec, roll),
                          starcat_time=starcat_time,
                          bad_stars=bad_stars, red_mag_lim=red_mag_lim)
    cat_plot.savefig(os.path.join(outdir, 'stars_{}.png'.format(obsid)), dpi=80)
    plt.close(cat_plot)
    compass_plot = plot_compass(roll)
    compass_plot.savefig(os.path.join(outdir, 'compass{}.png'.format(obsid)), dpi=80)
    plt.close(compass_plot)
예제 #6
0
def get_trak_cat_from_telem(start, stop, cmd_quat):
    start = DateTime(start)
    stop = DateTime(stop)
    msids = ["{}{}".format(m, i) for m in ['AOACYAN', 'AOACZAN', 'AOACFID', 'AOIMAGE', 'AOACFCT']
             for i in range(0, 8)]
    telem = fetch.MSIDset(['AOACASEQ', 'CORADMEN', 'AOPCADMD', 'AONSTARS', 'AOKALSTR']
                          + msids, start, stop)
    att = fetch.MSIDset(['AOATTQT{}'.format(i) for i in [1, 2, 3, 4]], start, stop)
    cat = {}
    for slot in range(0, 8):
        track = telem['AOACFCT{}'.format(slot)].vals == 'TRAK'
        fid = telem['AOACFID{}'.format(slot)].vals == 'FID '
        star = telem['AOIMAGE{}'.format(slot)].vals == 'STAR'
        n = 30
        if np.count_nonzero(track) < n:
            continue
        if np.any(fid & track):
            cat[slot] = {'type': 'FID',
                         'yag': telem['AOACYAN{}'.format(slot)].vals[fid & track][0],
                         'zag': telem['AOACZAN{}'.format(slot)].vals[fid & track][0]}
        else:
            n_samples = np.count_nonzero(track & star)
            if n_samples < (n + 4):
                continue
            # If there is tracked data with a star, let's try to get our n samples from about
            # the middle of the range
            mid_point = int(n_samples / 2.)
            yags = []
            zags = []
            for sample in range(mid_point - int(n / 2.), mid_point + int(n / 2.)):
                qref = Quat(normalize([att['AOATTQT{}'.format(i)].vals[track & star][sample]
                                       for i in [1, 2, 3, 4]]))
                ra, dec = yagzag2radec(
                    telem['AOACYAN{}'.format(slot)].vals[track & star][sample] / 3600.,
                    telem['AOACZAN{}'.format(slot)].vals[track & star][sample] / 3600.,
                    qref)
                yag, zag = radec2yagzag(ra, dec, cmd_quat)
                yags.append(yag)
                zags.append(zag)
            # This doesn't detect MON just yet
            cat[slot] = {'type': 'STAR',
                         'yag': np.median(yags) * 3600.,
                         'zag': np.median(zags) * 3600.}
    return cat, telem
예제 #7
0
def get_stars(starcat_time, quaternion, radius=1.5):
    import agasc
    from Ska.quatutil import radec2yagzag
    from chandra_aca.transform import yagzag_to_pixels
    stars = agasc.get_agasc_cone(quaternion.ra, quaternion.dec,
                                 radius=radius,
                                 date=starcat_time)

    if 'yang' not in stars.colnames or 'zang' not in stars.colnames:
        # Add star Y angle and Z angle in arcsec to the stars table.
        # radec2yagzag returns degrees.
        yags, zags = radec2yagzag(stars['RA_PMCORR'], stars['DEC_PMCORR'], quaternion)
        stars['yang'] = yags * 3600
        stars['zang'] = zags * 3600

    # Update table to include row/col values corresponding to yag/zag
    rows, cols = yagzag_to_pixels(stars['yang'], stars['zang'], allow_bad=True)
    stars['row'] = rows
    stars['col'] = cols

    return stars
예제 #8
0
def radec_to_yagzag(ra, dec, q_att):
    """
    Given RA, Dec, and pointing quaternion, determine ACA Y-ang, Z-ang.  The
    input ``ra`` and ``dec`` values can be 1-d arrays in which case the output
    ``yag`` and ``zag`` will be corresponding arrays of the same length.

    This is a wrapper around Ska.quatutil.radec2yagzag but uses arcsec instead
    of deg for yag, zag.

    :param ra: Right Ascension (degrees)
    :param dec: Declination (degrees)
    :param q_att: ACA pointing quaternion

    :returns:  yag, zag (arcsec)
    """
    from Ska.quatutil import radec2yagzag
    yag, zag = radec2yagzag(ra, dec, q_att)
    yag *= 3600
    zag *= 3600

    return yag, zag
예제 #9
0
def _plot_field_stars(ax, stars, attitude, red_mag_lim=None, bad_stars=None):
    """
    Plot plot field stars in yang and zang on the supplied
    axes object in place.

    :param ax: matplotlib axes
    :param stars: astropy.table compatible set of records of agasc entries of stars
    :param attitude: Quaternion-compatible attitude
    :param red_mag_lim: faint limit
    :param bad_stars: boolean mask of stars to be plotted in red
    """
    stars = Table(stars)
    quat = Quaternion.Quat(attitude)

    if bad_stars is None:
        bad_stars = np.zeros(len(stars), dtype=bool)

    if 'yang' not in stars.colnames or 'zang' not in stars.colnames:
        # Add star Y angle and Z angle in arcsec to the stars table.
        # radec2yagzag returns degrees.
        yags, zags = radec2yagzag(stars['RA_PMCORR'], stars['DEC_PMCORR'],
                                  quat)
        stars['yang'] = yags * 3600
        stars['zang'] = zags * 3600

    # Update table to include row/col values corresponding to yag/zag
    rows, cols = yagzag_to_pixels(stars['yang'], stars['zang'], allow_bad=True)
    stars['row'] = rows
    stars['col'] = cols

    # Initialize array of colors for the stars, default is black.  Use 'object'
    # type to not worry in advance about string length and also for Py2/3 compat.
    colors = np.zeros(len(stars), dtype='object')
    colors[:] = 'black'

    colors[bad_stars] = BAD_STAR_COLOR

    if red_mag_lim:
        # Mark stars with the FAINT_STAR_COLOR if they have MAG_ACA
        # that is fainter than red_mag_lim but brighter than red_mag_lim
        # plus a rough mag error.  The rough mag error calculation is
        # based on the SAUSAGE acq stage 1 check, which uses nsigma of
        # 3.0, a mag low limit of 1.5, and a random error of 0.26.
        nsigma = 3.0
        mag_error_low_limit = 1.5
        randerr = 0.26
        caterr = stars['MAG_ACA_ERR'] / 100.
        error = nsigma * np.sqrt(randerr**2 + caterr**2)
        error = error.clip(mag_error_low_limit)
        # Faint and bad stars will keep their BAD_STAR_COLOR
        # Only use the faint mask on stars that are not bad
        colors[(stars['MAG_ACA'] >= red_mag_lim)
               & (stars['MAG_ACA'] < red_mag_lim + error)
               & ~bad_stars] = FAINT_STAR_COLOR
        # Don't plot those for which MAG_ACA is fainter than red_mag_lim + error
        # This overrides any that may be 'bad'
        colors[stars['MAG_ACA'] >= red_mag_lim + error] = 'none'

    size = symsize(stars['MAG_ACA'])
    # scatter() does not take an array of alphas, and rgba is
    # awkward for color='none', so plot these in a loop.
    for color, alpha in [(FAINT_STAR_COLOR, FAINT_STAR_ALPHA),
                         (BAD_STAR_COLOR, BAD_STAR_ALPHA), ('black', 1.0)]:
        colormatch = colors == color
        ax.scatter(stars[colormatch]['row'],
                   stars[colormatch]['col'],
                   c=color,
                   s=size[colormatch],
                   edgecolor='none',
                   alpha=alpha)
예제 #10
0
def plot_starcheck(catalog, quat=None, field=None, title=None):
    cat = catalog
    fig = plt.figure(num=1, figsize=(4, 4))
    ax = fig.add_subplot(1, 1, 1)
    face = backcolor

    # plot the box and set the labels
    plt.xlim(2900, -2900)
    plt.ylim(-2900, 2900)
    b1hw = 2560
    box1 = plt.Rectangle((b1hw, -b1hw), -2 * b1hw, 2 * b1hw,
                         fill=False)
    ax.add_patch(box1)
    b2hw = 2600
    box2 = plt.Rectangle((b2hw, -b2hw), -2 * b2hw, 2 * b2hw,
                         fill=False)
    ax.add_patch(box2)

    ax.scatter([-2700, -2700, -2700, -2700, -2700],
               [2400, 2100, 1800, 1500, 1200],
               c='orange', edgecolors='orange',
               s=symsize(np.array([10.0, 9.0, 8.0, 7.0, 6.0])))

    [l.set_rotation(90) for l in ax.get_yticklabels()]
    ax.grid()
    ax.set_ylabel("Zag (arcsec)")
    ax.set_xlabel("Yag (arcsec)")

    # plot starcheck catalog
    gui = cat[cat['type'] == 'GUI']
    acq = cat[cat['type'] == 'ACQ']
    bot = cat[cat['type'] == 'BOT']
    fid = cat[cat['type'] == 'FID']
    for row in cat:
        ax.annotate("%s" % row['idx'],
                    xy=(row['yang'] - 120, row['zang'] + 60))
    ax.scatter(gui['yang'], gui['zang'],
               facecolors=face,
               edgecolors='green',
               s=100)
    ax.scatter(bot['yang'], bot['zang'],
               facecolors=face,
               edgecolors='green',
               s=100)
    for acq_star in acq:
        acq_box = plt.Rectangle(
            (acq_star['yang'] - acq_star['halfw'],
             acq_star['zang'] - acq_star['halfw']),
            width=acq_star['halfw'] * 2,
            height=acq_star['halfw'] * 2,
            color='blue',
            fill=False)
        ax.add_patch(acq_box)
    for acq_star in bot:
        acq_box = plt.Rectangle(
            (acq_star['yang'] - acq_star['halfw'],
             acq_star['zang'] - acq_star['halfw']),
            width=acq_star['halfw'] * 2,
            height=acq_star['halfw'] * 2,
            color='blue',
            fill=False)
        ax.add_patch(acq_box)
    ax.scatter(fid['yang'], fid['zang'],
               facecolors=face,
               edgecolors='green',
               marker='o',
               s=200)
    ax.scatter(fid['yang'], fid['zang'],
               facecolors=face,
               edgecolors='green',
               marker='+',
               linewidth=3,
               s=200)

    # plot field if present
    if field is not None:
        faint_plot_mag = 11.0
        bright_field = field[field['MAG_ACA'] < faint_plot_mag]
        yags = []
        zags = []
        for star in bright_field:
            yag, zag = radec2yagzag(star['RA_PMCORR'],
                                    star['DEC_PMCORR'],
                                    quat)
            yag *= 3600
            zag *= 3600
            yags.append(yag)
            zags.append(zag)

        bright_field = Ska.Numpy.add_column(bright_field,
                                            'yang',
                                            yags)
        bright_field = Ska.Numpy.add_column(bright_field,
                                            'zang',
                                            zags)
        color = np.ones(len(bright_field), dtype='|S10')
        color[:] = 'red'
        faint = ((bright_field['CLASS'] == 0)
                 & (bright_field['MAG_ACA'] >= 10.7))
        color[faint] = 'orange'
        ok = ((bright_field['CLASS'] == 0)
              & (bright_field['MAG_ACA'] < 10.7))
        color[ok] = frontcolor
        size = symsize(bright_field['MAG_ACA'])
        ax.scatter(bright_field['yang'], bright_field['zang'],
                   c=color.tolist(), s=size, edgecolors=color.tolist())
        if title is not None:
            fig.suptitle(title)
    return fig
예제 #11
0
def _plot_field_stars(ax, stars, attitude, red_mag_lim=None, bad_stars=None):
    """
    Plot plot field stars in yang and zang on the supplied
    axes object in place.

    :param ax: matplotlib axes
    :param stars: astropy.table compatible set of records of agasc entries of stars
    :param attitude: Quaternion-compatible attitude
    :param red_mag_lim: faint limit
    :param bad_stars: boolean mask of stars to be plotted in red
    """
    stars = Table(stars)
    quat = Quaternion.Quat(attitude)

    if bad_stars is None:
        bad_stars = np.zeros(len(stars), dtype=bool)

    if 'yang' not in stars.colnames or 'zang' not in stars.colnames:
        # Add star Y angle and Z angle in arcsec to the stars table.
        # radec2yagzag returns degrees.
        yags, zags = radec2yagzag(stars['RA_PMCORR'], stars['DEC_PMCORR'], quat)
        stars['yang'] = yags * 3600
        stars['zang'] = zags * 3600

    # Initialize array of colors for the stars, default is black
    colors = np.zeros(len(stars), dtype='S20')
    colors[:] = 'black'

    colors[bad_stars] = BAD_STAR_COLOR

    if red_mag_lim:
        # Mark stars with the FAINT_STAR_COLOR if they have MAG_ACA
        # that is fainter than red_mag_lim but brighter than red_mag_lim
        # plus a rough mag error.  The rough mag error calculation is
        # based on the SAUSAGE acq stage 1 check, which uses nsigma of
        # 3.0, a mag low limit of 1.5, and a random error of 0.26.
        nsigma = 3.0
        mag_error_low_limit = 1.5
        randerr = 0.26
        caterr = stars['MAG_ACA_ERR'] / 100.
        error = nsigma * np.sqrt(randerr**2 + caterr**2)
        error = error.clip(mag_error_low_limit)
        # Faint and bad stars will keep their BAD_STAR_COLOR
        # Only use the faint mask on stars that are not bad
        colors[(stars['MAG_ACA'] >= red_mag_lim)
               & (stars['MAG_ACA'] < red_mag_lim + error)
               & ~bad_stars] = FAINT_STAR_COLOR
        # Don't plot those for which MAG_ACA is fainter than red_mag_lim + error
        # This overrides any that may be 'bad'
        colors[stars['MAG_ACA'] >= red_mag_lim + error] = 'none'

    size = symsize(stars['MAG_ACA'])
    # scatter() does not take an array of alphas, and rgba is
    # awkward for color='none', so plot these in a loop.
    for color, alpha in [(FAINT_STAR_COLOR, FAINT_STAR_ALPHA),
                         (BAD_STAR_COLOR, BAD_STAR_ALPHA),
                         ('black', 1.0)]:
        colormatch = colors == color
        ax.scatter(stars[colormatch]['yang'],
                   stars[colormatch]['zang'],
                   c=color, s=size[colormatch], edgecolor='none',
                   alpha=alpha)
예제 #12
0
def schedule_monitor_windows(t,
                             predefined_locs=False,
                             mag_thres=13,
                             radius=25):
    """
    :t: astropy Table
    :predefined_locs: if True, try the predefined CCD locations first,
                      default=False.
    """
    locations = Table.read(PREDEFINED, delimiter=' ', format='ascii')

    for row in t:

        obsid = row['obsid']
        dur = row['duration']
        num_allowed = row['num_mon_windows']

        msg = ' '.join([
            '\nObsID = {}, duration = {:.0f} sec:'.format(obsid, dur),
            '{} monitor windows allowed'.format(num_allowed)
        ])
        print(msg)

        date = row['date']
        quat = row['quat']

        num_scheduled = 0

        stars = {}

        # If flag set, try predefined locations first
        if predefined_locs:

            print("Predefined locations:")

            kwargs = {'mag_thres': mag_thres, 'radius': radius}

            if not all(locations['scheduled']) == 1:
                idx_list = schedule_predefined(locations, date, quat,
                                               num_allowed, **kwargs)
                num_scheduled = len(idx_list)
                # Update status of predefined locations
                for idx in idx_list:
                    locations['scheduled'][idx] = 1

        # All allowed predefined locations are scheduled, or predefined_locs=False,
        # Add random locations if needed
        if num_scheduled < num_allowed:

            print("Random locations:")

            ra, dec = quatutil.yagzag2radec(0, 0, quat)
            cat = agasc.agasc.get_agasc_cone(ra, dec, 1.5, date)

            # Filter to pick up bright spoiler stars
            ok = np.array(cat['MAG_ACA'] < mag_thres, dtype=bool)
            cat = cat[ok]

            yags, zags = quatutil.radec2yagzag(cat['RA_PMCORR'],
                                               cat['DEC_PMCORR'], quat)
            rows, cols = transform.yagzag_to_pixels(yags * 3600.,
                                                    zags * 3600,
                                                    allow_bad=True)

            # Identify spoiler stars that fit on ccd
            ok = (rows > -512.5) * (rows < 511.5) * (cols > -512.5) * (cols <
                                                                       511.5)

            rows = np.array(np.round(rows[ok]), dtype=int)
            cols = np.array(np.round(cols[ok]), dtype=int)

            # Collect spoiler stars
            vals = np.ones(len(rows))
            stars = get_spoiler_stars(rows, cols, vals)

            # Now dict stars contains keys that represent (row, col) of
            # spoiler star centers and a 10px rim around each spoiler star.

            while num_scheduled < num_allowed:
                # Draw a random location on ccd, avoid edges:
                r, c = np.random.randint(-504, 505, 2)

                # Check if the location was previously scheduled - TBD

                # Check if the location is free of stars
                if (r, c) not in stars:
                    yag, zag = transform.pixels_to_yagzag(
                        r, c)  # yag, zag in arcsec
                    msg = ' '.join([
                        'Schedule {} monitor window at:\n'.format(
                            num_scheduled + 1),
                        '    (yag, zag) = ({:.0f}, {:.0f}) arcsec\n'.format(
                            yag,
                            zag), '    (row, col) = ({}, {})'.format(r, c)
                    ])
                    print(msg)
                    num_scheduled = num_scheduled + 1
                    stars = get_spoiler_stars([r], [c], [5], stars, rim=4)

    # Update the PREDEFINED file - TBD

    return stars