Skip to content

Fix Record.wrsamp() when d_signal is missing - #619

Open
divya-git03 wants to merge 3 commits into
MIT-LCP:mainfrom
divya-git03:fix-wrsamp-missing-d-signal
Open

divya-git03 wants to merge 3 commits into
MIT-LCP:mainfrom
divya-git03:fix-wrsamp-missing-d-signal

Conversation

@divya-git03

@divya-git03 divya-git03 commented Nov 1, 2026 •

Copy link
Copy Markdown

Fixes #618

Problem

Record.wrsamp() raises a TypeError when a record contains physical signal data (p_signal) but has no digital signal data (d_signal). This occurs because checksum calculation expects digital samples.

Changes

Convert p_signal to digital samples using the existing adc() method when d_signal is None.

Apply the conversion only when expanded=False and physical signal data is available.

Preserve the existing behavior for records that already contain digital signals.

Add a regression test using sample-data/100 to verify that a physical-only record can be written and read back with the expected digital samples.

Add an entry to the unreleased changelog.

Testing

python -m pytest -q tests/test_record.py

90 passed, 1 skipped.

The skipped test was network-dependent and encountered a remote connection error.

git diff --check completed successfully.

Fix writing physical-only and expanded physical signals without retaining converted digital samples on the record.

@tompollard

Copy link
Copy Markdown
Member

Thanks for picking this up! I checked that physical-only records from sample-data now write the same digital samples as when they're read with physical=False.

One problem: self.d_signal = self.adc() stays on the record after writing, so if p_signal is changed and the record is written again, the old data is written:

record = wfdb.rdrecord("sample-data/100", sampto=1000)
record.wrsamp()
record.p_signal = record.p_signal * 2
record.wrsamp()  # writes the original signal, not the doubled one

Could you convert only for the write and put d_signal back afterwards? The same works for expanded=True (with e_d_signal and self.adc(expanded=True)), which still fails at the moment. Something like:

converted = None
if not expanded and self.d_signal is None and self.p_signal is not None:
    self.d_signal = self.adc()
    converted = "d_signal"
elif expanded and self.e_d_signal is None and self.e_p_signal is not None:
    self.e_d_signal = self.adc(expanded=True)
    converted = "e_d_signal"
try:
    ...  # the existing checksum update and writing
finally:
    if converted:
        setattr(self, converted, None)

For the tests:

  • Compare with the digital samples from the original file (wfdb.rdrecord("sample-data/100", sampto=1000, physical=False).d_signal) rather than record.adc(), which is the function being tested.
  • Add a second write after changing p_signal, and check that record.d_signal is still None afterwards.
  • Add an expanded=True case (for example sample-data/test01_00s_frame read with smooth_frames=False).

Could you also add "Fixes #618" to the description, and put the changelog entry on one line like the others? We're planning to merge #620, which moves changelog entries into separate files in changelog.d/, so once that's in, the entry will need to move to a file there (for example changelog.d/wrsamp-physical.rst).

tompollard added a commit that referenced this pull request Nov 1, 2026
As discussed in #606: each change now adds its own file in
`changelog.d/` instead of editing `docs/changes.rst`, so pull requests
no longer conflict on the changelog.

- Fragments are plain `.rst` files named after the change (for example
`rr2ann-fixes.rst`), in the same bold-heading format as now.
`changelog.d/README.rst` explains this.
- At release time, `uv run towncrier build --version X.Y.Z --date "Mon
YYYY"` adds a `Version X.Y.Z (Mon YYYY)` section to `docs/changes.rst`
and deletes the fragments. Entries are sorted by file name.
- The 25 existing unreleased entries are moved into `changelog.d/`
unchanged. I checked that `towncrier build --draft` reproduces them
exactly, and ran a real build on a copy: the docs build passes with `-W`
both before and after.
- `towncrier` is pinned in the `dev` group, like Ruff. AGENTS.md and
DEVELOPING.md are updated.

One trade-off: the unreleased changes no longer show on the docs site
until a release. If you'd like them there, `sphinxcontrib-towncrier` can
render a draft section, but that touches `docs/conf.py`, so I'd do it
after #616.

#619 and #583 add entries to `docs/changes.rst`, so they'd need to move
them into a fragment after this is merged (or the other way round,
whichever goes in first).
@divya-git03

Copy link
Copy Markdown
Author

Thanks for the detailed review!

I've addressed the requested changes:

  • Made the digital conversion temporary and restored d_signal / e_d_signal after writing.
  • Added support for expanded=True using adc(expanded=True).
  • Updated the regression test to compare against the original file's digital samples.
  • Added a second-write test after modifying p_signal.
  • Added an expanded physical-only record test.
  • Moved the changelog entry to changelog.d/wrsamp-physical.rst in the new one-line format.

I've pushed the updates in commit 4b17cf7. The targeted regression tests pass (3 passed), and git diff --check is clean.

I've also updated the PR description with Fixes #618.

Thanks again for the guidance!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Record.wrsamp() fails with a TypeError when the record has no d_signal

2 participants