Skip to content

error

Script for calculating HWFE and pointing error. Also tells you how to move whatever elements are included.

get_hwfe(data, get_transform, add_err=False)

Get the HWFE errors based on the mirror and receiver positions. This calculation is from Parshely et al.

Parameters:

Name Type Description Default
data DatasetReference

This dataset must at least contain the primary mirror.

required
get_transform Callable[[NDArray[float64], NDArray[float64]], tuple[NDArray[float64], NDArray[float64]]]

Function that takes in two point clouds and returns an affine matrix and a shift to align them.

required
add_err bool

If True add the error term to the data.

False

Returns:

Name Type Description
hwfe float

The HWFE in um-rms.

Source code in lat_alignment/error.py
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
def get_hwfe(
    data: DatasetReference,
    get_transform: Callable[
        [NDArray[np.float64], NDArray[np.float64]],
        tuple[NDArray[np.float64], NDArray[np.float64]],
    ],
    add_err: bool = False,
) -> float:
    """
    Get the HWFE errors based on the mirror and receiver positions.
    This calculation is from Parshely et al.

    Parameters
    ----------
    data : DatasetReference
        This dataset must at least contain the primary mirror.
    get_transform : Callable[[NDArray[np.float64], NDArray[np.float64]], tuple[NDArray[np.float64], NDArray[np.float64]]]
        Function that takes in two point clouds and returns an affine matrix and a shift to align them.
    add_err : bool, default: False
        If True add the error term to the data.

    Returns
    -------
    hwfe : float
        The HWFE in um-rms.
    """
    # Put everything in M1 coordinates
    data_m1 = data.copy()
    for element in elements:
        dat = np.array(data_m1.elements[element], np.float64)
        if add_err:
            dat += np.nan_to_num(data_m1.errors[element])
        data_m1[element] = coord_transform(dat, "opt_global", "opt_primary")
        data_m1[f"{element}_ref"] = coord_transform(
            data_m1.reference[element], "opt_global", "opt_primary"
        )

    # Transform for M1 perfect
    aff_m1, sft_m1 = get_transform(
        data_m1.elements["primary"],
        data_m1.reference["primary"],
    )

    hwfe = 0
    for element in hwfe_factors.keys():
        src = data_m1.elements[element]
        dst = data_m1.reference[element]

        # Apply the transform to align M1
        src = apply_transform(src, aff_m1, sft_m1)

        # Get the new transform
        aff, sft = get_transform(src, dst)
        _, _, rot = decompose_affine(aff)
        rot = decompose_rotation(rot)

        # compute HWFE
        vals = np.hstack([sft * mm_to_um, rot * rad_to_arcsec]).ravel()
        hwfe += float(np.sum((np.array(hwfe_factors[element]) * vals) ** 2))
    return np.sqrt(hwfe)

get_pointing_error(data, get_transform, add_err=False, thresh=0.01)

Get the pointing error based on the mirror and receiver positions.

Parameters:

Name Type Description Default
data DatasetReference

Dataset of measured reference points.

required
get_transform Callable[[NDArray[float64], NDArray[float64]], tuple[NDArray[float64], NDArray[float64]]]

Function that takes in two point clouds and returns an affine matrix and a shift to align them.

required
add_err bool

If True add the error term to the data.

False
thresh float

The threshold in arcsecs to discard a rotation.

.01

Returns:

Name Type Description
pe float

The pointing error in arcsecs.

Source code in lat_alignment/error.py
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
def get_pointing_error(
    data: DatasetReference,
    get_transform: Callable[
        [NDArray[np.float64], NDArray[np.float64]],
        tuple[NDArray[np.float64], NDArray[np.float64]],
    ],
    add_err: bool = False,
    thresh: float = 0.01,
) -> float:
    """
    Get the pointing error based on the mirror and receiver positions.

    Parameters
    ----------
    data : DatasetReference
        Dataset of measured reference points.
    get_transform : Callable[[NDArray[np.float64], NDArray[np.float64]], tuple[NDArray[np.float64], NDArray[np.float64]]]
        Function that takes in two point clouds and returns an affine matrix and a shift to align them.
    add_err : bool, default: False
        If True add the error term to the data.
    thresh : float, default: .01
        The threshold in arcsecs to discard a rotation.

    Returns
    -------
    pe : float
        The pointing error in arcsecs.
    """
    thresh = np.deg2rad(thresh / 3600)
    rots = np.zeros((2, 3))
    # Get rotations
    for i, (element, factor) in enumerate([("primary", 1), ("secondary", 2)]):
        src = np.array(data.elements[element])
        if add_err:
            src += np.nan_to_num(data.errors[element])
        # Put things in the local coords
        src = coord_transform(src, "opt_global", f"opt_{element}")
        dst = coord_transform(
            np.array(data.reference[element]), "opt_global", f"opt_{element}"
        )
        # Get rotation
        aff, _ = get_transform(src, dst)
        *_, rot = decompose_affine(aff)
        rot = decompose_rotation(rot)
        rot[abs(rot) < thresh] = 0  # Help prevent float errors
        rot[-1] = 0  # clocking doesn't matter
        # Put into global coords
        aff = R.from_euler("xyz", rot, False).as_matrix()
        aff, _ = affine_basis_transform(
            aff, np.zeros(3, np.float64), f"opt_{element}", "opt_global"
        )
        *_, rot = decompose_affine(aff)
        rot = decompose_rotation(rot)
        rot[abs(rot) < thresh] = 0
        rots[i] = rot * factor
    tot_rot = np.linalg.norm(np.sum(rots, 0))
    return 3600 * np.rad2deg(tot_rot)