Fix psrflux loading bugs affecting the secondary-spectrum axes - #32
Merged
Conversation
Found while investigating a report of a "misaligned" secondary spectrum in high-frequency-resolution FAST data. The secondary-spectrum FFT and plotting code were audited and found to be coordinate-correct (zero-delay and zero-Doppler land on the right bins for both even and odd channel/subint counts, pre-whitening introduces no shift, and plot_sspec handles cell centres/edges consistently); the defects were all in the loader. remove_short_subs (dynspec.py): - The loop condition used `<=` together with a redundant, always-true `sdt >= 0` guard. Once the surviving sub-integrations became uniformly spaced, sdt fell to 0 and the test collapsed to `dt0 - dt <= 0`, so it kept deleting uniformly spaced subints. A 6-subint file with one short leading subint lost 4 of them (only 2 survived), silently truncating the observation and its time axis. Now stops as soon as the first subint is no longer a short outlier, and never runs the array down. Channel bandwidth (dynspec.py): - psrflux frequencies are channel centres, so the spacing is span/(nchan-1), but df was computed as span/nchan. df is now taken from the median difference between channel centres, which is also robust to an occasional missing channel. For the bundled 400 MHz / 512-channel example file this changes df from 0.77972 to 0.78125 MHz (= 400/512, exactly). Because the delay axis is built as tdel ~ 1/df, this directly mis-scaled the secondary-spectrum delay axis (~1/(nchan-1); large for small channel counts, negligible for very large ones). Non-uniform channels (dynspec.py): - The ACF and secondary-spectrum FFTs assume a uniform frequency grid, but nothing checked for one. Loading now warns when the channel spacing is non-uniform (gaps or stitched sub-bands), which distorts the delay axis, and separately warns when the number of unique channel centres does not match the channel count from the file. Channel ordering: - Descending-frequency files are now detected from the first channel block rather than inferred from a negative bandwidth, which keeps the flux-row flip independent of how bw/df are derived. Verified to agree with the previous behaviour on the bundled example file, and covered by tests that check each flux value stays matched to its (frequency, time). Adds tests/test_dynspec_loading.py with 12 regression tests. Full suite: 86 passed. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01EG6cv3jHNLYv1TN2YWDqi3
Owner
Author
|
Fixes for independently identified dynspec loading bugs. |
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.
Background
Found while investigating a user report of a "misaligned" secondary spectrum in high-frequency-resolution FAST data (PSR B1933+16, ~500 Hz and ~2.9 kHz channels).
I audited the whole chain from loading a psrflux dynamic spectrum through to plotting the secondary spectrum.
calc_sspecandplot_sspeccame out clean — verified empirically:nf/nt(the transform is zero-padded to a power of two, so input parity is irrelevant);convolve2d(..., mode='valid')moves the sample grid by half a pixel, but taking|F|²discards that linear phase (synthetic features land on the identical bin withprewhiteon and off);plot_sspecusescentres_to_edges+pcolormeshconsistently withplot_dyn, so cells are centred on their coordinates, and the fitted-arc overlay shares that convention (its vertex sits exactly on the DC cell).All of the real defects were in the loader.
Fixes
1.
remove_short_subsdeleted far more than it should (High)The loop used
<=together with a redundant, always-truesdt >= 0guard. Once the surviving sub-integrations became uniformly spaced,sdtfell to 0 and the condition collapsed todt0 - dt <= 0, so it kept deleting uniformly spaced subints.Reproduced: a 6-subint file with one short leading subint lost 4 of them (only 2 survived), silently truncating the observation and its time axis. It now stops as soon as the first subint is no longer a short outlier.
2. Channel bandwidth off-by-one (Medium)
psrflux frequencies are channel centres, so the spacing is
span/(nchan-1), butdfwas computed asspan/nchan.dfis now taken from the median difference between channel centres (also robust to an occasional missing channel).For the bundled 400 MHz / 512-channel example file this changes
dffrom0.77972to0.78125MHz (= 400/512, exactly). Since the delay axis is built astdel ~ 1/df, this directly mis-scaled the secondary-spectrum delay axis — by ~1/(nchan−1), so significant for small channel counts and negligible for very large ones.3. Non-uniform channels were silently assumed uniform (High, when it applies)
The ACF and secondary-spectrum FFTs assume a uniform frequency grid, but nothing checked. Loading now warns when the channel spacing is non-uniform (gaps or stitched sub-bands), and separately when the number of unique channel centres doesn't match the file's channel count.
4. Channel-ordering detection made independent of
bw/dfDescending-frequency files are now detected from the first channel block rather than inferred from a negative bandwidth, so the flux-row flip no longer depends on how
bw/dfare derived. Verified to agree with the previous behaviour on the bundled example file.Verification
tests/test_dynspec_loading.py(12 tests):remove_short_subsremoves exactly the short subint / leaves a uniform cadence intact,dfmatches the true spacing for several channel counts and for the real example file, non-uniform channels warn (and uniform ones don't), descending and ascending files keep each flux value matched to its (frequency, time), and the delay axis reflects the correcteddf.E9,F63,F7,F82) returns 0; the new test file is style-clean.J0437-4715file:nchan/nsub/dt/tobs/bwunchanged, no spurious warnings, frequencies correctly ascending.Note for users
The
dfchange slightly alters the delay-axis scaling of previously computed secondary spectra (it is now correct). The effect is ~1/(nchan−1) — noticeable for low channel counts, well under 0.01% for the very large channel counts typical of baseband data.🤖 Generated with Claude Code
Generated by Claude Code