Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion packages/embeddedci-openhtf/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# Changelog — `embeddedci-openhtf`

## Unreleased
## 2.3.1

- `signal_generate`, `signal_generate_phase` and `loopback_measure_phase` default to
`dac_path="3v3"` instead of `"5v"`; `dac_replay_phase` now defaults to the path stored with the
Expand Down
2 changes: 1 addition & 1 deletion packages/embeddedci-openhtf/pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ build-backend = "hatchling.build"

[project]
name = "embeddedci-openhtf"
version = "2.3.0"
version = "2.3.1"
description = "OpenHTF plug for driving an EmbeddedCI BenchPod directly over TCP or serial (no cloud)"
readme = "README.md"
requires-python = ">=3.10"
Expand Down
2 changes: 1 addition & 1 deletion packages/embeddedci/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# Changelog — `embeddedci`

## Unreleased
## 2.13.1

- `Capabilities.scope` is False on the digital-only board. Current firmware lists `signal` on every
board; it only stands for the scope on firmware without the `analog` flag.
Expand Down
2 changes: 1 addition & 1 deletion packages/embeddedci/pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ build-backend = "hatchling.build"

[project]
name = "embeddedci"
version = "2.13.0"
version = "2.13.1"
description = "BenchPod SDK and pytest plugin for EmbeddedCI hardware-in-the-loop testing"
readme = "README.md"
requires-python = ">=3.10"
Expand Down
8 changes: 8 additions & 0 deletions packages/embeddedci/tests/e2e/e2e_helpers.py
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,14 @@ def run() -> None:
return result["capture"], stamps


# How far the host-clock fit may stray from the reported rate. Over Ethernet or the USB network it
# lands within 0.5%; over Wi-Fi, where packets queue and leave in bursts, it scattered to 0.983x
# and 1.009x on a pod whose clock is fine (the same pod passed over the USB network). A capture
# that really runs at another rate is off by 5-25% (firmware that reports the requested rate
# instead of the achieved one), so 3% still catches that.
HOST_CLOCK_REL = 0.03


def host_clock_rate(event_samples: Sequence[int], stamps: Sequence[float]) -> float:
"""Real samples per second: fit the sample index of each event against the host time it was
sent. The host fires at a fixed interval, so events are paired in order; events after the
Expand Down
10 changes: 6 additions & 4 deletions packages/embeddedci/tests/e2e/test_e2e_pod.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,8 @@
from embeddedci.benchpod.control_loop import CURVE_POINTS
from embeddedci.benchpod.decode import decode_uart

from e2e_helpers import ANALOG, SETTLE, adc_levels, events_during, host_clock_rate, p2p, split_levels
from e2e_helpers import (ANALOG, HOST_CLOCK_REL, SETTLE, adc_levels, events_during, host_clock_rate, p2p,
split_levels)

pytestmark = pytest.mark.hardware

Expand Down Expand Up @@ -661,7 +662,8 @@ def test_la_reports_the_rate_it_achieved(pod, bench, samples, rate_hz):
# The host clock is the independent reference for the capture clock: events sent at known host
# times land at sample indices, and the fitted slope is the real sample rate. The drift a wrong
# rate causes grows with the capture length (samples / 24 MHz regardless of rate: ~170 ms for
# the LA test, ~80 ms for the ADC test), far above the few ms of network jitter.
# the LA test, ~80 ms for the ADC test), far above the few ms of wired network jitter; Wi-Fi
# needs the wider HOST_CLOCK_REL (e2e_helpers).
def test_la_sample_rate_matches_the_host_clock(pod, bench):
np = pytest.importorskip("numpy")
ch = bench.free_la[0]
Expand All @@ -674,7 +676,7 @@ def test_la_sample_rate_matches_the_host_clock(pod, bench):
count=18, interval=0.2)
bits = np.asarray(la.channel(ch), dtype=np.int8)
rate = host_clock_rate(np.flatnonzero(np.diff(bits) == 1) + 1, stamps)
assert rate == pytest.approx(la.sample_rate_hz, rel=0.005), (
assert rate == pytest.approx(la.sample_rate_hz, rel=HOST_CLOCK_REL), (
f"the LA really samples at {rate:.0f} Hz ({rate / la.sample_rate_hz:.4f}x the reported "
f"{la.sample_rate_hz:.0f} Hz)")

Expand All @@ -701,7 +703,7 @@ def toggle_dac(k): # a 5 kHz burst on even events, flat on odd ones
active = (blocks.max(axis=1) - blocks.min(axis=1)) > 800
edges = (np.flatnonzero(np.diff(active.astype(np.int8)) != 0) + 1) * block
rate = host_clock_rate(edges, stamps)
assert rate == pytest.approx(cap.sample_rate_hz, rel=0.005), (
assert rate == pytest.approx(cap.sample_rate_hz, rel=HOST_CLOCK_REL), (
f"the ADC really samples at {rate:.0f} Hz ({rate / cap.sample_rate_hz:.4f}x the reported "
f"{cap.sample_rate_hz:.0f} Hz)")

Expand Down