Run a Manta at its frame-rate limit, and wait for a DHT11's first reading

A free-running Manta runs at AcquisitionFrameRateAbs, not at its limit:
rietschen's RGB camera came set to 0.955 fps and delivered 5 frames in 5.2 s.
The gige recipe now writes a short fixed exposure and then the rate, clamped
to the camera's maximum (4.7 fps at 60 MB/s), before auto-exposure goes back
on. The export summary shows the rate set, not the limit, which had read
4.71 fps for a camera running at 0.96.

test hardware env asked for a DHT11 reading at once, but the firmware reads
it every 10 s, the first time 2 s after it starts. install-tower --verify
failed on rietschen 1.4 s into its run, 36 s after the same test had passed.
It now waits one read period for the first reading. Tests 542/542 (mock).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Peter Bochmann 2026-10-20 17:54:52 +02:00
parent 8850337250
commit fdb3c50b68
Signed by: peter.bochmann
GPG Key ID: 94E8C523489B8AC1
3 changed files with 27 additions and 7 deletions

View File

@ -723,7 +723,7 @@ MTU 8000): RGB (G-1236C), NIR and ACR (both G-1236B). Their firmware
| Program / Alvium | Manta |
|---|---|
| `ExposureTime` | `ExposureTimeAbs` |
| `AcquisitionFrameRate` (+ `…Enable`) | `AcquisitionFrameRateAbs`, free-running at `AcquisitionFrameRateLimit` |
| `AcquisitionFrameRate` (+ `…Enable`) | `AcquisitionFrameRateAbs` - which a free-running Manta runs at, up to `AcquisitionFrameRateLimit` |
| `IntensityControllerTarget` / `…Tolerance` | `ExposureAutoTarget` / `ExposureAutoAdjustTol` |
| `DeviceLinkThroughputLimit` | `StreamBytesPerSecond` (max 124 MB/s, shared by every camera on the port) |
| `BayerRG12p`, chunk data, `DeviceReset` | none |
@ -732,8 +732,9 @@ The program looks a missing feature up under its older name, so `test camera`
and the telemetry work. `export_camera_profile.py` picks its `gige` recipe by
itself when a camera has `ExposureTimeAbs` and no `ExposureTime`: frame trigger
off (the legacy cameras booted with `TriggerMode = On`, `Line1`), `BayerRG8` on
the colour camera and `Mono8` on a mono one, 60 MB/s each, and an auto-exposure
target of 50 %. That last one matters: the keep rule waits for the frame mean
the colour camera and `Mono8` on a mono one, 60 MB/s each, the frame rate at
the camera's maximum (rietschen's RGB camera came set to 0.955 fps), and an
auto-exposure target of 50 %. That last one matters: the keep rule waits for the frame mean
to reach 50 %, and at the cameras' old 25 every waypoint ran into
`frame_timeout_ms`.

View File

@ -143,6 +143,13 @@ _GIGE_COMMON = [
("Height", 100000),
("PixelFormat", ("BayerRG8", "Mono8")),
("StreamBytesPerSecond", 60000000),
# Free-running, a Manta still runs at AcquisitionFrameRateAbs, not at its
# limit: rietschen's RGB camera came with 0.955 and delivered 5 frames in
# 5.2 s. A short fixed exposure first, so the rate is not capped by the
# exposure the camera happens to hold, as in the Alvium recipe.
("ExposureAuto", "Off"),
("ExposureTimeAbs", 1000.0),
("AcquisitionFrameRateAbs", 1000.0), # clamped to the camera's maximum
("ChunkModeActive", False),
# The keep rule waits for the frame mean to reach the camera's target. The
# host starts on its default band of 50 % - rietschen's cameras were set to
@ -290,14 +297,13 @@ def main():
refused.append(n)
# What the settings actually cost per frame - the number that decides
# whether this profile can be delivered over the link at all. A
# free-running GigE camera reports its ceiling as the rate limit.
# whether this profile can be delivered over the link at all.
try:
w = cam.get_feature_by_name("Width").get()
h = cam.get_feature_by_name("Height").get()
fmt = cam.get_feature_by_name("PixelFormat").get()
bpp = {"RGB8": 3, "BGR8": 3, "BayerRG12p": 1.5, "BayerRG12": 2}.get(str(fmt), 1)
rate_name = "AcquisitionFrameRateLimit" if family == "gige" else "AcquisitionFrameRate"
rate_name = "AcquisitionFrameRateAbs" if family == "gige" else "AcquisitionFrameRate"
rate = cam.get_feature_by_name(rate_name).get()
mb = w * h * bpp / 1e6
print(f"\nresult : {w}x{h} {fmt} {mb:.1f} MB/frame "

View File

@ -1097,13 +1097,26 @@ void TestRunner::testHwEnv(TestReport& r) {
}
setProgress(s.id, 2, 2, "reading sensor");
std::optional<EnvSample> sample = hw_.env ? hw_.env->sample() : std::nullopt;
// The firmware reads its DHT11 every 10 s, the first time 2 s after it
// starts, so a run straight after the host connected may have no reading
// yet: install-tower --verify failed on rietschen 1.4 s into its run, 36 s
// after the same test had passed. Wait one read period before calling it
// missing.
if (hw_.env && !sample && on_mcu) {
const auto deadline = clock_t_::now() + 12s;
while (!sample && clock_t_::now() < deadline && !cancelled()) {
std::this_thread::sleep_for(200ms);
sample = hw_.env->sample();
}
}
CheckResult m;
if (!hw_.env) {
m.pass = false;
m.detail = "declared as '" + hw_.declared->env +
"' but no sensor was built -- check [Env] i2c_device; the declaration itself is "
"what enables it";
} else if (auto sample = hw_.env->sample()) {
} else if (sample) {
m.detail = "CRC-valid reading from " + hw_.env->name();
} else if (on_mcu) {
m.pass = false;