Conversation
arahlin
force-pushed
the
faster-sidereal-time
branch
from
September 27, 2026 07:45
9a79b6c to
98d0506
Compare
arahlin
force-pushed
the
light-deflection
branch
from
September 27, 2026 07:45
f81f501 to
e4d8275
Compare
arahlin
force-pushed
the
faster-sidereal-time
branch
from
September 27, 2026 13:40
98d0506 to
0d79e23
Compare
arahlin
force-pushed
the
light-deflection
branch
from
September 27, 2026 13:40
e4d8275 to
aeaf63d
Compare
arahlin
added this pull request to stack #32
September 27, 2026 13:43
arahlin
removed this pull request from stack #32
September 27, 2026 15:43
arahlin
force-pushed
the
faster-sidereal-time
branch
from
September 27, 2026 15:44
0d79e23 to
0c6af84
Compare
arahlin
force-pushed
the
light-deflection
branch
from
September 27, 2026 15:44
aeaf63d to
3aba059
Compare
arahlin
force-pushed
the
faster-sidereal-time
branch
from
September 27, 2026 16:59
0c6af84 to
eee5e24
Compare
arahlin
force-pushed
the
light-deflection
branch
from
September 27, 2026 16:59
3aba059 to
00459de
Compare
arahlin
force-pushed
the
faster-sidereal-time
branch
from
September 27, 2026 17:11
eee5e24 to
e5ff09a
Compare
arahlin
force-pushed
the
light-deflection
branch
from
September 27, 2026 17:11
00459de to
bca1fc8
Compare
arahlin
force-pushed
the
faster-sidereal-time
branch
from
September 27, 2026 17:19
e5ff09a to
ad33d11
Compare
arahlin
force-pushed
the
light-deflection
branch
from
September 27, 2026 17:19
bca1fc8 to
41ef2ab
Compare
arahlin
force-pushed
the
faster-sidereal-time
branch
from
September 27, 2026 18:03
ad33d11 to
e5017c6
Compare
arahlin
force-pushed
the
light-deflection
branch
from
September 27, 2026 18:03
41ef2ab to
d2b3a07
Compare
arahlin
force-pushed
the
faster-sidereal-time
branch
from
September 27, 2026 18:14
e5017c6 to
1d14e05
Compare
arahlin
force-pushed
the
light-deflection
branch
from
September 27, 2026 18:14
d2b3a07 to
bacf0dd
Compare
arahlin
force-pushed
the
faster-sidereal-time
branch
from
September 27, 2026 18:21
1d14e05 to
830ded8
Compare
arahlin
force-pushed
the
light-deflection
branch
from
September 27, 2026 18:21
bacf0dd to
8eee92a
Compare
arahlin
force-pushed
the
faster-sidereal-time
branch
from
September 27, 2026 18:29
830ded8 to
6804334
Compare
arahlin
force-pushed
the
light-deflection
branch
from
September 27, 2026 18:29
8eee92a to
9bc8b82
Compare
arahlin
force-pushed
the
faster-sidereal-time
branch
from
September 27, 2026 18:40
6804334 to
8516cd6
Compare
arahlin
force-pushed
the
light-deflection
branch
from
September 27, 2026 18:40
9bc8b82 to
c099fa2
Compare
arahlin
force-pushed
the
faster-sidereal-time
branch
from
September 27, 2026 19:21
8516cd6 to
a3762e9
Compare
arahlin
force-pushed
the
light-deflection
branch
from
September 27, 2026 19:21
c099fa2 to
ca6336c
Compare
arahlin
force-pushed
the
faster-sidereal-time
branch
from
September 27, 2026 19:58
a3762e9 to
03640e6
Compare
arahlin
force-pushed
the
light-deflection
branch
from
September 27, 2026 19:58
ca6336c to
04e3e5e
Compare
arahlin
force-pushed
the
faster-sidereal-time
branch
from
September 27, 2026 20:24
03640e6 to
a42ea2d
Compare
arahlin
force-pushed
the
light-deflection
branch
from
September 27, 2026 20:24
04e3e5e to
f7344e3
Compare
arahlin
force-pushed
the
faster-sidereal-time
branch
from
September 27, 2026 20:41
a42ea2d to
829c185
Compare
arahlin
force-pushed
the
light-deflection
branch
from
September 27, 2026 20:41
f7344e3 to
04a3739
Compare
arahlin
force-pushed
the
faster-sidereal-time
branch
from
September 27, 2026 20:55
829c185 to
ff48a45
Compare
arahlin
force-pushed
the
light-deflection
branch
from
September 27, 2026 20:55
04a3739 to
d3309d6
Compare
arahlin
force-pushed
the
faster-sidereal-time
branch
from
September 27, 2026 21:14
ff48a45 to
b975315
Compare
arahlin
force-pushed
the
light-deflection
branch
from
September 27, 2026 21:14
d3309d6 to
2407890
Compare
arahlin
force-pushed
the
faster-sidereal-time
branch
from
September 28, 2026 01:57
b975315 to
7f3454e
Compare
arahlin
force-pushed
the
light-deflection
branch
from
September 28, 2026 01:57
2407890 to
96c89eb
Compare
arahlin
force-pushed
the
faster-sidereal-time
branch
from
September 28, 2026 02:14
7f3454e to
b975315
Compare
arahlin
force-pushed
the
light-deflection
branch
from
September 28, 2026 02:14
96c89eb to
2407890
Compare
arahlin
force-pushed
the
faster-sidereal-time
branch
from
September 28, 2026 05:35
b975315 to
37b6265
Compare
arahlin
force-pushed
the
light-deflection
branch
from
September 28, 2026 05:35
2407890 to
8015e34
Compare
arahlin
force-pushed
the
faster-sidereal-time
branch
from
September 28, 2026 05:50
37b6265 to
6aa1efb
Compare
arahlin
force-pushed
the
light-deflection
branch
2 times, most recently
from
September 28, 2026 15:46
c045451 to
8b4c391
Compare
The sun bends the incoming ray and neither package accounted for it. That is the whole of the 14 mas by which azel2radec disagreed with astropy, and of the 43 mas on the way back -- one missing term rather than an accumulation of small ones. Adding it takes azel2radec to 0.79 mas and radec2azel to 0.84, so test_astropy's tolerance drops from a tenth of an arcsecond to five milliarcseconds, resting on two models agreeing rather than on room for a term one of them lacks. rate_defl turns it on and defaults to 'never', like rate_dut1 and rate_wobble: at ~20 mas for about a tenth of azel2bore it is opt-in, and a caller who does not ask gets exactly what they got before. Only the sun's position is cached at that rate; the deflection depends on where the telescope points, so the per-sample part runs every sample, as aberration does. It goes last in the forward chain and first in the inverse, physics applying it before aberration and the forward chain undoing an observation. Aberration and deflection are one function rather than two, because eraEpv00 costs 7.8 us a call and serves both -- aberration wants the earth's barycentric velocity, deflection its heliocentric position, and eraEpv00 returns both. Computing it twice doubled the whole transform with both rates on 'always', 3202 ms against 1610 per 200k samples. Sharing the pointing vector and summing the two rotation vectors into one quaternion halves the per-sample cost again, 35.5 to 17.5 ns. Summing drops the commutator of the two rotations, 1e-4 * 5e-7 rad or 0.002 uas. With deflection off the sum is the aberration vector untouched: with the default rates the output is bit-identical to the pre-deflection build over 240k values, including fast_aber=False, which is not a small-angle rotation and keeps its own quaternion. Switched on it costs 1.10x on azel2bore for at most ~20 mas at the elongations a telescope observes at, which is the trade and the reason the default is off. eraLdsun does the arithmetic rather than the closed form 4.07 mas / tan(elongation / 2), so a port of this shares the vendored ERFA and agrees bit for bit by construction rather than by transcription. The rotation always takes the small-angle form, unlike aberration's fast_aber switch: the deflection never exceeds ~0.1 arcsec, so the dropped cubic term is ~2e-20 rad, where the exact path measured nearly twice the cost. The inverse is a sign flip -- eraLdsun has no closed-form inverse, ERFA iterating in eraAticq -- which is second order in the deflection, 1e-9 mas. qp_reset_rates enumerates the rates one at a time and was missing qp_reset_rate_defl, so reset_rates carried the previous chunk's sun position into the next one, having been documented as the call to make at the start of each chunk. TestResetRates now pins the property rather than calling it and asserting nothing: with one rate set to 'once' the correction is computed at the first sample and frozen, so after reset_rates the answer has to match a freshly built QPoint. It is parametrised over every rate the package reports rather than a list written out in the test, so a rate added later is covered, and a companion case checks the comparison can fail. TestLightDeflection checks the term against erfa.ldsun rather than the closed form, because get_sun returns a GCRS position with a distance attached: compared to an ICRS coordinate, astropy applies parallax, which for a body one au away swings the direction by 80 degrees. test_astropy switches the term on in make_qpoint beside the IERS rates, astropy applying it too, so leaving it off would compare two different models. The module's shared TOL_ARCSEC moves with the term rather than being left loose, since every comparison in the file now has the deflection switched on. make_qpoint lets a caller override one of the rates it sets, which is how TestLightDeflection gets the same QPoint with the term off to show what it is worth. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
arahlin
force-pushed
the
faster-sidereal-time
branch
from
September 28, 2026 15:54
aa04fa2 to
3dbe272
Compare
arahlin
force-pushed
the
light-deflection
branch
from
September 28, 2026 15:54
8b4c391 to
1b56c81
Compare
This branch has not been deployed
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.
Based on
faster-sidereal-time(#30). C library andqpointonly.The sun bends the incoming ray, and
qpointdid not account for it. It was the whole ofthe 14 mas by which
azel2radecdiffered from astropy, and of the 43 mas coming back:azel2radecradec2azeltest_astropy.py's tolerance drops from 0.1 arcsec to 5 mas, andmake_qpointthereswitches the term on beside the IERS rates, astropy applying it too.
How it is wired
rate_deflturns it on and defaults tonever, likerate_dut1andrate_wobble:~20 mas for about a tenth of
azel2bore, so a caller who does not ask gets byte-for-bytewhat they got before. Only the sun's position is cached at that rate — the deflection
depends on where the telescope points, so the per-sample part runs every sample, as
aberration does. It sits last in the forward chain and first in the inverse, physics
applying it before aberration.
Aberration and deflection are one function,
qp_apply_aaber_defl, replacingqp_apply_annual_aberration.eraEpv00costs 7.8 µs a call and serves both — aberrationwants the earth's barycentric velocity, deflection its heliocentric position, and it
returns both. Calling it twice doubled the whole transform with both rates on
always(3202 ms against 1610 for 200k samples). Sharing the pointing vector and summing the two
rotation vectors into one quaternion halved the per-sample cost again, 35.5 → 17.5 ns.
Switched on it costs 1.10x on
azel2bore. Switched off the output is bit-identical to thepre-deflection build over 240k values, including the
mean_aber=Falseandfast_aber=Falsepaths.
Three implementation choices:
eraLdsundoes the arithmetic, not the textbook4.07 mas · cot(elongation/2):ERFA is already vendored, and its routine takes the sun-to-observer vector directly
rather than needing the elongation.
fast_aberswitch: the deflection never exceeds ~0.1″, so the dropped cubic term is ~2e-20 rad and
the exact path measured nearly twice the cost.
fast_aber=Falsekeeps its own quaternion.eraLdsunhas no closed-form inverse — ERFA iterates ineraAticq— but negating is second order, 1e-9 mas.Also
eraEpv00was being called with its two outputs aliased to one array, so the heliocentricposition was overwritten by the barycentric one. Harmless while only the velocity was
wanted; deflection needs both.
qp_reset_ratesenumerates the rates one at a time and was missingqp_reset_rate_defl,so
reset_ratescarried the previous chunk's sun position into the next.TestResetRatesis rewritten to pin the property rather than call it and assert nothing: with a rate set to
'once'the correction freezes at the first sample, so afterreset_ratesthe answer mustmatch a freshly built
QPoint— parametrised over every rate the package reports, so arate added later is covered without anyone remembering.
C API:
qp_memory_tgainsstate_defl,state_defl_inv,e_sunandem_sun; thectypes mirror tracks them and all 55 field offsets were checked against the compiled struct.
Tests
465 passing, 14 skipped.
TestLightDeflectionasserts the term is present, that switchingit off reproduces the old 14 mas and 43 mas exactly, and pins size and direction against
erfa.ldsun.The module's shared
TOL_ARCSECmoves with the term rather than being left loose, sinceevery comparison in the file now has the deflection switched on.
make_qpointlets acaller override one of the rates it sets, which is how
TestLightDeflectiongets the sameQPointwith the term off to show what it is worth.Coverage, from a temporary
-O0 --coveragebuild under gcc:qp_apply_aaber_deflat 95%of its 41 lines,
qpoint.cunmoved at 95%. Worth stating becauserate_defldefaults tonever, so a term nobody switched on would read as zero coverage and look identical to onethat does not work.
🤖 Generated with Claude Code