Beispiel #1
0
def plot_spoilers(guide, cand_guides, ax):
    """
    Make the spoilers plot for a given `guide` candidate.
    """
    # Define bounds of spoiler image plot: halfwidth, center, lower-left corner
    hw = 10
    rowc = int(round(guide['row']))
    colc = int(round(guide['col']))
    row0 = rowc - hw
    col0 = colc - hw

    # Pixel image canvas for plot
    region = ACAImage(np.zeros((hw * 2, hw * 2)),
                      row0=row0,
                      col0=col0)

    # Get spoilers
    stars = cand_guides.stars
    ok = ((np.abs(guide['row'] - stars['row']) < hw) &
          (np.abs(guide['col'] - stars['col']) < hw) &
          (stars['id'] != guide['id']))
    spoilers = stars[ok]

    # Add to image
    for spoil in spoilers:
        spoil_img = APL.get_psf_image(row=spoil['row'], col=spoil['col'], pix_zero_loc='edge',
                                      norm=mag_to_count_rate(spoil['mag']))
        region = region + spoil_img.aca

    # Get vmax from max pix value, but round to nearest 100, and create the image
    vmax = np.max(region).clip(100)
    vmax = np.ceil(vmax / 100) * 100
    ax.imshow(region.transpose(), cmap='hot', origin='lower', vmin=1, vmax=vmax,
              extent=(row0, row0 + hw * 2, col0, col0 + hw * 2))

    # Plot a box showing the 8x8 image boundaries
    x = rowc - 4
    y = colc - 4
    patch = patches.Rectangle((x, y), 8, 8, edgecolor='y', facecolor='none', lw=1)
    ax.add_patch(patch)

    # Make an empty box (no spoilers) or a cute 8x8 grid
    if len(spoilers) == 0:
        plt.text(rowc, colc, "No Spoilers", color='y', fontweight='bold',
                 ha='center', va='center')
    else:
        for i in range(1, 8):
            ax.plot([x, x + 8], [y + i, y + i], color='y', lw=1)
            ax.plot([x + i, x + i], [y, y + 8], color='y', lw=1)

    # Add mag text to each spoiler
    for spoil in spoilers:
        # Mag label above or below to ensure the label is in the image
        dcol = -3 if (spoil['col'] > colc) else 3
        plt.text(spoil['row'], spoil['col'] + dcol, f"mag={spoil['mag']:.1f}",
                 color='y', fontweight='bold', ha='center', va='center')

    plt.title(f"Spoiler stars (vmax={vmax:.0f})")
    ax.set_xlabel('Row')
    ax.set_ylabel('Column')
Beispiel #2
0
def plot_imposters(guide, cand_guides, ax):
    """
    Make the hot pixel imposters plot for a given `guide` candidate.
    """
    # Figure out pixel region for dithered-over-pixels plot
    row_extent = np.ceil(4 + cand_guides.dither.row)
    col_extent = np.ceil(4 + cand_guides.dither.col)
    rminus, rplus = get_ax_range(guide['row'], row_extent)
    cminus, cplus = get_ax_range(guide['col'], col_extent)

    # Pixel region of the guide
    img = ACAImage(np.zeros(shape=(rplus - rminus, cplus - cminus)),
                   row0=rminus, col0=cminus)
    dark = ACAImage(cand_guides.dark, row0=-512, col0=-512)
    img += dark.aca

    # Pixel region of the guide plus some space for annotation
    row0 = rminus - 4
    col0 = cminus - 4
    drow = (rplus - rminus) + 8
    dcol = (cplus - cminus) + 8
    canvas = ACAImage(np.zeros(shape=(drow, dcol)), row0=row0, col0=col0)
    canvas += img.aca

    ax.imshow(canvas.transpose(), interpolation='none', cmap='hot', origin='lower',
              vmin=50, vmax=3000, extent=(row0, row0 + drow, col0, col0 + dcol))

    # If force excluded, will not have imposter mag
    if not (guide['forced'] and guide['stage'] == -1):
        # Add max region with "mag"
        x = guide['imp_r']
        y = guide['imp_c']
        patch = patches.Rectangle((x, y), 2, 2, edgecolor='y', facecolor='none', lw=1.5)
        ax.add_patch(patch)
        plt.text(row0 + drow / 2, col0 + dcol - 1, f"box 'mag' {guide['imp_mag']:.1f}",
                 ha='center', va='center', color='y', fontweight='bold')

    # Plot a box showing the 8x8 image boundaries
    x = row0 + drow / 2 - 4
    y = col0 + dcol / 2 - 4
    patch = patches.Rectangle((x, y), 8, 8, edgecolor='y', facecolor='none', lw=1)
    ax.add_patch(patch)

    # Plot a box showing the 8x8 image boundaries
    patch = patches.Rectangle((rminus, cminus), rplus - rminus, cplus - cminus,
                              edgecolor='g', facecolor='none', lw=1)
    ax.add_patch(patch)

    ax.set_xlabel('Row')
    ax.set_ylabel('Column')
    plt.title("Dark Current in dither region")
Beispiel #3
0
def test_check_pixmag_offset():
    """Test the get_pixmag_for_offset guide function.

    get_pixmag_for_offset returns the magnitude required for an individual
    pixel to spoil the centroid of a candidate star by an specified offset.
    This test uses a range of pixel locations and intensities, and confirms
    that for any pixel that would cause an offset in the centroid position over
    the threshold given that the pixel value would be over the magnitude given
    by get_pixmag_for_offset.

    """
    APL = AcaPsfLibrary()

    # Then use one star and test with various pixels
    star = {'row': -.5, 'col': .5, 'mag': 9.0, 'id': 1}

    # Confirm that when the bad pixel is bright enough to shift the centroid
    # by N arcsecs that the mag/offset code agrees
    rs = range(-4, 4)
    cs = range(-4, 4)
    lims = [0.1, 0.2, .5, 1.0, 5.0]
    pixvals = range(250, 5000, 250)

    for r_dist, c_dist, lim, pixval in itertools.product(rs, cs, lims, pixvals):
        # Get a new star image every time because centroid_fm messes with it in-place
        star_img = APL.get_psf_image(star['row'], star['col'],
                                     norm=mag_to_count_rate(star['mag']))
        pix = ACAImage(np.zeros((8, 8)), row0=-4, col0=-4)

        # Get the the non-spoiled centroid
        cr1, cc1, n = star_img.centroid_fm()
        pix.aca[r_dist, c_dist] = pixval
        star_img = star_img + pix.aca

        # Spoil with a pixel and get tne new centroid
        cr2, cc2, n = star_img.centroid_fm()

        # Get the offset in pixel space
        dr = np.sqrt(((cr1 - cr2) ** 2) + ((cc1 - cc2) ** 2))

        # Check that if it is spoiled, it would have been spoiled with the tool
        pmag = get_pixmag_for_offset(star['mag'], lim)
        if dr > lim:
            assert count_rate_to_mag(pixval) < pmag
Beispiel #4
0
def test_check_spoiler_cases():
    """
    Regression test guide star selection against a star and a spoiler

    This moves a spoiling star from center past and edge then moves a spoiling star diagonally.
    This should hit check_spoil_contrib, has_spoiler_in_box, and check_mag_spoilers tests in
    the guide star selection.

    """
    drcs = np.arange(0, 13, 2)
    mag0 = 8.0  # Baseline mag
    dmags = [0, 3, 7]  # Spoiler delta mag
    # Use a blank dark map to skip imposter checks
    dark = ACAImage(np.zeros((1024, 1024)), row0=-512, col0=-512)
    spoiled = []
    for dmag in dmags:
        for drc in drcs:
            r = 10
            c = 10
            stars = StarsTable.empty()
            stars.add_fake_star(row=r, col=c, mag=mag0, id=1, ASPQ1=1)
            # Add a "spoiling" star and move it from center past edge through
            # the drcs.  The spoiling star is set with CLASS=1 so it is also not a
            # selectable guide star.
            stars.add_fake_star(row=r + drc, col=c, mag=mag0 + dmag, id=2, ASPQ1=0, CLASS=1)
            selected = get_guide_catalog(**STD_INFO, stars=stars, dark=dark)
            # Is the id=1 star spoiled / not selected?
            spoiled.append(1 if (1 not in selected['id']) else 0)
    spoiled = np.array(spoiled).reshape(-1, len(drcs)).tolist()
    #                    0  2  4  6  8 10 12 pixels
    expected_spoiled = [[1, 1, 1, 1, 1, 0, 0],  # dmag = 0
                        [1, 1, 1, 1, 0, 0, 0],  # dmag = 3
                        [1, 1, 1, 1, 0, 0, 0]]  # dmag = 7
    assert spoiled == expected_spoiled

    spoiled = []
    dmags = [3, 5, 7]  # Spoiler delta mag
    for dmag in dmags:
        for drc in drcs:
            r = 10
            c = 10
            stars = StarsTable.empty()
            stars.add_fake_star(row=r, col=c, mag=mag0, id=1, ASPQ1=1)
            # Add a "spoiling" star 5 mags fainter and move it from center out through a corner
            # The spoiling star is set with CLASS=1 so it is also not a selectable guide star.
            stars.add_fake_star(row=r + drc, col=c + drc, mag=mag0 + dmag, id=2, ASPQ1=0, CLASS=1)
            selected = get_guide_catalog(**STD_INFO, stars=stars, dark=dark)
            spoiled.append(1 if (1 not in selected['id']) else 0)
    spoiled = np.array(spoiled).reshape(-1, len(drcs)).tolist()
    #                    0  2  4  6  8 10 12 pixels
    expected_spoiled = [[1, 1, 1, 1, 0, 0, 0],  # dmag = 3
                        [1, 1, 1, 1, 0, 0, 0],  # dmag = 5
                        [1, 1, 1, 1, 0, 0, 0]]  # dmag = 7
    assert spoiled == expected_spoiled
Beispiel #5
0
def _get_dark_cal_image_props(date, select='before', t_ccd_ref=None, aca_image=False,
                              allow_negative=False):
    """
    Return the dark calibration image (e-/s) nearest to ``date`` and the corresponding
    dark_props file.

    :param date: date in any CxoTime format
    :param select: method to select dark cal (before|nearest|after)
    :param t_ccd_ref: rescale dark map to temperature (degC, default=no scaling)
    :param aca_image: return an AcaImage instance
    :param allow_negative: allow negative values in raw dark map (default=False)

    :returns: 1024 x 1024 ndarray with dark cal image in e-/s, props dict
    """
    DARK_CAL['id'] = get_dark_cal_id(date, select)

    with fits.open(MICA_FILES['dark_image.fits'].abs, memmap=False) as hdus:
        dark = hdus[0].data
        # Recast as native byte ordering since FITS is typically not. This
        # statement is normally the same as dark.astype(np.float32).
        dark = dark.astype(dark.dtype.type)

    with open(MICA_FILES['dark_props.json'].abs, 'r') as fh:
        props = json.load(fh)

    # Change unicode to ascii at top level
    if six.PY2:
        keys = list(props.keys())
        asciiprops = {}
        for key in keys:
            asciiprops[str(key)] = props[key]
        props = asciiprops

    if t_ccd_ref is not None:
        # Scale factor to adjust data to an effective temperature of t_ccd_ref.
        # For t_ccds warmer than t_ccd_ref this scale factor is < 1, i.e. the
        # observed dark current is made smaller to match what it would be at the
        # lower reference temperature.
        t_ccd = props['ccd_temp']
        dark *= dark_temp_scale(t_ccd, t_ccd_ref)

    if not allow_negative:
        np.clip(dark, a_min=0, a_max=None, out=dark)

    if aca_image:
        dark = ACAImage(dark, row0=-512, col0=-512)

    return dark, props
Beispiel #6
0
def test_overlap_spoiler():
    """
    Add a brighter "spoiler" that is a good guide star and confirm the test for
    overlapping selected stars works until just past (12 pixels).
    """

    # Use a blank dark map to skip imposter checks
    dark = ACAImage(np.zeros((1024, 1024)), row0=-512, col0=-512)

    spoiled = []
    drcs = np.arange(6, 17, 2)
    for drc in drcs:
        r = 10
        c = 10
        stars = StarsTable.empty()
        stars.add_fake_star(row=r, col=c, mag=9, id=1, ASPQ1=0)
        # Add a brighter spoiling star
        stars.add_fake_star(row=r, col=c + drc, mag=7, id=2, ASPQ1=0)
        selected = get_guide_catalog(**STD_INFO, stars=stars, dark=dark)
        spoiled.append(1 if (1 not in selected['id']) else 0)
    #                   6  8 10 12 14 16  pixels
    expected_spoiled = [1, 1, 1, 1, 0, 0]
    assert spoiled == expected_spoiled
Beispiel #7
0
def plot_imposters(acq,
                   dark,
                   dither,
                   vmin=100,
                   vmax=2000,
                   figsize=(5, 5),
                   r=None,
                   c=None,
                   filename=None):
    """
    Plot dark current, relevant boxes, imposters and spoilers.
    """
    drc = int(np.max(ACQ.box_sizes) / 5 + 5 + dither.max() / 5)

    # Make an image of `dark` centered on acq row, col.  Use ACAImage
    # arithmetic to handle the corner case where row/col are near edge
    # and a square image goes off the edge.
    if r is None:
        r = int(np.round(acq['row']))
        c = int(np.round(acq['col']))
    img = ACAImage(np.zeros(shape=(drc * 2, drc * 2)),
                   row0=r - drc,
                   col0=c - drc)
    dark = ACAImage(dark, row0=-512, col0=-512)
    img += dark.aca

    # Show the dark current image
    fig = plt.figure(figsize=figsize)
    ax = fig.add_subplot(1, 1, 1)
    ax.imshow(img.transpose(),
              interpolation='none',
              cmap='hot',
              origin='lower',
              vmin=vmin,
              vmax=vmax)

    # CCD edge
    r = -512.5 - img.row0
    c = -512.5 - img.col0
    patch = patches.Rectangle((r, c),
                              1025,
                              1025,
                              edgecolor="g",
                              facecolor="none",
                              lw=2.5)
    ax.add_patch(patch)

    # Imposter stars
    for idx, imp in enumerate(acq['imposters']):
        r = imp['row0'] - img.row0
        c = imp['col0'] - img.col0
        patch = patches.Rectangle((r + 0.5, c + 0.5),
                                  6,
                                  6,
                                  edgecolor="y",
                                  facecolor="none",
                                  lw=1.5)
        ax.add_patch(patch)
        plt.text(r + 7,
                 c + 7,
                 str(idx),
                 color='y',
                 fontsize='large',
                 fontweight='bold')

    # Box regions
    rc = img.shape[0] // 2 + 0.5
    cc = img.shape[1] // 2 + 0.5
    for hw in ACQ.p_man_errs['man_err_hi']:
        hwpr = hw / 5 + dither.row
        hwpc = hw / 5 + dither.col
        patch = patches.Rectangle((rc - hwpr, cc - hwpc),
                                  hwpr * 2,
                                  hwpc * 2,
                                  edgecolor='r',
                                  facecolor='none',
                                  lw=1,
                                  alpha=1)
        ax.add_patch(patch)
        plt.text(rc - hwpr + 1,
                 cc - hwpc + 1,
                 f'{hw}"',
                 color='y',
                 fontweight='bold')

    # Hack to fix up ticks to have proper row/col coords.  There must be a
    # correct way to do this.
    xticks_loc = ax.get_xticks().tolist()
    xticks = [str(int(label) + img.row0) for label in xticks_loc]
    ax.xaxis.set_major_locator(FixedLocator(xticks_loc))
    ax.set_xticklabels(xticks)
    ax.set_xlabel('Row')
    yticks_loc = ax.get_yticks().tolist()
    yticks = [str(int(label) + img.col0) for label in yticks_loc]
    ax.yaxis.set_major_locator(FixedLocator(yticks_loc))
    ax.set_yticklabels(yticks)
    ax.set_ylabel('Column')
    ax.set_title('Red boxes show search box size + dither')

    plt.tight_layout()
    if filename is not None:
        # When Ska3 has matplotlib 2.2+ then just use `filename`
        plt.savefig(str(filename), pad_inches=0.0)
    plt.close(fig)

    return img, ax
Beispiel #8
0
def get_l0_images(start, stop, slot, imgsize=None, columns=None):
    """
    Get ACA L0 images for the given  ``start`` and ``stop`` times and
    the given ``slot``.  Optionally filter on image size via ``imgsize``
    or change the default image metadata via ``columns``.

      >>> from mica.archive import aca_l0
      >>> imgs = aca_l0.get_l0_images('2012:001', '2012:002', slot=7)
      >>> imgs = aca_l0.get_l0_images('2005:001', '2005:002', slot=6, imgsize=[8])

    The default columns are:
    ['TIME', 'IMGROW0', 'IMGCOL0', 'BGDAVG', 'IMGSTAT', 'IMGFUNC1', 'IMGSIZE', 'IMGSCALE', 'INTEG']

    The image pixel values are given in units of DN.  One can convert to e-/sec
    by multiplying by (5 / INTEG).

    :param start: start time of requested interval
    :param stop: stop time of requested interval
    :param slot: slot number integer (in the range 0 -> 7)
    :param imgsize: list of integers of desired image sizes (default=[4, 6, 8])
    :param columns: image meta-data columns

    :returns: list of ACAImage objects
    """
    if columns is None:
        columns = [
            'TIME', 'BGDAVG', 'IMGSTAT', 'IMGFUNC1', 'IMGSIZE', 'IMGSCALE',
            'INTEG'
        ]
    if 'IMGROW0' not in columns:
        columns.append('IMGROW0')
    if 'IMGCOL0' not in columns:
        columns.append('IMGCOL0')

    slot_columns = list(set(columns + ['QUALITY', 'IMGRAW', 'IMGSIZE']))
    dat = get_slot_data(start,
                        stop,
                        slot,
                        imgsize=imgsize,
                        columns=slot_columns)

    ok = dat['QUALITY'] == 0
    if not (np.all(ok)):
        dat = dat[ok]

    # Convert temperatures from K to degC
    for temp in ('TEMPCCD', 'TEMPHOUS', 'TEMPPRIM', 'TEMPSEC'):
        if temp in dat.dtype.names:
            dat[temp] -= 273.15

    # Masked array col access ~100 times slower than ndarray so convert
    dat = dat.filled(-9999)

    imgs = []
    imgsizes = dat['IMGSIZE']
    imgraws = dat['IMGRAW']
    cols = {name: dat[name] for name in columns}

    for i, row in enumerate(dat):
        imgraw = imgraws[i].reshape(8, 8)
        sz = imgsizes[i]
        if sz < 8:
            imgraw = imgraw[:sz, :sz]

        meta = {name: col[i] for name, col in cols.items() if col[i] != -9999}
        imgs.append(ACAImage(imgraw, meta=meta))

    return imgs