Conversation
These changes are needed to account for the non-linear distortions due to the LAT's off-axis mirrors; otherwise only a single value of the "roll" can be treated in the existing framework. Introduces the idea of "focal_plane_template", a TOD member like focal_plane that will be used as the source to compute the (model-corrected) focal_plane. This changes the apply_pointing_model interface a bit but probably not in a way that users will notice.
mapcat's uuid7/datetime/path-rename migrations left this module behind: - ctime/start_time/stop_time were passed as raw unix-time floats into columns mapcat's "Swapping to datetime" migration made datetime, causing session.commit() to raise "SQLite DateTime type only accepts Python datetime and date objects" on the very first commit_depth1_tods call (outside the mapmaking try/except, so it hard-crashes the pipeline before a map is made). - commit_depth1_map passed time_path, a field DepthOneMapTable no longer has (split into start_time_path/mean_time_path/end_time_path). SQLModel silently drops unknown kwargs, so no time-map path was ever recorded. - commit_depth1_tods passed map_name into TODDepthOneTable, which has no such column anymore (association is via a many-to-many relationship through TODToMapTable). The kwarg was silently ignored, so TODs were never actually linked to their map. Verified against a real mapcat db migrated to head: commit succeeds, ctime/start_time/stop_time land as datetimes, mean_time_path is populated, and the TOD<->map relationship is linked in both directions. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Rh1TMcQhLGM8TyQZ947Rvt
…rument_centered_coordinates
…_centered_coordinates
…elobe cuts
This change was implemented with Claude Code (Opus 5.5).
It requires the companion so3g change that adds the det_left mode to
the projection engine (Assembly/ProjEng "det_left").
Summary
-------
Adds a way to build a pointing matrix in instrument-centered
coordinates of a source (Moon, Sun, or any name accepted by
planets.get_source_pos), and uses it to flag samples from per-wafer
sidelobe masks delivered as observation metadata.
Instrument-centered coordinates give the position of the source
relative to each detector, on axes fixed to the telescope structure
(the focal plane xi/eta axes at zero roll). Unlike the previous
object-centered frame (detector pointing relative to the source), a
sidelobe that is fixed to the instrument stays fixed in this frame.
Quaternion chain
----------------
For detector d and sample t:
q[d,t] = A[d] * X[t]
X[t] = Rz(roll[t]) * q_bore[t]^-1 * q_src[t] (sight.Q)
A[d] = Ry(pi/2) * Rz(roll0) * rotation_xieta(xi, eta, 0)^-1
* Rz(-roll0) (fp.quats)
- q_bore is the standard CelestialSightLine.az_el boresight
(qpoint * Rz(pi + roll)); q_src = rotation_lonlat(ra, dec) of the
source. Only q_bore^-1 * q_src enters, so the horizon->celestial
rotation cancels and the full-precision celestial pointing is used.
- Rz(roll[t]) removes the focal-plane roll so the axes are fixed to the
telescope structure; conjugating q_det by Rz(roll0) (circular mean
roll) expresses the detector offsets in the same axes. Exact for
constant roll; a warning is logged if roll varies by more than 0.1
deg from its mean.
- Detector quaternions are rebuilt with gamma = 0, since with q_det^-1
on the left gamma would rotate each detector's map about the
detector.
- Ry(pi/2) moves the detector from the pole to (lon, lat) = (0, 0).
Map conventions: each detector is at (0, 0); for small offsets
lat ~ eta_src - eta_det and lon ~ -(xi_src - xi_det), so in a standard
CAR geometry right = +az and up = +el (commanded). For LAT observations
past the zenith (el > 90) the axes follow the structure and are
therefore flipped relative to the sky.
Changes
-------
- coords/pmat.py:
- P has a det_left attribute, passed to Assembly.attach in _get_asm.
- P.for_tod(..., instrument_centered=<source name>) builds the
pointing above. Requires comps='T', geom given, rot=None and the
boresight in az/el/roll (boresight_equ is not supported); raises
ValueError otherwise.
- New get_instrument_centered(sight, fp, roll, source, timestamps,
site) returning the (sight, fp) pair for det_left projection.
- coords/planets.py: new get_source_quat(source, timestamps, site,
dt=600): source RA/Dec from get_source_pos on a grid of at most
dt seconds, RA unwrapped and linearly interpolated to each sample,
returned as rotation_lonlat quaternions.
- coords/sidelobes.py: rewritten. Masks are no longer fixed files per
object; they come from the observation metadata as aman.sidelobes
(Enmap metadata loader): .map is an enmap [nfields, ny, nx] of 0/1
in the instrument-centered coordinates of each object, labelled by
the "fields" LabelAxis (e.g. ['moon', 'sun']).
- get_cuts(aman, sidelobes="sidelobes", object_list=None,
rise_tol=1 deg) returns one RangesMatrix [ndet, nsamp] per object.
Clear errors if the metadata is missing or the map has no WCS.
- make_cuts: skips objects below rise_tol at the first, middle and
last sample; projects the mask with
P.for_tod(instrument_centered=...); uses unit responsivities so the
mask value is independent of the detector T response (the old
astype(int) could round 1*T < 1 down to 0); cuts samples where the
projected mask is non-zero.
- site_pipeline/make_ml_map.py: call sidelobes.get_cuts(obs) with the
new API. The --sun-mask/--moon-mask options are no longer used.
- tests/test_pmat.py: test_pmat_instrument_centered checks gamma
independence, that the angular distance from the origin equals the
detector-source distance on the sky, the orientation for the
boresight detector against the Moon's az/el, and that
from_map/to_map run; comps='TQU' is rejected.
Validation on a LAT observation (el = 120 deg, roll = 60 deg): the
spread across detectors at fixed time is ~0.6 x 0.5 deg (one wafer),
and the boresight orientation check agrees with skyfield's Moon az/el
to ~1e-4 at all tested samples.
WARNING
-------
This implementation can ONLY be used to go from a mask to flagged
samples, "T only". It CANNOT be used to make maps in
instrument-centered coordinates: polarization is not supported
(gamma is set to 0 and only comps='T' is allowed), and mapmaking in
these coordinates has not been designed or validated. That will need a
future implementation.
Known open point: the direction in which detector offsets are rotated
by the roll (+roll vs -roll) has not been verified independently; for a
~1 deg LAT wafer the effect is at most ~1 deg.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Member
Author
|
this will fail tests without the so3g PR, so we need to merge that first |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
This change was implemented with Claude Code (Opus 5.5).
It requires the companion so3g PR that adds the det_left mode to
the projection engine (Assembly/ProjEng "det_left").
Summary
Adds a way to build a pointing matrix in instrument-centered
coordinates of a source (Moon, Sun, or any name accepted by
planets.get_source_pos), and uses it to flag samples from per-wafer
sidelobe masks delivered as observation metadata.
Instrument-centered coordinates give the position of the source
relative to each detector, on axes fixed to the telescope structure
(the focal plane xi/eta axes at zero roll). Unlike the previous
object-centered frame (detector pointing relative to the source), a
sidelobe that is fixed to the instrument stays fixed in this frame.
Quaternion chain
For detector d and sample t:
(qpoint * Rz(pi + roll)); q_src = rotation_lonlat(ra, dec) of the
source. Only q_bore^-1 * q_src enters, so the horizon->celestial
rotation cancels and the full-precision celestial pointing is used.
telescope structure; conjugating q_det by Rz(roll0) (circular mean
roll) expresses the detector offsets in the same axes. Exact for
constant roll; a warning is logged if roll varies by more than 0.1
deg from its mean.
on the left gamma would rotate each detector's map about the
detector.
Map conventions: each detector is at (0, 0); for small offsets
lat ~ eta_src - eta_det and lon ~ -(xi_src - xi_det), so in a standard
CAR geometry right = +az and up = +el (commanded). For LAT observations
past the zenith (el > 90) the axes follow the structure and are
therefore flipped relative to the sky.
Changes
pointing above. Requires comps='T', geom given, rot=None and the
boresight in az/el/roll (boresight_equ is not supported); raises
ValueError otherwise.
site) returning the (sight, fp) pair for det_left projection.
dt=600): source RA/Dec from get_source_pos on a grid of at most
dt seconds, RA unwrapped and linearly interpolated to each sample,
returned as rotation_lonlat quaternions.
object; they come from the observation metadata as aman.sidelobes
(Enmap metadata loader): .map is an enmap [nfields, ny, nx] of 0/1
in the instrument-centered coordinates of each object, labelled by
the "fields" LabelAxis (e.g. ['moon', 'sun']).
rise_tol=1 deg) returns one RangesMatrix [ndet, nsamp] per object.
Clear errors if the metadata is missing or the map has no WCS.
last sample; projects the mask with
P.for_tod(instrument_centered=...); uses unit responsivities so the
mask value is independent of the detector T response (the old
astype(int) could round 1*T < 1 down to 0); cuts samples where the
projected mask is non-zero.
new API. The --sun-mask/--moon-mask options are no longer used.
independence, that the angular distance from the origin equals the
detector-source distance on the sky, the orientation for the
boresight detector against the Moon's az/el, and that
from_map/to_map run; comps='TQU' is rejected.
Validation on a LAT observation (el = 120 deg, roll = 60 deg): the
spread across detectors at fixed time is ~0.6 x 0.5 deg (one wafer),
and the boresight orientation check agrees with skyfield's Moon az/el
to ~1e-4 at all tested samples.
WARNING
This implementation can ONLY be used to go from a mask to flagged
samples, "T only". It CANNOT be used to make maps in
instrument-centered coordinates: polarization is not supported
(gamma is set to 0 and only comps='T' is allowed), and mapmaking in
these coordinates has not been designed or validated. That will need a
future implementation.
Known open point: the direction in which detector offsets are rotated
by the roll (+roll vs -roll) has not been verified independently; for a
~1 deg LAT wafer the effect is at most ~1 deg.
Co-Authored-By: Claude Opus 5.5 noreply@anthropic.com