Thanks for the simple example Tim Jenness; that's super useful.
I converted your YAML to a string using fits::makeLimitedFitsHeader, then tried the following:
$ cat astshim_test.py
|
import astshim
|
|
f = open("fitsheader-decam-calexp-0412037_10.str", "r")
|
fitshead = f.read()
|
|
for _ in range(400):
|
ss = astshim.StringStream(fitshead)
|
fc = astshim.FitsChan(ss, "Encoding=FITS-WCS, IWC=1, SipReplace=0")
|
print(fc.read().show())
|
|
$ python astshim_test.py | grep PV1_3 | sort | uniq
|
PV1_3 = 0.00020875199697911739 # Projection parameter 3 for axis 1
|
PV1_3 = 0.0017097517848014832 # Projection parameter 3 for axis 1
|
PV1_3 = 0.0018924465402960777 # Projection parameter 3 for axis 1
|
PV1_3 = -0.0024933274835348129 # Projection parameter 3 for axis 1
|
PV1_3 = 0.007853679358959198 # Projection parameter 3 for axis 1
|
PV1_3 = -0.011195987462997437 # Projection parameter 3 for axis 1
|
PV1_3 = -0.016240030527114868 # Projection parameter 3 for axis 1
|
PV1_3 = -0.019780918955802917 # Projection parameter 3 for axis 1
|
PV1_3 = 0.029940336942672729 # Projection parameter 3 for axis 1
|
PV1_3 = -0.040052175521850586 # Projection parameter 3 for axis 1
|
PV1_3 = -0.7469334602355957 # Projection parameter 3 for axis 1
|
PV1_3 = -0.9999995231628418 # Projection parameter 3 for axis 1
|
PV1_3 = 0.9999995231628418 # Projection parameter 3 for axis 1
|
PV1_3 = 0 # Projection parameter 3 for axis 1
|
PV1_3 = 1.0147895812988281 # Projection parameter 3 for axis 1
|
PV1_3 = 1.0185579796633307e-312 # Projection parameter 3 for axis 1
|
PV1_3 = 1.0397779375729834e-312 # Projection parameter 3 for axis 1
|
PV1_3 = 1.0609978954826362e-312 # Projection parameter 3 for axis 1
|
PV1_3 = 1.0822178533922889e-312 # Projection parameter 3 for axis 1
|
PV1_3 = 1.1458777271212471e-312 # Projection parameter 3 for axis 1
|
PV1_3 = 1.2851807146773715e-70 # Projection parameter 3 for axis 1
|
PV1_3 = 1.2862567834555961e+248 # Projection parameter 3 for axis 1
|
PV1_3 = 1 # Projection parameter 3 for axis 1
|
PV1_3 = -2.2954349517822266 # Projection parameter 3 for axis 1
|
PV1_3 = 2.3364124312642001e-307 # Projection parameter 3 for axis 1
|
PV1_3 = 2.3364158264574656e-307 # Projection parameter 3 for axis 1
|
PV1_3 = 2.3364616615665505e-307 # Projection parameter 3 for axis 1
|
PV1_3 = 2.5691505243038807e+161 # Projection parameter 3 for axis 1
|
PV1_3 = 2.6204526022630148e-310 # Projection parameter 3 for axis 1
|
PV1_3 = 4.6548099670614214e-310 # Projection parameter 3 for axis 1
|
PV1_3 = 4.8909910913784047e+199 # Projection parameter 3 for axis 1
|
PV1_3 = 57073.03125 # Projection parameter 3 for axis 1
|
PV1_3 = 6.0134693029269245e-154 # Projection parameter 3 for axis 1
|
PV1_3 = 6306.33203125 # Projection parameter 3 for axis 1
|
PV1_3 = 6.7786571791757129e+78 # Projection parameter 3 for axis 1
|
PV1_3 = 6.9359554423490891e-310 # Projection parameter 3 for axis 1
|
PV1_3 = 7.4057653104688004e-312 # Projection parameter 3 for axis 1
|
PV1_3 = 8.4879831638610893e-314 # Projection parameter 3 for axis 1
|
PV1_3 = -nan # Projection parameter 3 for axis 1
|
(Apologies for my naivety in extracting values from astshim; I've never attempted to use this API before, and I didn't want to spend long understanding it.)
The above is effectively replicating what's happening in the guts of afw, and I'm pretty sure that's where the problem is arising: every time the WCS is incorrect, its because FitsChan.read() is giving back something with PV1_3 or PV2_3 set to garbage.
Based on the above, I think I disagree with Tim Jenness's conclusions — this looks like it is happening in astshim or maybe starlink_ask.
I note that Valgrind says a bunch of things like:
==153626== Use of uninitialised value of size 8
|
==153626== at 0x5A947EB: __cos_avx (s_sin.c:510)
|
==153626== by 0x5A53F9D: sincos (s_sincos.c:43)
|
==153626== by 0x9C4E9975: astEraS2c (s2c.c:21)
|
==153626== by 0x9C18A2B3: Transform (sphmap.c:1282)
|
==153626== by 0x9BFFEB9B: astTransform_ (mapping.c:24060)
|
==153626== by 0x9BD74724: Transform (cmpmap.c:3790)
|
==153626== by 0x9BFFEB9B: astTransform_ (mapping.c:24060)
|
==153626== by 0x9BD7473D: Transform (cmpmap.c:3791)
|
==153626== by 0x9BFFEB9B: astTransform_ (mapping.c:24060)
|
==153626== by 0x9BD7473D: Transform (cmpmap.c:3791)
|
==153626== by 0x9BFFEB9B: astTransform_ (mapping.c:24060)
|
==153626== by 0x9BD7473D: Transform (cmpmap.c:3791)
|
which look suspicious, but I'm not sufficiently familiar with this part of the codebase to dive in further without significant effort.
Russell Owen or Tim Jenness — do you have time to look at this further? I realise you're both busy, but this looks like a major problem and you have the most expertise here.
My understanding is that Exposure.getWcs() will use the full serialized WCS retrieved from a table extension, and that butler calexp_wcs will try to calculate an approximate WCS from the FITS headers. One difference between that January files and the modern files is that the new files have distortion terms included as QV headers whereas no distortion was present previously. I'm a bit surprised that QV is being used rather than PV but maybe AST is using that as a default somewhere. My guess would be that Starlink AST is hitting some edge cases with the distortion handling.