Example #1
0
def calc_pcad_from_targ(ra_targ,
                        dec_targ,
                        roll_targ,
                        y_off,
                        z_off,
                        si_align=ODB_SI_ALIGN):
    """
    Calculates required Y and Z offsets (deg) required from a target to
    arrive at the desired PCAD pointing.

    :param ra_targ: RA of science target from OR/Obscat
    :param dec_targ: Dec of science target from OR/Obscat
    :param roll_targ: Roll of science target attitude
    :param y_off: Y offset (deg, sign per OR-list convention)
    :param z_off: Z offset (deg,sign per OR-list convention)
    :param si_align: SI ALIGN matrix (default=ODB_SI_ALIGN)

    :rtype: q_pcad (Quat)
    """
    q_si_align = Quat(si_align)
    q_targ = Quat([ra_targ, dec_targ, roll_targ])
    q_off = Quat([y_off, z_off, 0])
    q_pcad = q_targ * q_off.inv() * q_si_align.inv()

    return q_pcad
Example #2
0
def pcad_point(ra, dec, roll, y_offset, z_offset):
    # offsets are in arcmin, convert to degrees
    offset = Quat([y_offset / 60., z_offset / 60., 0])
    q_targ = Quat([ra, dec, roll])
    q_hrma = q_targ * offset.inv()
    q_pnt = Quat(np.dot(q_hrma.transform, ODB_SI_ALIGN))
    return q_pnt.ra, q_pnt.dec
Example #3
0
def calc_pcad_from_targ(ra_targ, dec_targ, roll_targ, y_off, z_off, si_align=ODB_SI_ALIGN):
    """
    Calculates required Y and Z offsets (deg) required from a target to
    arrive at the desired PCAD pointing.

    :param ra_targ: RA of science target from OR/Obscat
    :param dec_targ: Dec of science target from OR/Obscat
    :param roll_targ: Roll of science target attitude
    :param y_off: Y offset (deg, sign per OR-list convention)
    :param z_off: Z offset (deg,sign per OR-list convention)
    :param si_align: SI ALIGN matrix (default=ODB_SI_ALIGN)

    :rtype: q_pcad (Quat)
    """
    q_si_align = Quat(si_align)
    q_targ = Quat([ra_targ, dec_targ, roll_targ])
    q_off = Quat([y_off, z_off, 0])
    q_pcad = q_targ * q_off.inv() * q_si_align.inv()

    return q_pcad
Example #4
0
def calc_aca_from_targ(targ, y_off, z_off, si_align=None):
    """
    Calculate the PCAD (ACA) pointing attitude from target attitude and
    Y,Z offset (per OR-list).

    :param targ: science target from OR/Obscat (Quat-compliant object)
    :param y_off: Y offset (deg, sign per OR-list convention)
    :param z_off: Z offset (deg, sign per OR-list convention)
    :param si_align: SI ALIGN matrix (default=ODB_SI_ALIGN)

    :rtype: q_aca (Quat)
    """
    if si_align is None:
        si_align = ODB_SI_ALIGN

    q_si_align = Quat(si_align)
    q_targ = Quat(targ)
    q_off = Quat([y_off, z_off, 0])
    q_aca = q_targ * q_off.inv() * q_si_align.inv()

    return q_aca
Example #5
0
def calc_aca_from_targ(targ, y_off, z_off, si_align=None):
    """
    Calculate the PCAD (ACA) pointing attitude from target attitude and
    Y,Z offset (per OR-list).

    :param targ: science target from OR/Obscat (Quat-compliant object)
    :param y_off: Y offset (deg, sign per OR-list convention)
    :param z_off: Z offset (deg, sign per OR-list convention)
    :param si_align: SI ALIGN matrix (default=ODB_SI_ALIGN)

    :rtype: q_aca (Quat)
    """
    if si_align is None:
        si_align = ODB_SI_ALIGN

    q_si_align = Quat(si_align)
    q_targ = Quat(targ)
    q_off = Quat([y_off, z_off, 0])
    q_aca = q_targ * q_off.inv() * q_si_align.inv()

    return q_aca
Example #6
0
def test_si_align_with_data():
    """
    Test an actual observation planned with a target offset in Y and Z:

    Strategy: use a real observation with non-trivial and non-identical
    OR-specified target offsets.  This provides:

      - Target RA, Dec (science target, not OBC target)
      - Target offset DY, DZ
      - PCAD pointing attitude that gets uplinked via command loads.
        The PCAD attitude is at the target attitude with the ODB_SI_ALIGN
        transform applied and the specified target offset applied.

    The aimpoint alignment offset from http://cxc.cfa.harvard.edu/mta/ASPECT/aimpoint_mon/
    is defined as an ADDITIVE offset to the Target offset.  Specifically, define:

     DY_total = DY_target + dy_align
     DZ_total = DZ_target + dz_align

    These DY_total, DZ_total offsets *could* be used in place of the OR-specified values
    in the planning process to correct for aimpoint drift.  But in fact the aimpoint drift
    is being corrected via the ODB_SI_ALIGN matrix.

    A function calc_si_align(dy_align, dz_align) computes the new SI_ALIGN matrix for
    given dy_align, dz_align values.

    To test that this is giving correct values (and the signs in particular), *pretend*
    that the required aimpoint alignment offset is exactly the Target offset from obsid
    13928.  In this case we compute a new SI_ALIGN with that offset and then zero out
    the target offset.  By the additive property above the final PCAD attitude must be the
    same as the originally scheduled (and uplinked) attitude.

    ----------------------

    Observation request:
     ID=13928,TARGET=(191.321250,27.125556,{Haro 9}),DURATION=(17000.000000),
     PRIORITY=9,SI=ACIS-S,GRATING=NONE,SI_MODE=TE_0045A,ACA_MODE=DEFAULT,
     TARGET_OFFSET=(0.002500,-0.004167),
     DITHER=(ON,0.002222,0.360000,0.000000,0.002222,0.509100,0.000000),
     SEGMENT=(1,15300.000000),PRECEDING=(13632),MIN_ACQ=1,MIN_GUIDE=1

    PCAD:  As-planned pointing from starcheck
      Q1,Q2,Q3,Q4: -0.18142595  -0.37811633  -0.89077416  0.17502588
    """
    # Attitude quaternion for the as-run PCAD attitude
    q_pcad = Quat([-0.18142595, -0.37811633, -0.89077416, 0.17502588])

    # Target coordinates and quaternion, using the PCAD roll
    ra_targ, dec_targ = 191.321250, 27.125556
    q_targ = Quat([ra_targ, dec_targ, q_pcad.roll])

    # Offsets from OR (Target DY, DZ) in degrees
    dy_offset, dz_offset = 0.002500, -0.004167

    # Sanity check that offset between as-planned PCAD and target are as expected.  This
    # uses the baseline ODB_SI_ALIGN.  The calc_offsets() function has been independently
    # validated for planning purposes.
    dy_nom_align, dz_nom_align = calc_offsets(ra_targ, dec_targ, q_pcad.ra,
                                              q_pcad.dec, q_pcad.roll)
    assert np.allclose(dy_nom_align, dy_offset, atol=3e-6)  # 0.01 arcsec
    assert np.allclose(dz_nom_align, dz_offset, atol=3e-6)

    # Test calc_pcad_from_targ() for baseline SI_ALIGN
    q_pcad_calc = calc_pcad_from_targ(ra_targ, dec_targ, q_pcad.roll,
                                      dy_offset, dz_offset)
    dq = q_pcad.inv() * q_pcad_calc
    assert np.allclose(dq.q[0] * 2, 0, atol=36 * ARCSEC2RAD)
    assert np.allclose(dq.q[1] * 2, 0, atol=0.01 * ARCSEC2RAD)
    assert np.allclose(dq.q[2] * 2, 0, atol=0.01 * ARCSEC2RAD)

    # Compute PCAD attitude using a new SI ALIGN values based on OR-list TARGET_OFFSET
    # values and with no target offset applied.  This must match the as-planned
    # attitude using the baseline ODB_SI_ALIGN and WITH a target offset applied.
    si_align = calc_si_align(dy_offset, dz_offset, check_consistency=True)
    q_pcad_align = q_targ * Quat(si_align).inv()

    dy_align, dz_align = calc_offsets(ra_targ, dec_targ,
                                      *q_pcad_align.equatorial)
    dq = q_pcad.inv() * q_pcad_align

    # Check delta quaternion (error) is less than 0.01 arcsec
    # in pitch and yaw, 36 arcsec in roll.
    assert np.allclose(dq.q[0] * 2, 0, atol=36 * ARCSEC2RAD)
    assert np.allclose(dq.q[1] * 2, 0, atol=0.01 * ARCSEC2RAD)
    assert np.allclose(dq.q[2] * 2, 0, atol=0.01 * ARCSEC2RAD)

    # Check offsets match to better than 0.01 arcsec
    assert np.allclose(dy_align, dy_offset, atol=3e-6)
    assert np.allclose(dz_align, dz_offset, atol=3e-6)
Example #7
0
def test_si_align_with_data():
    """
    Test an actual observation planned with a target offset in Y and Z:

    Strategy: use a real observation with non-trivial and non-identical
    OR-specified target offsets.  This provides:

      - Target RA, Dec (science target, not OBC target)
      - Target offset DY, DZ
      - PCAD pointing attitude that gets uplinked via command loads.
        The PCAD attitude is at the target attitude with the ODB_SI_ALIGN
        transform applied and the specified target offset applied.

    The aimpoint alignment offset from http://cxc.cfa.harvard.edu/mta/ASPECT/aimpoint_mon/
    is defined as an ADDITIVE offset to the Target offset.  Specifically, define:

     DY_total = DY_target + dy_align
     DZ_total = DZ_target + dz_align

    These DY_total, DZ_total offsets *could* be used in place of the OR-specified values
    in the planning process to correct for aimpoint drift.  But in fact the aimpoint drift
    is being corrected via the ODB_SI_ALIGN matrix.

    A function calc_si_align(dy_align, dz_align) computes the new SI_ALIGN matrix for
    given dy_align, dz_align values.

    To test that this is giving correct values (and the signs in particular), *pretend*
    that the required aimpoint alignment offset is exactly the Target offset from obsid
    13928.  In this case we compute a new SI_ALIGN with that offset and then zero out
    the target offset.  By the additive property above the final PCAD attitude must be the
    same as the originally scheduled (and uplinked) attitude.

    ----------------------

    Observation request:
     ID=13928,TARGET=(191.321250,27.125556,{Haro 9}),DURATION=(17000.000000),
     PRIORITY=9,SI=ACIS-S,GRATING=NONE,SI_MODE=TE_0045A,ACA_MODE=DEFAULT,
     TARGET_OFFSET=(0.002500,-0.004167),
     DITHER=(ON,0.002222,0.360000,0.000000,0.002222,0.509100,0.000000),
     SEGMENT=(1,15300.000000),PRECEDING=(13632),MIN_ACQ=1,MIN_GUIDE=1

    PCAD:  As-planned pointing from starcheck
      Q1,Q2,Q3,Q4: -0.18142595  -0.37811633  -0.89077416  0.17502588
    """
    # Attitude quaternion for the as-run PCAD attitude
    q_pcad = Quat([-0.18142595, -0.37811633, -0.89077416, 0.17502588])

    # Target coordinates and quaternion, using the PCAD roll
    ra_targ, dec_targ = 191.321250, 27.125556
    q_targ = Quat([ra_targ, dec_targ, q_pcad.roll])

    # Offsets from OR (Target DY, DZ) in degrees
    dy_offset, dz_offset = 0.002500, -0.004167

    # Sanity check that offset between as-planned PCAD and target are as expected.  This
    # uses the baseline ODB_SI_ALIGN.  The calc_offsets() function has been independently
    # validated for planning purposes.
    dy_nom_align, dz_nom_align = calc_offsets(ra_targ, dec_targ,
                                              q_pcad.ra, q_pcad.dec, q_pcad.roll)
    assert np.allclose(dy_nom_align, dy_offset, atol=3e-6)  # 0.01 arcsec
    assert np.allclose(dz_nom_align, dz_offset, atol=3e-6)

    # Test calc_pcad_from_targ() for baseline SI_ALIGN
    q_pcad_calc = calc_pcad_from_targ(ra_targ, dec_targ, q_pcad.roll, dy_offset, dz_offset)
    dq = q_pcad.inv() * q_pcad_calc
    assert np.allclose(dq.q[0] * 2, 0, atol=36 * ARCSEC2RAD)
    assert np.allclose(dq.q[1] * 2, 0, atol=0.01 * ARCSEC2RAD)
    assert np.allclose(dq.q[2] * 2, 0, atol=0.01 * ARCSEC2RAD)

    # Compute PCAD attitude using a new SI ALIGN values based on OR-list TARGET_OFFSET
    # values and with no target offset applied.  This must match the as-planned
    # attitude using the baseline ODB_SI_ALIGN and WITH a target offset applied.
    si_align = calc_si_align(dy_offset, dz_offset, check_consistency=True)
    q_pcad_align = q_targ * Quat(si_align).inv()

    dy_align, dz_align = calc_offsets(ra_targ, dec_targ, *q_pcad_align.equatorial)
    dq = q_pcad.inv() * q_pcad_align

    # Check delta quaternion (error) is less than 0.01 arcsec
    # in pitch and yaw, 36 arcsec in roll.
    assert np.allclose(dq.q[0] * 2, 0, atol=36 * ARCSEC2RAD)
    assert np.allclose(dq.q[1] * 2, 0, atol=0.01 * ARCSEC2RAD)
    assert np.allclose(dq.q[2] * 2, 0, atol=0.01 * ARCSEC2RAD)

    # Check offsets match to better than 0.01 arcsec
    assert np.allclose(dy_align, dy_offset, atol=3e-6)
    assert np.allclose(dz_align, dz_offset, atol=3e-6)
Example #8
0
                                0] >= last_frame + pred_window or j == len(
                                    series_ds[i]) - 1:
                            last_frame_idx[i] = j
                            break

                        # Calculate proximity of quaternions
                        mean[entry[0] - first_frame] = [
                            entry[2], entry[3], entry[4], entry[5]
                        ]  # w,x,y,z
                        q1 = Quat(
                            Quaternion.normalize(
                                [entry[3], entry[4], entry[5],
                                 entry[2]]))  # w,x,y,z -> x,y,z,w
                        q2 = list(value[entry[0] - first_frame])  # w,x,y,z
                        q2 = [q2[1], q2[2], q2[3], q2[0]]  # w,x,y,z -> x,y,z,w
                        new_q = Quat(list(q2)).__mul__(q1.inv()).q  # x,y,z,w

                        # Less than 30 degree
                        if abs(2 * np.arccos(new_q[3])) < (np.pi / 6):
                            num += 1

                        tot += 1

                    # threshold
                    if tot is not 0 and float(num) / float(tot) > 0.9:
                        mean = interpolate(mean)

                        C_mean_idx[key].append(i)
                        for i in range(len(value)):
                            C_mean[key][i] = averageQuaternions(
                                np.array([value[i], mean[i]]))