-
Notifications
You must be signed in to change notification settings - Fork 19
LAT pointing model and ffp updates #1216
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Changes from all commits
c0674dc
5ef5dca
cfbdc46
7699a8d
e9496ea
b857da6
69529f4
fc95de2
c07083e
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -72,28 +72,33 @@ def apply_pointing_model(tod, pointing_model=None, ancil=None, | |
AxisManager: the corrected boresight. | ||
|
||
""" | ||
if pointing_model is None and 'pointing_model' not in tod: | ||
logger.warning('No pointing_model found -- applying basic model.') | ||
assert wrap in (None, 'boresight'), \ | ||
'When using naive pointing model, wrap=... not supported' | ||
if pointing_model is None and "pointing_model" not in tod: | ||
logger.warning("No pointing_model found -- applying basic model.") | ||
assert wrap in ( | ||
None, | ||
"boresight", | ||
), "When using naive pointing model, wrap=... not supported" | ||
return apply_basic_pointing_model(tod) | ||
|
||
pointing_model = _valid_arg(pointing_model, 'pointing_model', | ||
src=tod) | ||
ancil = _valid_arg(ancil, 'ancil', src=tod) | ||
pointing_model = _valid_arg(pointing_model, "pointing_model", src=tod) | ||
ancil = _valid_arg(ancil, "ancil", src=tod) | ||
|
||
# Encoder values, to radians. | ||
vers = pointing_model['version'] | ||
tel_type = vers.split('_')[0] | ||
if tel_type == 'sat': | ||
if pointing_model is None: | ||
raise ValueError("No pointing_model specified") | ||
if "version" not in pointing_model: | ||
raise ValueError("Pointing model version not specified") | ||
vers = pointing_model["version"] | ||
tel_type = vers.split("_")[0] | ||
if tel_type == "sat": | ||
boresight = apply_pointing_model_sat(vers, pointing_model, tod, ancil) | ||
elif tel_type == 'lat': | ||
boresight = apply_pointing_model_lat(vers, pointing_model, tod, ancil) | ||
elif tel_type == "lat": | ||
boresight = apply_pointing_model_lat(vers, pointing_model, ancil) | ||
else: | ||
raise ValueError(f'Unimplemented pointing model "{vers}"') | ||
|
||
if wrap is None: | ||
wrap = 'boresight' | ||
wrap = "boresight" | ||
if wrap is not False: | ||
if wrap in tod._fields: | ||
del tod[wrap] | ||
|
@@ -115,8 +120,74 @@ def apply_pointing_model_sat(vers, params, tod, ancil): | |
raise ValueError(f'Unimplemented pointing model "{vers}"') | ||
|
||
|
||
def apply_pointing_model_lat(vers, tod, pointing_model, ancil): | ||
raise ValueError(f'Unimplemented pointing model "{vers}"') | ||
def apply_pointing_model_lat(vers, params, ancil): | ||
az, el, roll = _get_lat_enc_radians(ancil) | ||
|
||
if vers == 'lat_naive': | ||
return _new_boresight(ancil.samps, az=az, el=el, roll=roll) | ||
|
||
if vers == "lat_v1": | ||
az1, el1, roll1 = model_lat_v1(params, az, el, roll) | ||
return _new_boresight(ancil.samps, az=az1, el=el1, roll=roll1) | ||
|
||
else: | ||
raise ValueError(f'Unimplemented pointing model "{vers}"') | ||
|
||
|
||
# | ||
# LAT model(s) | ||
# | ||
def model_lat_v1(params, az, el, roll): | ||
"""Applies pointing model to (az, el, roll). | ||
|
||
Args: | ||
params: AxisManager (or dict) of pointing parameters. | ||
az, el, roll: naive horizon coordinates, in radians, of the | ||
boresight. | ||
|
||
The implemented model parameters are: | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. On naming of the parameters ... here are some things that I like about the SAT model naming:
I'm on board with "cr" for the corotator. I left some comments in the model about how the various (xi, eta) pairs should be described, and whether or not that means you apply (xi, eta) or (-xi, -eta) in the model... it bears a bit of thought, in case we ever want to describe this carefully to someone. The specific problem I have with the current param descriptions is that "the offset between A and B" is ambiguous to me ... it doesn't have a specific direction attached to it (whereas "the position of A relative to B" does). |
||
|
||
- rx_{xi, eta}_offset: The offset between the LATR center and the axis | ||
of the corotator. | ||
- cr_offset: The corotator encoder offset. | ||
- el_{xi, eta}_offset: The offset between the elevation exis and the | ||
corotator axis. | ||
- mir_{xi, eta}_offset: The offset between the mirror's axis and the elevation | ||
axis (ie: a tilt in the mirrors). | ||
- az_offset: The azimuth encoder offset. | ||
- el_offset: The elevation encoder offset. | ||
""" | ||
_p = dict(param_defaults['lat_v1']) | ||
if isinstance(params, dict): | ||
_p.update(params) | ||
else: | ||
_p.update({k: params[k] for k in params._fields.keys()}) | ||
params, _p = _p, None | ||
|
||
for k, v in params.items(): | ||
if k == 'version': | ||
continue | ||
if k not in param_defaults['lat_v1'] and v != 0.: | ||
raise ValueError(f'Handling of model param "{k}" is not implemented.') | ||
|
||
cr = el - roll - np.deg2rad(60) | ||
q_enc = quat.rotation_lonlat( | ||
-1 * (az.copy() + params["az_offset"]), el.copy() + params["el_offset"] | ||
) | ||
q_mir = quat.rotation_xieta(params["mir_xi_offset"], params["mir_eta_offset"]) | ||
q_el_roll = quat.euler(2, el.copy() + params["el_offset"] - np.deg2rad(60)) | ||
q_tel = quat.rotation_xieta(params["el_xi_offset"], params["el_eta_offset"]) | ||
q_cr_roll = quat.euler(2, -1 * cr - params["cr_offset"]) | ||
q_rx = quat.rotation_xieta(params["rx_xi_offset"], params["rx_eta_offset"]) | ||
Comment on lines
+177
to
+181
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. So with regards to But more importantly we should decide whether we want to apply them as Consider the combo It's equivalent, mathematically. But the signs of the parameters change. So it's really a choice of how you want to be able to describe this, in words, in code comments and supporting writeups, going forward. I think it's nice to be able to say that second thing -- "xi, eta is a position in the focal plane where some thing happens". This is the rationale for the docstring re-wording above. But there is probably a reasonable opposite view. (In the current SAT parametrization I think these might be mixed ... fp_rot seems to the "position of" convention but fp_offset uses the "shift applied" convention. Something to consider for v2...) |
||
new_az, el, roll = ( | ||
quat.decompose_lonlat(q_enc * q_mir * q_el_roll * q_tel * q_cr_roll * q_rx) | ||
* np.array([-1, 1, 1])[..., None] | ||
) | ||
|
||
change = ((new_az - az) + np.pi) % (2 * np.pi) - np.pi | ||
az = az.copy() + change | ||
|
||
return az, el, roll | ||
|
||
|
||
# | ||
|
@@ -126,19 +197,6 @@ def apply_pointing_model_lat(vers, tod, pointing_model, ancil): | |
# sat_v1: you can expand v1, as long as new params don't do anything | ||
# if their value is zero (and that should be the registered default). | ||
|
||
defaults_sat_v1 = { | ||
'enc_offset_az': 0., | ||
'enc_offset_el': 0., | ||
'enc_offset_boresight': 0., | ||
'fp_offset_xi0': 0., | ||
'fp_offset_eta0': 0., | ||
'fp_rot_xi0': 0., | ||
'fp_rot_eta0': 0., | ||
'az_rot': 0., | ||
'base_tilt_cos': 0., | ||
'base_tilt_sin': 0., | ||
} | ||
|
||
def model_sat_v1(params, az, el, roll): | ||
"""Applies pointing model to (az, el, roll). | ||
|
||
|
@@ -161,7 +219,7 @@ def model_sat_v1(params, az, el, roll): | |
- az_rot: Dimensionless parameter describing a linear dependence of Az on El. | ||
|
||
""" | ||
_p = dict(defaults_sat_v1) | ||
_p = dict(param_defaults['sat_v1']) | ||
if isinstance(params, dict): | ||
_p.update(params) | ||
else: | ||
|
@@ -171,7 +229,7 @@ def model_sat_v1(params, az, el, roll): | |
for k, v in params.items(): | ||
if k == 'version': | ||
continue | ||
if k not in defaults_sat_v1 and v != 0.: | ||
if k not in param_defaults['sat_v1'] and v != 0.: | ||
raise ValueError(f'Handling of model param "{k}" is not implemented.') | ||
|
||
# Construct offsetted encoders. | ||
|
@@ -205,6 +263,31 @@ def model_sat_v1(params, az, el, roll): | |
|
||
|
||
# Support functions | ||
param_defaults={ | ||
'lat_v1' : { | ||
'az_offset': 0, | ||
'el_offset': 0, | ||
'cr_offset': 0, | ||
'el_xi_offset': 0, | ||
'el_eta_offset': 0, | ||
'rx_xi_offset': 0, | ||
'rx_eta_offset': 0, | ||
'mir_xi_offset': 0, | ||
'mir_eta_offset': 0, | ||
}, | ||
'sat_v1' : { | ||
'enc_offset_az': 0., | ||
'enc_offset_el': 0., | ||
'enc_offset_boresight': 0., | ||
'fp_offset_xi0': 0., | ||
'fp_offset_eta0': 0., | ||
'fp_rot_xi0': 0., | ||
'fp_rot_eta0': 0., | ||
'az_rot': 0., | ||
'base_tilt_cos': 0., | ||
'base_tilt_sin': 0. | ||
} | ||
} | ||
|
||
def _new_boresight(samps, az=None, el=None, roll=None): | ||
boresight = core.AxisManager(samps) | ||
|
@@ -219,6 +302,15 @@ def _get_sat_enc_radians(ancil): | |
ancil.el_enc * DEG, | ||
-ancil.boresight_enc * DEG) | ||
|
||
def _get_lat_enc_radians(ancil): | ||
az = ancil.az_enc * DEG | ||
el = ancil.el_enc * DEG | ||
if "roll_enc" in ancil: | ||
roll = ancil.roll_enc * DEG | ||
else: | ||
roll = (ancil.el_enc - ancil.corotator_enc - 60) * DEG | ||
return (az, el, roll) | ||
|
||
def get_base_tilt_q(c, s): | ||
"""Returns the quaternion rotation that applies base tilt, taking | ||
vectors in the platforms horizon coordinates to vectors in the | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
"r2" and "R2"?