Ejemplo n.º 1
0
def test_edge_checking():
    """Test row/col edge checking"""

    # Within limits, doesn't fail
    yag, zag = pixels_to_yagzag(511.7, -511.7)
    yagzag_to_pixels(yag, zag)

    with pytest.raises(ValueError):
        pixels_to_yagzag(512.2, 0)

    with pytest.raises(ValueError):
        pixels_to_yagzag(0, -512.2)

    yag, zag = pixels_to_yagzag(512.2, -512.2, allow_bad=True)
    with pytest.raises(ValueError):
        yagzag_to_pixels(yag, zag)
Ejemplo n.º 2
0
def test_monitor_input_processing_ra_dec(stars):
    ra, dec = 0.1, 0.2
    mag = 7.0
    monitors = [[ra, dec, MonCoord.RADEC, mag, MonFunc.MON_FIXED]]
    aca = get_aca_catalog(**mod_std_info(att=stars.att, n_fid=0, n_guide=5),
                          stars=stars,
                          monitors=monitors)
    monitors = aca.mons.monitors
    assert len(monitors) == 1
    assert isinstance(monitors, Table)
    yang, zang = radec_to_yagzag(ra, dec, aca.att)
    row, col = yagzag_to_pixels(yang, zang)
    assert np.allclose(monitors['yang'][0], yang)
    assert np.allclose(monitors['zang'][0], zang)
    assert np.allclose(monitors['ra'][0], ra)
    assert np.allclose(monitors['dec'][0], dec)
    assert np.allclose(monitors['row'][0], row)
    assert np.allclose(monitors['col'][0], col)

    ok = aca['type'] == 'MON'
    assert np.count_nonzero(ok) == 1
    mon = aca[ok][0]
    assert np.allclose(mon['yang'], yang)
    assert np.allclose(mon['zang'], zang)
    assert np.allclose(mon['mag'], mag)
    assert np.allclose(mon['maxmag'], ACA.monitor_maxmag)
    assert mon['id'] == 1000
Ejemplo n.º 3
0
Archivo: fid.py Proyecto: sot/proseco
    def get_fid_candidates(self):
        """
        Get all fids for this detector that are on the CCD (with margin) and are not
        impacted by a bad pixel.

        This also finds fid spoiler stars and computes the spoiler_score.

        Result is updating self.cand_fids.
        """
        yang, zang = get_fid_positions(self.detector, self.focus_offset,
                                       self.sim_offset)
        row, col = yagzag_to_pixels(yang, zang, allow_bad=True)
        ids = np.arange(len(yang), dtype=np.int64) + 1  # E.g. 1 to 6 for ACIS

        # Set up candidate fids table (which copies relevant meta data) and add
        # columns.
        cand_fids = FidTable([ids, yang, zang, row, col],
                             names=['id', 'yang', 'zang', 'row', 'col'])
        shape = (len(cand_fids), )
        cand_fids['mag'] = np.full(shape, FID.fid_mag)  # 7.000
        cand_fids['spoilers'] = np.full(
            shape, None)  # Filled in with Table of spoilers
        cand_fids['spoiler_score'] = np.full(shape, 0, dtype=np.int64)

        self.log(f'Initial candidate fid ids are {cand_fids["id"].tolist()}')

        # First check that any manually included fid ids are valid by seeing if
        # the supplied fid is in the initial ids for this detector.
        if id_diff := set(self.include_ids_fid) - set(cand_fids['id']):
            raise ValueError(f'included fid ids {id_diff} are not valid')
Ejemplo n.º 4
0
def add_imposter(dark, acq, dyang, dzang, dmag):
    """
    For testing, add an imposter (single hot pixel) at the specified delta location
    and mag relative to an ``acq`` star.  Returns a new dark map.
    """
    dark = dark.copy()
    yang = acq['yang'] + dyang
    zang = acq['zang'] + dzang
    row, col = yagzag_to_pixels(yang, zang)
    row0 = int(row + 512)
    col0 = int(col + 512)
    dark[row0, col0] += mag_to_count_rate(acq['mag'] + dmag)

    return dark
Ejemplo n.º 5
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
Ejemplo n.º 6
0
def add_spoiler(stars, acq, dyang, dzang, dmag, mag_err=0.05):
    """
    For testing, add a spoiler stars at the specified delta location
    and mag relative to an ``acq`` star.  Returns a new stars table.
    """
    stars = stars.copy()
    ok = stars['id'] == acq['id']
    stars.add_row(stars[ok][0])
    star = stars[-1]
    star['id'] = -star['id']
    star['yang'] = acq['yang'] + dyang
    star['zang'] = acq['zang'] + dzang
    star['mag'] = acq['mag'] + dmag
    star['mag_err'] = mag_err
    star['MAG_ACA'] = star['mag']
    row, col = yagzag_to_pixels(star['yang'], star['zang'])
    star['row'] = row
    star['col'] = col
    star['CLASS'] = 0

    return stars
Ejemplo n.º 7
0
def plot_crs_visualization(obsid, plot_dir, crs=None, factor=20, save=False, on_the_fly=False):
    """
    Plot visualization of OBC centroid residuals with respect to ground (obc)
    aspect solution for science (ER) observations in the yang/zang plain.

    :param obsid: obsid
    :param crs: dictionary with keys 'ground' and 'obc'. Dictionary values
                are dictionaries keyed by slot number containing corresponding
                CentroidResiduals objects. If ground or obc centroid residuals
                cannot be computed for a given slot, the value is None.
    :param on_the_fly: default False, if True then ignore param crs and calculate
                       centroid residuals for the requested obsid
    """

    # catalog
    cat = get_starcat(obsid)

    # keep only BOT and GUI entries
    ok = (cat['type'] == 'BOT') | (cat['type'] == 'GUI')
    cat = cat[ok]
    cat['idx'] = cat['slot']  # so that the plot is numbered by slot

    # attitude
    att = get_att(obsid)

    # stars
    cols = ['RA_PMCORR', 'DEC_PMCORR', 'MAG_ACA', 'MAG_ACA_ERR',
            'CLASS', 'ASPQ1', 'ASPQ2', 'ASPQ3', 'VAR', 'POS_ERR']
    stars = Table(names=cols)
    for star in cat:
        row = []
        s = get_star(star['id'])
        for col in cols:
            row.append(s[col])
        stars.add_row(row)

    fig = plot_stars(att, cat, stars)
    cs = ['orange', 'forestgreen', 'steelblue', 'maroon', 'gray']

    if on_the_fly:
        crs = get_crs_per_obsid(obsid)
    else:
        if crs is None:
            raise ValueError('Need to provide crs if on_the_fly is False')

    if obsid > 40000:  # ERs
        crs_ref = crs['obc']
    else:
        crs_ref = crs['ground']

    ax = fig.axes[0]

    for slot in cat['slot']:
        ok = cat['slot'] == slot
        yag = cat['yang'][ok]
        zag = cat['zang'][ok]
        yp, zp = yagzag_to_pixels(yag, zag)
        # 1 px -> factor px; 5 arcsec = 5 * factor arcsec
        try:
            """
            Minus sign for y-coord to reflect sign flip in the pixel
            to yag conversion and yag scale going from positive to negative
            """
            yy = yp - crs_ref[slot].dyags * factor
            zz = zp + crs_ref[slot].dzags * factor
            ax.plot(yy, zz, alpha=0.3, marker=',', color=cs[slot - 3])
            ax.plot([-1000, -1020], [2700, 2700], color='k', lw=3)
            circle = plt.Circle((yp, zp), 5 * factor,
                                color='darkorange', fill=False)
            ax.add_artist(circle)
        except Exception:
            pass

    plt.text(-511, 530, "ring radius = 5 arcsec (scaled)", color='darkorange')

    if save:
        outroot = os.path.join(plot_dir, f'crs_vis_{obsid}')
        logger.info(f'Writing plot file {outroot}.png')
        plt.savefig(outroot + '.png')
        plt.close()

    return crs
Ejemplo n.º 8
0
    def process_monitors(self):
        """Process monitor window requests"""
        if self.monitors is None:
            return

        # Add columns for each of the three coordinate representations. The
        # original list input for monitors has been turned into a Table by the
        # Meta processing.
        monitors = self.monitors
        monitors['id'] = 0
        monitors['ra'] = 0.0
        monitors['dec'] = 0.0
        monitors['yang'] = 0.0
        monitors['zang'] = 0.0
        monitors['row'] = 0.0
        monitors['col'] = 0.0

        for monitor in monitors:
            if monitor['coord_type'] == MonCoord.RADEC:
                # RA, Dec
                monitor['ra'], monitor['dec'] = monitor['coord0'], monitor['coord1']
                monitor['yang'], monitor['zang'] = radec_to_yagzag(
                    monitor['ra'], monitor['dec'], self.att)
                monitor['row'], monitor['col'] = yagzag_to_pixels(
                    monitor['yang'], monitor['zang'], allow_bad=True)

            elif monitor['coord_type'] == MonCoord.ROWCOL:
                # Row, col
                monitor['row'], monitor['col'] = monitor['coord0'], monitor['coord1']
                monitor['yang'], monitor['zang'] = pixels_to_yagzag(
                    monitor['row'], monitor['col'], allow_bad=True, flight=True)
                monitor['ra'], monitor['dec'] = yagzag_to_radec(
                    monitor['yang'], monitor['zang'], self.att)

            elif monitor['coord_type'] == MonCoord.YAGZAG:
                # Yag, zag
                monitor['yang'], monitor['zang'] = monitor['coord0'], monitor['coord1']
                monitor['row'], monitor['col'] = yagzag_to_pixels(
                    monitor['yang'], monitor['zang'], allow_bad=True)
                monitor['ra'], monitor['dec'] = yagzag_to_radec(
                    monitor['yang'], monitor['zang'], self.att)

        # Process bona fide monitor windows according to function
        mon_id = 1000
        for monitor in self.monitors:
            if monitor['function'] in (MonFunc.GUIDE, MonFunc.MON_TRACK):
                # Try to get star at MON position
                dist = np.linalg.norm([self.stars['yang'] - monitor['yang'],
                                       self.stars['zang'] - monitor['zang']], axis=0)
                idx = np.argmin(dist)
                if dist[idx] < 2.0:
                    star = self.stars[idx]
                    monitor['id'] = star['id']
                    monitor['mag'] = star['mag']
                elif monitor['function'] == MonFunc.GUIDE:
                    raise BadMonitorError('no acceptable AGASC star within '
                                          '2 arcsec of monitor position')

            if monitor['function'] in (MonFunc.MON_FIXED, MonFunc.MON_TRACK):
                if monitor['id'] == 0:
                    monitor['id'] = mon_id
                    mon_id += 1

                # Make a stub row for a MON entry using zero everywhere. This
                # also works for str (giving '0').
                mon = {col.name: col.dtype.type(0) for col in self.itercols()}
                # These type codes get fixed later in merge_catalog
                mon['type'] = 'MFX' if monitor['function'] == MonFunc.MON_FIXED else 'MTR'
                mon['sz'] = '8x8'
                mon['dim'] = -999  # Set an obviously bad value for DTS, gets fixed later.
                mon['res'] = 0
                mon['halfw'] = 20
                mon['maxmag'] = ACA.monitor_maxmag
                for name in ('id', 'mag', 'yang', 'zang', 'row', 'col', 'ra', 'dec'):
                    mon[name] = monitor[name]

                # Finally add the MON as a row in table
                self.add_row(mon)

            elif monitor['function'] != MonFunc.GUIDE:
                raise ValueError(f'unexpected monitor function {monitor["function"]}')
Ejemplo n.º 9
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