Files
csaxs_bec/developer/panda_box_free_running_setup.html

2080 lines
61 KiB
HTML

<!doctype html>
<html lang="en" class="no-js">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width,initial-scale=1">
<meta name="description" content="User and developer documentation for the cSAXS beamline">
<meta name="author" content="Paul Scherrer Institute">
<link rel="canonical" href="https://bec.pages.psi.ch/csaxs_bec/developer/panda_box_free_running_setup.html">
<link rel="prev" href="lamni_smear_architecture.html">
<link rel="next" href="panda_box_free_running_data_schema.html">
<link rel="icon" href="../assets/images/favicon.png">
<meta name="generator" content="zensical-0.0.11">
<title>PandABox (omny_panda) burst acquisition: two operation modes - SLS cSAXS</title>
<link rel="stylesheet" href="../assets/stylesheets/modern/main.bd6182e7.min.css">
<link rel="stylesheet" href="../assets/stylesheets/modern/palette.dfe2e883.min.css">
<style>:root{}</style>
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Inter:300,300i,400,400i,500,500i,700,700i%7CJetBrains+Mono:400,400i,700,700i&display=fallback">
<style>:root{--md-text-font:"Inter";--md-code-font:"JetBrains Mono"}</style>
<link rel="stylesheet" href="../stylesheets/extra.css">
<script>__md_scope=new URL("..",location),__md_hash=e=>[...e].reduce(((e,t)=>(e<<5)-e+t.charCodeAt(0)),0),__md_get=(e,t=localStorage,a=__md_scope)=>JSON.parse(t.getItem(a.pathname+"."+e)),__md_set=(e,t,a=localStorage,_=__md_scope)=>{try{a.setItem(_.pathname+"."+e,JSON.stringify(t))}catch(e){}},document.documentElement.setAttribute("data-platform",navigator.platform)</script>
</head>
<body dir="ltr" data-md-color-scheme="default" data-md-color-primary="indigo" data-md-color-accent="indigo">
<input class="md-toggle" data-md-toggle="drawer" type="checkbox" id="__drawer" autocomplete="off">
<input class="md-toggle" data-md-toggle="search" type="checkbox" id="__search" autocomplete="off">
<label class="md-overlay" for="__drawer"></label>
<div data-md-component="skip">
<a href="#pandabox-omny_panda-burst-acquisition-two-operation-modes" class="md-skip">
Skip to content
</a>
</div>
<div data-md-component="announce">
</div>
<header class="md-header" data-md-component="header">
<nav class="md-header__inner md-grid" aria-label="Header">
<a href="../index.html" title="SLS cSAXS" class="md-header__button md-logo" aria-label="SLS cSAXS" data-md-component="logo">
<svg xmlns="http://www.w3.org/2000/svg" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" class="lucide lucide-book-open" viewBox="0 0 24 24"><path d="M12 7v14M3 18a1 1 0 0 1-1-1V4a1 1 0 0 1 1-1h5a4 4 0 0 1 4 4 4 4 0 0 1 4-4h5a1 1 0 0 1 1 1v13a1 1 0 0 1-1 1h-6a3 3 0 0 0-3 3 3 3 0 0 0-3-3z"/></svg>
</a>
<label class="md-header__button md-icon" for="__drawer">
<svg xmlns="http://www.w3.org/2000/svg" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" class="lucide lucide-menu" viewBox="0 0 24 24"><path d="M4 5h16M4 12h16M4 19h16"/></svg>
</label>
<div class="md-header__title" data-md-component="header-title">
<div class="md-header__ellipsis">
<div class="md-header__topic">
<span class="md-ellipsis">
SLS cSAXS
</span>
</div>
<div class="md-header__topic" data-md-component="header-topic">
<span class="md-ellipsis">
PandABox (omny_panda) burst acquisition: two operation modes
</span>
</div>
</div>
</div>
<form class="md-header__option" data-md-component="palette">
<input class="md-option" data-md-color-media="(prefers-color-scheme)" data-md-color-scheme="default" data-md-color-primary="indigo" data-md-color-accent="indigo" aria-label="Switch to light mode" type="radio" name="__palette" id="__palette_0">
<label class="md-header__button md-icon" title="Switch to light mode" for="__palette_1" hidden>
<svg xmlns="http://www.w3.org/2000/svg" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" class="lucide lucide-sun-moon" viewBox="0 0 24 24"><path d="M12 2v2M14.837 16.385a6 6 0 1 1-7.223-7.222c.624-.147.97.66.715 1.248a4 4 0 0 0 5.26 5.259c.589-.255 1.396.09 1.248.715M16 12a4 4 0 0 0-4-4M19 5l-1.256 1.256M20 12h2"/></svg>
</label>
<input class="md-option" data-md-color-media="(prefers-color-scheme: light)" data-md-color-scheme="default" data-md-color-primary="indigo" data-md-color-accent="indigo" aria-label="Switch to dark mode" type="radio" name="__palette" id="__palette_1">
<label class="md-header__button md-icon" title="Switch to dark mode" for="__palette_2" hidden>
<svg xmlns="http://www.w3.org/2000/svg" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" class="lucide lucide-sun" viewBox="0 0 24 24"><circle cx="12" cy="12" r="4"/><path d="M12 2v2M12 20v2M4.93 4.93l1.41 1.41M17.66 17.66l1.41 1.41M2 12h2M20 12h2M6.34 17.66l-1.41 1.41M19.07 4.93l-1.41 1.41"/></svg>
</label>
<input class="md-option" data-md-color-media="(prefers-color-scheme: dark)" data-md-color-scheme="slate" data-md-color-primary="indigo" data-md-color-accent="indigo" aria-label="Switch to system preference" type="radio" name="__palette" id="__palette_2">
<label class="md-header__button md-icon" title="Switch to system preference" for="__palette_0" hidden>
<svg xmlns="http://www.w3.org/2000/svg" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" class="lucide lucide-moon" viewBox="0 0 24 24"><path d="M20.985 12.486a9 9 0 1 1-9.473-9.472c.405-.022.617.46.402.803a6 6 0 0 0 8.268 8.268c.344-.215.825-.004.803.401"/></svg>
</label>
</form>
<script>var palette=__md_get("__palette");if(palette&&palette.color){if("(prefers-color-scheme)"===palette.color.media){var media=matchMedia("(prefers-color-scheme: light)"),input=document.querySelector(media.matches?"[data-md-color-media='(prefers-color-scheme: light)']":"[data-md-color-media='(prefers-color-scheme: dark)']");palette.color.media=input.getAttribute("data-md-color-media"),palette.color.scheme=input.getAttribute("data-md-color-scheme"),palette.color.primary=input.getAttribute("data-md-color-primary"),palette.color.accent=input.getAttribute("data-md-color-accent")}for(var[key,value]of Object.entries(palette.color))document.body.setAttribute("data-md-color-"+key,value)}</script>
<label class="md-header__button md-icon" for="__search">
<svg xmlns="http://www.w3.org/2000/svg" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" class="lucide lucide-search" viewBox="0 0 24 24"><path d="m21 21-4.34-4.34"/><circle cx="11" cy="11" r="8"/></svg>
</label>
<div class="md-search" data-md-component="search" role="dialog" aria-label="Search">
<button type="button" class="md-search__button">
Search
</button>
</div>
<div class="md-header__source">
<a href="https://gitea.psi.ch/bec/csaxs_bec" title="Go to repository" class="md-source" data-md-component="source">
<div class="md-source__icon md-icon">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512"><!--! Font Awesome Free 7.1.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) Copyright 2025 Fonticons, Inc.--><path fill="currentColor" d="M439.6 236.1 244 40.5c-5.4-5.5-12.8-8.5-20.4-8.5s-15 3-20.4 8.4L162.5 81l51.5 51.5c27.1-9.1 52.7 16.8 43.4 43.7l49.7 49.7c34.2-11.8 61.2 31 35.5 56.7-26.5 26.5-70.2-2.9-56-37.3L240.3 199v121.9c25.3 12.5 22.3 41.8 9.1 55-6.4 6.4-15.2 10.1-24.3 10.1s-17.8-3.6-24.3-10.1c-17.6-17.6-11.1-46.9 11.2-56v-123c-20.8-8.5-24.6-30.7-18.6-45L142.6 101 8.5 235.1C3 240.6 0 247.9 0 255.5s3 15 8.5 20.4l195.6 195.7c5.4 5.4 12.7 8.4 20.4 8.4s15-3 20.4-8.4l194.7-194.7c5.4-5.4 8.4-12.8 8.4-20.4s-3-15-8.4-20.4"/></svg>
</div>
<div class="md-source__repository">
bec/csaxs_bec
</div>
</a>
</div>
</nav>
</header>
<div class="md-container" data-md-component="container">
<nav class="md-tabs" aria-label="Tabs" data-md-component="tabs">
<div class="md-grid">
<ul class="md-tabs__list">
<li class="md-tabs__item">
<a href="../index.html" class="md-tabs__link">
Home
</a>
</li>
<li class="md-tabs__item">
<a href="../introduction/introduction.html" class="md-tabs__link">
Introduction
</a>
</li>
<li class="md-tabs__item">
<a href="../safety/safety.html" class="md-tabs__link">
Safety
</a>
</li>
<li class="md-tabs__item">
<a href="../user/user.html" class="md-tabs__link">
User guide
</a>
</li>
<li class="md-tabs__item md-tabs__item--active">
<a href="developer.html" class="md-tabs__link">
Developer guide
</a>
</li>
</ul>
</div>
</nav>
<main class="md-main" data-md-component="main">
<div class="md-main__inner md-grid">
<div class="md-sidebar md-sidebar--primary" data-md-component="sidebar" data-md-type="navigation" >
<div class="md-sidebar__scrollwrap">
<div class="md-sidebar__inner">
<nav class="md-nav md-nav--primary md-nav--lifted" aria-label="Navigation" data-md-level="0">
<label class="md-nav__title" for="__drawer">
<a href="../index.html" title="SLS cSAXS" class="md-nav__button md-logo" aria-label="SLS cSAXS" data-md-component="logo">
<svg xmlns="http://www.w3.org/2000/svg" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" class="lucide lucide-book-open" viewBox="0 0 24 24"><path d="M12 7v14M3 18a1 1 0 0 1-1-1V4a1 1 0 0 1 1-1h5a4 4 0 0 1 4 4 4 4 0 0 1 4-4h5a1 1 0 0 1 1 1v13a1 1 0 0 1-1 1h-6a3 3 0 0 0-3 3 3 3 0 0 0-3-3z"/></svg>
</a>
SLS cSAXS
</label>
<div class="md-nav__source">
<a href="https://gitea.psi.ch/bec/csaxs_bec" title="Go to repository" class="md-source" data-md-component="source">
<div class="md-source__icon md-icon">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512"><!--! Font Awesome Free 7.1.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) Copyright 2025 Fonticons, Inc.--><path fill="currentColor" d="M439.6 236.1 244 40.5c-5.4-5.5-12.8-8.5-20.4-8.5s-15 3-20.4 8.4L162.5 81l51.5 51.5c27.1-9.1 52.7 16.8 43.4 43.7l49.7 49.7c34.2-11.8 61.2 31 35.5 56.7-26.5 26.5-70.2-2.9-56-37.3L240.3 199v121.9c25.3 12.5 22.3 41.8 9.1 55-6.4 6.4-15.2 10.1-24.3 10.1s-17.8-3.6-24.3-10.1c-17.6-17.6-11.1-46.9 11.2-56v-123c-20.8-8.5-24.6-30.7-18.6-45L142.6 101 8.5 235.1C3 240.6 0 247.9 0 255.5s3 15 8.5 20.4l195.6 195.7c5.4 5.4 12.7 8.4 20.4 8.4s15-3 20.4-8.4l194.7-194.7c5.4-5.4 8.4-12.8 8.4-20.4s-3-15-8.4-20.4"/></svg>
</div>
<div class="md-source__repository">
bec/csaxs_bec
</div>
</a>
</div>
<ul class="md-nav__list" data-md-scrollfix>
<li class="md-nav__item">
<a href="../index.html" class="md-nav__link">
<span class="md-ellipsis">
Home
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../introduction/introduction.html" class="md-nav__link">
<span class="md-ellipsis">
Introduction
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../safety/safety.html" class="md-nav__link">
<span class="md-ellipsis">
Safety
</span>
</a>
</li>
<li class="md-nav__item md-nav__item--nested">
<input class="md-nav__toggle md-toggle " type="checkbox" id="__nav_4" >
<label class="md-nav__link" for="__nav_4" id="__nav_4_label" tabindex="0">
<span class="md-ellipsis">
User guide
</span>
<span class="md-nav__icon md-icon"></span>
</label>
<nav class="md-nav" data-md-level="1" aria-labelledby="__nav_4_label" aria-expanded="false">
<label class="md-nav__title" for="__nav_4">
<span class="md-nav__icon md-icon"></span>
User guide
</label>
<ul class="md-nav__list" data-md-scrollfix>
<li class="md-nav__item">
<a href="../user/user.html" class="md-nav__link">
<span class="md-ellipsis">
Overview
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../user/before_you_arrive/before_you_arrive.html" class="md-nav__link">
<span class="md-ellipsis">
Before you arrive
</span>
</a>
</li>
<li class="md-nav__item md-nav__item--nested">
<input class="md-nav__toggle md-toggle " type="checkbox" id="__nav_4_3" >
<label class="md-nav__link" for="__nav_4_3" id="__nav_4_3_label" tabindex="0">
<span class="md-ellipsis">
Ptychography
</span>
<span class="md-nav__icon md-icon"></span>
</label>
<nav class="md-nav" data-md-level="2" aria-labelledby="__nav_4_3_label" aria-expanded="false">
<label class="md-nav__title" for="__nav_4_3">
<span class="md-nav__icon md-icon"></span>
Ptychography
</label>
<ul class="md-nav__list" data-md-scrollfix>
<li class="md-nav__item">
<a href="../user/ptychography/ptychography.html" class="md-nav__link">
<span class="md-ellipsis">
Overview
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../user/ptychography/flomni.html" class="md-nav__link">
<span class="md-ellipsis">
flOMNI
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../user/ptychography/lamni.html" class="md-nav__link">
<span class="md-ellipsis">
LamNI
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../user/ptychography/omny.html" class="md-nav__link">
<span class="md-ellipsis">
OMNY
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../user/ptychography/reconstructions.html" class="md-nav__link">
<span class="md-ellipsis">
Ptychoshelves
</span>
</a>
</li>
</ul>
</nav>
</li>
<li class="md-nav__item md-nav__item--nested">
<input class="md-nav__toggle md-toggle " type="checkbox" id="__nav_4_4" >
<label class="md-nav__link" for="__nav_4_4" id="__nav_4_4_label" tabindex="0">
<span class="md-ellipsis">
SAXS
</span>
<span class="md-nav__icon md-icon"></span>
</label>
<nav class="md-nav" data-md-level="2" aria-labelledby="__nav_4_4_label" aria-expanded="false">
<label class="md-nav__title" for="__nav_4_4">
<span class="md-nav__icon md-icon"></span>
SAXS
</label>
<ul class="md-nav__list" data-md-scrollfix>
<li class="md-nav__item">
<a href="../user/saxs/saxs.html" class="md-nav__link">
<span class="md-ellipsis">
Overview
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../user/saxs/online_integration.html" class="md-nav__link">
<span class="md-ellipsis">
Online integration
</span>
</a>
</li>
</ul>
</nav>
</li>
<li class="md-nav__item md-nav__item--nested">
<input class="md-nav__toggle md-toggle " type="checkbox" id="__nav_4_5" >
<label class="md-nav__link" for="__nav_4_5" id="__nav_4_5_label" tabindex="0">
<span class="md-ellipsis">
Data analysis
</span>
<span class="md-nav__icon md-icon"></span>
</label>
<nav class="md-nav" data-md-level="2" aria-labelledby="__nav_4_5_label" aria-expanded="false">
<label class="md-nav__title" for="__nav_4_5">
<span class="md-nav__icon md-icon"></span>
Data analysis
</label>
<ul class="md-nav__list" data-md-scrollfix>
<li class="md-nav__item">
<a href="../user/data_analysis/data_analysis.html" class="md-nav__link">
<span class="md-ellipsis">
Overview
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../user/data_analysis/computing.html" class="md-nav__link">
<span class="md-ellipsis">
Computing
</span>
</a>
</li>
</ul>
</nav>
</li>
</ul>
</nav>
</li>
<li class="md-nav__item md-nav__item--active md-nav__item--section md-nav__item--nested">
<input class="md-nav__toggle md-toggle " type="checkbox" id="__nav_5" checked>
<label class="md-nav__link" for="__nav_5" id="__nav_5_label" tabindex="">
<span class="md-ellipsis">
Developer guide
</span>
<span class="md-nav__icon md-icon"></span>
</label>
<nav class="md-nav" data-md-level="1" aria-labelledby="__nav_5_label" aria-expanded="true">
<label class="md-nav__title" for="__nav_5">
<span class="md-nav__icon md-icon"></span>
Developer guide
</label>
<ul class="md-nav__list" data-md-scrollfix>
<li class="md-nav__item">
<a href="developer.html" class="md-nav__link">
<span class="md-ellipsis">
Overview
</span>
</a>
</li>
<li class="md-nav__item">
<a href="editing_docs.html" class="md-nav__link">
<span class="md-ellipsis">
Editing the documentation
</span>
</a>
</li>
<li class="md-nav__item">
<a href="lamni_smear_architecture.html" class="md-nav__link">
<span class="md-ellipsis">
LamNI smear aid architecture
</span>
</a>
</li>
<li class="md-nav__item md-nav__item--active">
<input class="md-nav__toggle md-toggle" type="checkbox" id="__toc">
<label class="md-nav__link md-nav__link--active" for="__toc">
<span class="md-ellipsis">
PandaBox free-running setup
</span>
<span class="md-nav__icon md-icon"></span>
</label>
<a href="panda_box_free_running_setup.html" class="md-nav__link md-nav__link--active">
<span class="md-ellipsis">
PandaBox free-running setup
</span>
</a>
<nav class="md-nav md-nav--secondary" aria-label="On this page">
<label class="md-nav__title" for="__toc">
<span class="md-nav__icon md-icon"></span>
On this page
</label>
<ul class="md-nav__list" data-md-component="toc" data-md-scrollfix>
<li class="md-nav__item">
<a href="#context" class="md-nav__link">
<span class="md-ellipsis">
Context
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#mode-comparison" class="md-nav__link">
<span class="md-ellipsis">
Mode comparison
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#physical-wiring-mode-b-only" class="md-nav__link">
<span class="md-ellipsis">
Physical wiring (Mode B only)
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#block-configuration" class="md-nav__link">
<span class="md-ellipsis">
Block configuration
</span>
</a>
<nav class="md-nav" aria-label="Block configuration">
<ul class="md-nav__list">
<li class="md-nav__item">
<a href="#mode-a-legacy-reference-baseline" class="md-nav__link">
<span class="md-ellipsis">
Mode A (legacy) — reference baseline
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#mode-b-new-free-running-raw-capture" class="md-nav__link">
<span class="md-ellipsis">
Mode B (new) — free-running raw capture
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#possible-variant-gate-continuous-capture-by-exposure-active-proposed-not-implemented" class="md-nav__link">
<span class="md-ellipsis">
Possible variant: gate continuous capture by exposure-active (proposed, not implemented)
</span>
</a>
</li>
</ul>
</nav>
</li>
<li class="md-nav__item">
<a href="#saving-restoring-layouts" class="md-nav__link">
<span class="md-ellipsis">
Saving / restoring layouts
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#signal-alias-mapping" class="md-nav__link">
<span class="md-ellipsis">
Signal alias mapping
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#known-data-quality-caveat-stale-frame_counter-carryover-found-fixed-2026-09-21" class="md-nav__link">
<span class="md-ellipsis">
Known data-quality caveat: stale frame_counter carryover (found + fixed 2026-09-21)
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#conditional-arming-arm_scan_allowlist-added-2026-09-22" class="md-nav__link">
<span class="md-ellipsis">
Conditional arming (arm_scan_allowlist, added 2026-09-22)
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#switching-between-modes" class="md-nav__link">
<span class="md-ellipsis">
Switching between modes
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#open-question-now-partially-resolved" class="md-nav__link">
<span class="md-ellipsis">
Open question, now partially resolved
</span>
</a>
</li>
</ul>
</nav>
</li>
<li class="md-nav__item">
<a href="panda_box_free_running_data_schema.html" class="md-nav__link">
<span class="md-ellipsis">
PandaBox data schema
</span>
</a>
</li>
</ul>
</nav>
</li>
</ul>
</nav>
</div>
</div>
</div>
<div class="md-sidebar md-sidebar--secondary" data-md-component="sidebar" data-md-type="toc" >
<div class="md-sidebar__scrollwrap">
<div class="md-sidebar__inner">
<nav class="md-nav md-nav--secondary" aria-label="On this page">
<label class="md-nav__title" for="__toc">
<span class="md-nav__icon md-icon"></span>
On this page
</label>
<ul class="md-nav__list" data-md-component="toc" data-md-scrollfix>
<li class="md-nav__item">
<a href="#context" class="md-nav__link">
<span class="md-ellipsis">
Context
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#mode-comparison" class="md-nav__link">
<span class="md-ellipsis">
Mode comparison
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#physical-wiring-mode-b-only" class="md-nav__link">
<span class="md-ellipsis">
Physical wiring (Mode B only)
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#block-configuration" class="md-nav__link">
<span class="md-ellipsis">
Block configuration
</span>
</a>
<nav class="md-nav" aria-label="Block configuration">
<ul class="md-nav__list">
<li class="md-nav__item">
<a href="#mode-a-legacy-reference-baseline" class="md-nav__link">
<span class="md-ellipsis">
Mode A (legacy) — reference baseline
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#mode-b-new-free-running-raw-capture" class="md-nav__link">
<span class="md-ellipsis">
Mode B (new) — free-running raw capture
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#possible-variant-gate-continuous-capture-by-exposure-active-proposed-not-implemented" class="md-nav__link">
<span class="md-ellipsis">
Possible variant: gate continuous capture by exposure-active (proposed, not implemented)
</span>
</a>
</li>
</ul>
</nav>
</li>
<li class="md-nav__item">
<a href="#saving-restoring-layouts" class="md-nav__link">
<span class="md-ellipsis">
Saving / restoring layouts
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#signal-alias-mapping" class="md-nav__link">
<span class="md-ellipsis">
Signal alias mapping
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#known-data-quality-caveat-stale-frame_counter-carryover-found-fixed-2026-09-21" class="md-nav__link">
<span class="md-ellipsis">
Known data-quality caveat: stale frame_counter carryover (found + fixed 2026-09-21)
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#conditional-arming-arm_scan_allowlist-added-2026-09-22" class="md-nav__link">
<span class="md-ellipsis">
Conditional arming (arm_scan_allowlist, added 2026-09-22)
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#switching-between-modes" class="md-nav__link">
<span class="md-ellipsis">
Switching between modes
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#open-question-now-partially-resolved" class="md-nav__link">
<span class="md-ellipsis">
Open question, now partially resolved
</span>
</a>
</li>
</ul>
</nav>
</div>
</div>
</div>
<div class="md-content" data-md-component="content">
<article class="md-content__inner md-typeset">
<h1 id="pandabox-omny_panda-burst-acquisition-two-operation-modes">PandABox (<code>omny_panda</code>) burst acquisition: two operation modes<a class="headerlink" href="#pandabox-omny_panda-burst-acquisition-two-operation-modes" title="Permanent link">&para;</a></h1>
<blockquote>
<p><strong>Status: Mode B confirmed working end-to-end against real PandA hardware
(2026-09-15, omny-panda.psi.ch)</strong> — correct per-channel raw values, masked
<code>gate_detector_active</code>, and <code>frame_counter</code>-based completion all verified,
then speed-tested up to 20 kHz with no reported issues (though see the
caveat under "Saving / restoring layouts" — the saved <code>omny_freerun.ini</code>
layout itself was captured at 10 kHz, not 20 kHz). <code>ptycho_flomni.yaml</code>
currently has <code>omny_panda_continuous</code> (Mode B) active — note the PandA's
Design was switched back to <code>OMNY</code> on 2026-09-15 solely to capture the
<code>omny.ini</code> layout file below, so double-check which Design is actually
loaded on the physical PandA before running a scan against either config.</p>
</blockquote>
<h2 id="context">Context<a class="headerlink" href="#context" title="Permanent link">&para;</a></h2>
<p>flomni burst scans acquire <code>frames_per_trigger</code> sub-frames per scan point.
Historically, the PandABox (<code>omny_panda</code>, device class <code>PandaBoxOMNY</code> in
<code>csaxs_bec/devices/panda_box/panda_box_omny.py</code>) reports one PCAP-aggregated
statistic (Mean/Min/Max/Sum/Diff) per gate, discarding the time structure
within a gate. This document describes the two supported operation modes —
the existing per-gate aggregate mode, and a new continuous free-running raw
mode — and how the PandA hardware and <code>ptycho_flomni.yaml</code> device config must
be set up for each.</p>
<p>Mode selection is a single software flag (<code>raw_stream_mode</code> in the device
config); it does <strong>not</strong> by itself reconfigure the PandA hardware. The PandA
layout must independently match the intended mode (see "Open question" at
the end) before that mode can be used.</p>
<h2 id="mode-comparison">Mode comparison<a class="headerlink" href="#mode-comparison" title="Permanent link">&para;</a></h2>
<table>
<thead>
<tr>
<th></th>
<th>Mode A: legacy (default)</th>
<th>Mode B: free-running (new)</th>
</tr>
</thead>
<tbody>
<tr>
<td>Selected by</td>
<td><code>raw_stream_mode</code> unset / <code>false</code></td>
<td><code>raw_stream_mode: true</code></td>
</tr>
<tr>
<td>PCAP capture fields</td>
<td><code>Mean</code>/<code>Min</code>/<code>Max</code>/<code>Sum</code>/<code>Diff</code> per gate</td>
<td><code>Value</code> (raw instantaneous sample) + gate bit + frame counter</td>
</tr>
<tr>
<td>Gate/arm timing</td>
<td>Pulsed per point / sub-frame (rate = scan rate × <code>frames_per_trigger</code>)</td>
<td>PCAP held open continuously for the burst window, free-running at <code>CLOCK1</code>'s configured rate (started at <strong>100 Hz</strong> for initial testing on 2026-09-15; will be ramped up as testing progresses)</td>
</tr>
<tr>
<td><code>_acquisition_group</code> (BEC async metadata)</td>
<td><code>fly</code> / <code>monitored</code> / <code>burst</code>, chosen from <code>scan_type</code>/<code>frames_per_trigger</code></td>
<td><code>free_running</code></td>
</tr>
<tr>
<td>Message publishing</td>
<td>One <code>.data.put()</code> per PandA network frame, unchanged</td>
<td>Coalesced client-side: flushed every <code>raw_stream_flush_row_count</code> rows or <code>raw_stream_flush_interval</code> seconds, whichever comes first (mitigates Redis/HDF5 message-rate risk — see the acquisition plan's data-path notes)</td>
</tr>
<tr>
<td>Completion check (<code>on_complete</code>)</td>
<td>Exact count: waits for <code>*PCAP.CAPTURED?</code> to reach <code>num_points * frames_per_trigger</code></td>
<td>Exact count: waits for <code>COUNTER1.OUT?</code> (<code>frame_counter</code>) to reach <code>num_points * frames_per_trigger</code>. <code>*PCAP.CAPTURED?</code> can't be used here — <code>CLOCK1</code> drives <code>PCAP.gate</code>/<code>PCAP.trig</code> continuously (see "Continuous gate/arm" below), so the captured count never naturally stops increasing while armed; <code>COUNTER1</code> instead counts completed detector exposures directly (it's clocked off the detector-trigger's falling edge), so it reaches the expected total exactly when the last exposure finishes</td>
</tr>
<tr>
<td>Data completeness verification</td>
<td>Implicit in the exact-count completion check</td>
<td>Same mechanism as Mode A: <code>frame_counter</code> (<code>COUNTER1</code>) is both the completion signal and the post-hoc completeness record — a gap in an otherwise-monotonic sequence in the HDF5 data means dropped rows even though the expected total was reached</td>
</tr>
</tbody>
</table>
<p>Both modes are implemented in the same <code>PandaBoxOMNY</code> class; <code>raw_stream_mode</code>
defaults to <code>false</code> so any device config that doesn't set it keeps today's
exact behavior unchanged.</p>
<h2 id="physical-wiring-mode-b-only">Physical wiring (Mode B only)<a class="headerlink" href="#physical-wiring-mode-b-only" title="Permanent link">&para;</a></h2>
<p>The detector-trigger signal is patched into <strong>TTL input 1</strong> (<code>TTLIN1</code>) on the
PandA front panel. That same <code>TTLIN1.VAL</code> signal feeds both downstream
consumers (see "Block configuration" below): it is the source counted by
<code>COUNTER1</code> (the frame counter) and the source captured into the gate bit.</p>
<h2 id="block-configuration">Block configuration<a class="headerlink" href="#block-configuration" title="Permanent link">&para;</a></h2>
<h3 id="mode-a-legacy-reference-baseline">Mode A (legacy) — reference baseline<a class="headerlink" href="#mode-a-legacy-reference-baseline" title="Permanent link">&para;</a></h3>
<p>The blocks already configured and aliased today, for reference / so this
layout can be restored unambiguously (see <code>ptycho_flomni.yaml</code>):</p>
<ul>
<li>
<p><code>FMC_IN.VAL1</code>/<code>FMC_IN.VAL2</code> (capacitive sensor analog inputs) — <code>Min</code>,
<code>Max</code>, <code>Mean</code> capture fields.</p>
</li>
<li>
<p><code>INENC1</code>-<code>INENC4.VAL</code> (position-capture encoder inputs) — <code>Min</code>, <code>Max</code>,
<code>Mean</code> capture fields.</p>
</li>
<li>
<p><code>PCAP.GATE_DURATION</code> — <code>Value</code>.</p>
</li>
<li>PCAP gate is pulsed once per point / sub-frame by the existing external
trigger electronics (unchanged by this work).</li>
</ul>
<h3 id="mode-b-new-free-running-raw-capture">Mode B (new) — free-running raw capture<a class="headerlink" href="#mode-b-new-free-running-raw-capture" title="Permanent link">&para;</a></h3>
<ul>
<li>
<p><strong>Gate-bit input</strong>: the detector-trigger signal, wired into <code>TTLIN1</code> (see
"Physical wiring" above). <code>TTLIN1.VAL</code> is captured into <strong><code>PCAP.BITS0</code>,
bit offset 0</strong> — confirmed 2026-09-15 against the omny-panda hardware via:</p>
<div class="highlight"><pre><span></span><code>TTLIN1.VAL.CAPTURE_WORD? -&gt; OK =PCAP.BITS0
TTLIN1.VAL.OFFSET? -&gt; OK =0
</code></pre></div>
<p><code>PCAP.BITS0</code> is a shared 32-bit capture word — other captured <code>bit_out</code>
signals (several TTLIN/LVDSIN/encoder bits are also flagged for capture on
this layout) may occupy other bit positions of the same word, so the raw
<code>PCAP.BITS0.Value</code> is <strong>not</strong> itself a clean 0/1. <code>PandaBoxOMNY</code> masks it
down to bit 0 in <code>raw_stream_mode</code> (see <code>_GATE_BIT_OFFSET</code> in
<code>panda_box_omny.py</code>) before publishing it as <code>gate_detector_active</code> — treat
that masking as required, not optional, unless it's confirmed nothing else
shares <code>BITS0</code>. Re-run the query above (read-only, safe against live
hardware) if the PandA layout is ever rebuilt, since bit assignment is
fixed by the FPGA build and not guaranteed stable across rebuilds.</p>
</li>
<li>
<p><strong>Frame counter</strong>: <code>COUNTER1</code>, triggered off <code>TTLIN1.VAL</code>'s <strong>falling
edge</strong> (see layout below) — i.e. it increments once per <em>completed</em>
detector exposure, not once per PCAP capture tick as originally guessed
here. Captured with <code>CAPTURE=Value</code>; this is both the data-completeness
signal and the completion-detection signal (<code>frame_counter</code> in
<code>signal_alias</code>, see below, and "Completion check" in the mode-comparison
table above) — once the last expected exposure's trigger pulse has fallen,
<code>COUNTER1.OUT</code> reaches <code>num_points * frames_per_trigger</code> exactly.</p>
</li>
<li>
<p><strong>Raw analog/position capture</strong>: <code>FMC_IN.VAL1</code>, <code>FMC_IN.VAL2</code> and all four
<code>INENC1</code>-<code>4.VAL</code> are switched from aggregate capture to <code>CAPTURE=Value</code>
on this layout (confirmed 2026-09-15) — i.e. every analog/encoder channel
goes raw together, not a subset. This means the current PandA layout
cannot simultaneously produce Mode A's aggregate (Min/Max/Mean) data — see
"Open question" below.</p>
</li>
<li>
<p><strong>Continuous gate/arm</strong>: <code>CLOCK1</code>, enabled by the constant <code>ONE</code>, drives
<code>PCAP</code>'s <code>gate</code> and <code>trig</code> inputs continuously (instead of the external
per-point trigger used in Mode A), so PCAP free-runs at <code>CLOCK1</code>'s
configured rate. Testing started at <strong>100 Hz</strong> on 2026-09-15 and will be
ramped up; there is no PandA-side layout change needed to change the
rate, only <code>CLOCK1</code>'s period.</p>
<div class="highlight"><pre><span></span><code>TTLIN1 (TTL input 1, detector trigger)
├──▶ COUNTER1.trig, falling edge (Up/Down pulse counter) ──▶ COUNTER1.OUT = frame_counter (CAPTURE=Value)
│ = completion signal too: reaches num_points * frames_per_trigger when the last exposure finishes
└──▶ captured into PCAP.BITS0 bit 0 ──▶ masked in software = gate_detector_active
CLOCK1 (enabled by constant ONE) ──▶ PCAP.gate, PCAP.trig (continuous arm/capture)
</code></pre></div>
</li>
</ul>
<h3 id="possible-variant-gate-continuous-capture-by-exposure-active-proposed-not-implemented">Possible variant: gate continuous capture by exposure-active (proposed, not implemented)<a class="headerlink" href="#possible-variant-gate-continuous-capture-by-exposure-active-proposed-not-implemented" title="Permanent link">&para;</a></h3>
<p>Discussed 2026-09-19, not yet implemented or tested against hardware. Today
<code>PCAP.gate</code> and <code>PCAP.trig</code> are both driven by <code>CLOCK1.OUT</code>, so PCAP
free-runs continuously at <code>CLOCK1</code>'s rate regardless of whether the detector
is actually exposing -- including idle/dead time between exposures. The
proposal is to split the two roles so they're driven by different sources:</p>
<div class="highlight"><pre><span></span><code>CLOCK1 (enabled by constant ONE) ──▶ PCAP.trig (sample-rate clock, unchanged)
TTLIN1.VAL (detector-trigger / exposure-active) ──▶ PCAP.gate (only capture while exposing)
</code></pre></div>
<p><code>PCAP.trig</code> keeps ticking at <code>CLOCK1</code>'s configured rate exactly as today;
<code>PCAP.gate</code> switches from <code>CLOCK1.OUT</code> to <code>TTLIN1.VAL</code>, so a <code>CLOCK1</code> tick
only produces a captured row while the detector is actively exposing --
idle-time ticks are simply dropped, not written.</p>
<p>This should be a pure PandA-layout change -- no <code>ptycho_flomni.yaml</code> or
<code>panda_box_omny.py</code> change would be needed, since it doesn't change which
BEC signal names exist or what they mean:</p>
<ul>
<li>
<p><code>TTLIN1.VAL</code> is already proven usable as a <code>PCAP.gate</code> source -- it's what
Mode A already uses (<code>PCAP.GATE</code>/<code>PCAP.TRIG</code> = <code>TTLIN1.VAL</code> there).</p>
</li>
<li>
<p><code>frame_counter</code> (<code>COUNTER1</code>, clocked directly off <code>TTLIN1.VAL</code>'s falling
edge, not off <code>PCAP</code>) is unaffected -- still counts every completed
exposure and still works as the completion/grouping signal exactly as
today.</p>
</li>
<li>
<p><code>gate_detector_active</code> (<code>PCAP.BITS0</code> bit 0, also <code>TTLIN1.VAL</code>) would become
trivially always <code>1</code> on every captured row, since a row is only ever
captured while that signal is high -- harmless, since <code>frame_counter</code>
already does the actual per-exposure grouping, not the gate bit.</p>
</li>
</ul>
<p>Motivation: at 20 kHz continuous (current, ungated behavior), the
free-running raw stream produces roughly 67-86 MB/minute (~4-5 GB/hour) of
raw payload across the 10 captured fields (see
<code>panda_box_free_running_data_schema.md</code> for the field list; the range
reflects uncertainty over whether the four <code>INENC</code> channels report as
<code>int32</code> or <code>float64</code>). Gating capture to only actual exposure windows would
cut that in proportion to the detector's duty cycle -- e.g. roughly 10x less
at a 10% duty cycle -- with no BEC-side software change required.</p>
<p>If this is implemented: update this section (and the "Continuous gate/arm"
wiring diagram above) to confirmed status, record which <code>.ini</code> layout
captures it, and re-verify the <code>PCAP.BITS0</code> bit-0 assignment per the caveat
under "Gate-bit input" above (bit assignment is fixed by the FPGA build, not
guaranteed stable across a layout rebuild).</p>
<h2 id="saving-restoring-layouts">Saving / restoring layouts<a class="headerlink" href="#saving-restoring-layouts" title="Permanent link">&para;</a></h2>
<p>Use <code>ophyd_devices/devices/panda_box/utility_scripts.py</code> to save the current
PandA configuration to a <code>.ini</code> layout file and reload it later, so a
configuration survives a PandA power cycle. Both layouts are saved in this
repo under <code>csaxs_bec/devices/panda_box/layouts/</code> — <code>omny.ini</code> (Mode A) and
<code>omny_freerun.ini</code> (Mode B), captured 2026-09-15 directly from the PandA
design named <code>OMNY</code>/<code>omny_freerun</code> respectively (switch the PandA's active
Design first, then save):</p>
<div class="highlight"><pre><span></span><code>python ophyd_devices/devices/panda_box/utility_scripts.py \
--host omny-panda.psi.ch --save-layout csaxs_bec/devices/panda_box/layouts/omny.ini
python ophyd_devices/devices/panda_box/utility_scripts.py \
--host omny-panda.psi.ch --save-layout csaxs_bec/devices/panda_box/layouts/omny_freerun.ini
</code></pre></div>
<p>To restore either, swap <code>--save-layout</code> for <code>--load-layout</code> with the same
file. Diffing the two confirms the mode-specific differences documented
above: <code>PCAP.GATE</code>/<code>PCAP.TRIG</code> are <code>TTLIN1.VAL</code> in Mode A vs <code>CLOCK1.OUT</code> in
Mode B; <code>CLOCK1.ENABLE</code> is <code>ZERO</code> (off) in Mode A vs <code>ONE</code> in Mode B;
<code>INENC1</code>-<code>4.VAL.CAPTURE</code>/<code>FMC_IN.VAL1/VAL2.CAPTURE</code> are <code>Min Max Mean</code> in
Mode A vs <code>Value</code> in Mode B; <code>PCAP.BITS0.CAPTURE</code>/<code>COUNTER1.OUT.CAPTURE</code> are
<code>No</code> in Mode A vs <code>Value</code> in Mode B.</p>
<p><code>omny_freerun.ini</code> was captured with <code>CLOCK1.PERIOD=0.0001</code> (100 µs → 10 kHz)
— note this doesn't match the 20 kHz reported as tested successfully the
same day, so it may not be the exact layout state the 20 kHz test ran at;
worth double-checking before relying on this file as "the" validated
high-rate layout.</p>
<h2 id="signal-alias-mapping">Signal alias mapping<a class="headerlink" href="#signal-alias-mapping" title="Permanent link">&para;</a></h2>
<p>The physical PandA block feeding each BEC signal name, kept here and in
<code>csaxs_bec/device_configs/ptycho_flomni.yaml</code> (<code>omny_panda.deviceConfig.signal_alias</code> for
Mode A, <code>omny_panda_continuous.deviceConfig.signal_alias</code> for Mode B) in sync as a single
source of truth:</p>
<table>
<thead>
<tr>
<th>PandA block</th>
<th>Mode</th>
<th>BEC signal name</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>FMC_IN.VAL1.Min</code>/<code>.Max</code>/<code>.Mean</code></td>
<td>A</td>
<td><code>cap_voltage_fzp_y_min</code>/<code>_max</code>/<code>_mean</code></td>
</tr>
<tr>
<td><code>FMC_IN.VAL2.Min</code>/<code>.Max</code>/<code>.Mean</code></td>
<td>A</td>
<td><code>cap_voltage_fzp_x_min</code>/<code>_max</code>/<code>_mean</code></td>
</tr>
<tr>
<td><code>INENC1.VAL.Min</code>/<code>.Max</code>/<code>.Mean</code></td>
<td>A</td>
<td><code>interf_st_fzp_y_min</code>/<code>_max</code>/<code>_mean</code></td>
</tr>
<tr>
<td><code>INENC2.VAL.Min</code>/<code>.Max</code>/<code>.Mean</code></td>
<td>A</td>
<td><code>interf_st_fzp_x_min</code>/<code>_max</code>/<code>_mean</code></td>
</tr>
<tr>
<td><code>INENC3.VAL.Min</code>/<code>.Max</code>/<code>.Mean</code></td>
<td>A</td>
<td><code>interf_st_rotz_min</code>/<code>_max</code>/<code>_mean</code></td>
</tr>
<tr>
<td><code>INENC4.VAL.Min</code>/<code>.Max</code>/<code>.Mean</code></td>
<td>A</td>
<td><code>interf_st_rotx_min</code>/<code>_max</code>/<code>_mean</code></td>
</tr>
<tr>
<td><code>PCAP.GATE_DURATION.Value</code></td>
<td>A, B</td>
<td><code>pcap_gate_duration_value</code></td>
</tr>
<tr>
<td><code>FMC_IN.VAL1/VAL2.Value</code>, <code>INENC1</code>-<code>4.VAL.Value</code></td>
<td>B</td>
<td><code>cap_voltage_fzp_y</code>/<code>_x</code>, <code>interf_st_fzp_y</code>/<code>_x</code>/<code>_rotz</code>/<code>_rotx</code></td>
</tr>
<tr>
<td><code>PCAP.BITS0.Value</code> (bit 0 = <code>TTLIN1.VAL</code>, confirmed 2026-09-15; masked in software, see above)</td>
<td>B</td>
<td><code>gate_detector_active</code></td>
</tr>
<tr>
<td><code>COUNTER1.OUT.Value</code> (confirmed 2026-09-15, triggered off <code>TTLIN1.VAL</code>'s falling edge)</td>
<td>B</td>
<td><code>frame_counter</code></td>
</tr>
</tbody>
</table>
<p>The BEC-side data schema produced by Mode B (what ends up in the HDF5 async
dataset) is documented separately in
<code>docs/developer/panda_box_free_running_data_schema.md</code>.</p>
<h2 id="known-data-quality-caveat-stale-frame_counter-carryover-found-fixed-2026-09-21">Known data-quality caveat: stale <code>frame_counter</code> carryover (found + fixed 2026-09-21)<a class="headerlink" href="#known-data-quality-caveat-stale-frame_counter-carryover-found-fixed-2026-09-21" title="Permanent link">&para;</a></h2>
<p>Diagnosed against a real scan (<code>S06313</code>): grouping raw <code>interf_st_fzp_x</code>/<code>_y</code>
samples by <code>frame_counter</code> (restricted to <code>gate_detector_active == 1</code>) and
averaging per group reproduces <code>rt_positions</code>'s trusted
<code>average_x_st_fzp</code>/<code>average_y_st_fzp</code> to R² &gt; 0.9999 once a single constant
per-axis offset is removed -- an expected reference-zero difference between
the PandA/<code>INENC</code> counter and RT's own internal tracking, irrelevant to
reconstruction since it's the same constant for every point in a scan. This
also confirms the grouping/filtering method itself: naively treating every
raw row as its own position (without grouping by <code>frame_counter</code> and
filtering to <code>gate_detector_active == 1</code>) does not reproduce the true
positions and cannot be fixed by any scale/offset correction, since most raw
rows are samples taken between real exposures, not at them (see "Continuous
gate/arm" above).</p>
<p>One point out of 472 was a clear outlier under that method: its
<code>frame_counter</code> group had 38 extra samples (1036 vs. the usual ~998) and its
recomputed position was off by ~116 nm in Y, against ~5 nm typical.</p>
<p>Root cause: PandA's <code>COUNTER1.OUT</code> (the block behind <code>frame_counter</code>) is not
cleared by <code>Arm()</code>. <code>COUNTER1.ENABLE=PCAP.ACTIVE</code> only gates <em>counting</em>, not
the persisted output value, and <code>COUNTER1.SET</code> -- the block's reset-trigger
input -- is wired to the constant <code>ZERO</code> in the saved layout (see
<code>layouts/omny_freerun.ini</code>), i.e. never pulsed on arm, unlike <code>ENABLE</code> which
<em>is</em> wired to a live source (<code>PCAP.ACTIVE</code>). So the first raw samples of a new
acquisition can briefly carry a stale <code>frame_counter</code> value left over from the
previous acquisition's tail -- observed directly in <code>S06313</code>'s raw data: 36
samples reading a stale <code>167</code> before dropping to <code>0</code> and counting correctly
from there. Harmless on its own, except that stale value collided with a real
point index later in the <em>same</em> scan (point 167), so grouping by
<code>frame_counter</code> silently mixed ~38 stale rows into that one point's average.</p>
<p>Fix, implemented in <code>panda_box_omny.py</code> (software-only, no PandA layout
change needed): every time PCAP is armed -- including <code>on_pre_scan</code> retries,
since each <code>Arm()</code> reopens the same stale-value window -- <code>PandaBoxOMNY</code> now
drops any raw rows captured before it observes <code>frame_counter == 0</code> for the
first time in that acquisition (<code>_drop_stale_frame_counter_rows</code>, called from
<code>_receive_frame_data</code>), and drops any leftover buffered rows from an
abandoned previous attempt at the same point (<code>_reset_raw_stream_state</code>,
called from <code>on_stage</code> and from <code>_try_arm</code> right before every <code>Arm()</code>). Every
signal's row list is truncated at the same index to keep channels aligned.
Verified against <code>S06313</code>: dropping the (37, in that scan) leading stale rows
before grouping brings point 167's residual from ~116 nm down to ~0.1 nm, in
line with the rest of the scan.</p>
<p>A more thorough fix would rewire <code>COUNTER1.SET</code> on the PandA layout itself
(from the constant <code>ZERO</code> to <code>PCAP.ACTIVE</code>'s rising edge, matching how
<code>ENABLE</code> is already wired) so <code>OUT</code> resets at the source on every arm, rather
than relying on the client to detect and discard the stale prefix. Not
implemented here since it requires a PandA layout change to be tested against
live hardware; the software-side drop is a correct and sufficient fix in the
meantime, and remains a reasonable defense-in-depth even if the layout is
fixed later. If this is revisited: re-verify against a range of scans
(especially ones with <code>frame_counter</code> staying below a few hundred, where a
stale-value collision like point 167's is most likely) before removing the
software-side check.</p>
<p>This also means the "gap in an otherwise-monotonic sequence" completeness
check described in the mode-comparison table's "Data completeness
verification" row needs one caveat: a single backward step at the very start
of the raw sequence, from the previous acquisition's terminal value down to
<code>0</code>, is this expected (now-filtered) artifact, not a sign of dropped rows.</p>
<h2 id="conditional-arming-arm_scan_allowlist-added-2026-09-22">Conditional arming (<code>arm_scan_allowlist</code>, added 2026-09-22)<a class="headerlink" href="#conditional-arming-arm_scan_allowlist-added-2026-09-22" title="Permanent link">&para;</a></h2>
<p>Generic BEC staging (<code>stage_all_devices</code>/<code>pre_scan_all_devices</code> in
<code>bec_server.scan_server.scans.scan_actions</code>) has no concept of "this device
only matters for scan X" -- every <code>enabled: true</code> device gets <code>stage()</code>/
<code>pre_scan()</code>/<code>unstage()</code> called for <em>every</em> scan that runs (<code>grid_scan</code>,
<code>line_scan</code>, alignment moves, ...), not just the ones that actually consume
its data. <code>deviceTags</code> (e.g. <code>ptycho_flomni</code>) are never consulted by that
staging code -- they're purely a client-side grouping convenience. So
without any further gating, <code>omny_panda_continuous</code> was being <code>Arm()</code>ed and
disarmed for every single scan run during a session, even though only
<code>flomni_fermat_scan</code> reads its data. That's unnecessary hardware arm/disarm
cycling, and it directly compounds the stale-<code>frame_counter</code> bug above: more
arm cycles mean more windows where a mid-scan abort can leave a stale
counter value for the <em>next</em> acquisition to inherit (see
<code>AI_docs/PANDA_POSITION_VALIDATION.md</code> sec 7 for a real incident traced to
exactly this).</p>
<p><code>PandaBoxOMNY</code> now takes an <code>arm_scan_allowlist: list[str] | None</code> constructor
kwarg (<code>None</code> by default -- arm for every scan, unchanged behavior). When
set, <code>on_stage()</code> computes <code>_should_arm_panda</code> from
<code>scan_parameters.scan_name</code> against that list; <code>on_pre_scan()</code> skips sending
<code>Arm()</code> entirely (resolves immediately) when it's <code>False</code>, and <code>on_complete()</code>
skips the wait-for-frame-count poll loop the same way. <code>on_stage()</code>'s own
readout-thread/TCP-connection startup is untouched by this -- it doesn't
touch PandA's hardware register state (<code>COUNTER1</code>), unlike <code>Arm()</code>, so it
stays out of scope. <code>ptycho_flomni.yaml</code>'s <code>omny_panda_continuous</code> sets
<code>arm_scan_allowlist: [flomni_fermat_scan]</code>.</p>
<h2 id="switching-between-modes">Switching between modes<a class="headerlink" href="#switching-between-modes" title="Permanent link">&para;</a></h2>
<p><code>omny_panda</code> (Mode A) and <code>omny_panda_continuous</code> (Mode B) need different PandA hardware
layouts and can't run simultaneously (see "Open question" below), so exactly one should be
active in <code>ptycho_flomni.yaml</code> at a time, matching whichever layout is currently loaded on
the PandA — <strong>comment out the other one's block entirely</strong>, don't rely on <code>enabled: false</code>
alone.</p>
<p>That's a correction from earlier in this project: <code>enabled: false</code> on a device BEC has never
connected to is an inert placeholder — <code>connect_device</code> in
<code>bec_server/device_server/devices/devicemanager.py</code> is only called when <code>enabled</code> — so the
theory was that both entries could stay uncommented and switching would just be flipping the
flag. In practice (2026-09-15), disabling <code>omny_panda_continuous</code> this way was <strong>not</strong>
sufficient once it had already been connected in the running session: both devices ended up
active simultaneously against the same physical PandA. A device that's already connected
doesn't appear to get disconnected just because a later config reload marks it disabled.
Commenting the block out (so BEC never even constructs the device object) is the reliable
way to guarantee it's gone; if in doubt, confirm the other device has actually dropped its
connection (e.g. via a device server restart) rather than trusting <code>enabled: false</code> alone.</p>
<p>Confirmed end-to-end 2026-09-15: Mode B tested working (correct per-channel values, masked
<code>gate_detector_active</code>, <code>frame_counter</code>-based completion), then switched back to Mode A
(commented out) for A/B comparison testing.</p>
<h2 id="open-question-now-partially-resolved">Open question, now partially resolved<a class="headerlink" href="#open-question-now-partially-resolved" title="Permanent link">&para;</a></h2>
<p>Can both modes coexist under a single static PandA layout — i.e. is mode
switching purely the <code>raw_stream_mode</code> software flag, with the PandA always
capturing both the aggregate and raw fields simultaneously — or do Mode A
and Mode B require two distinct saved layouts that must be loaded before a
scan depending on the desired mode?</p>
<p>As currently wired for testing (2026-09-15), the answer looks like <strong>two
distinct layouts are required</strong>: <code>FMC_IN.VAL1/VAL2</code> and <code>INENC1</code>-<code>4.VAL</code> are
each set to <code>CAPTURE=Value</code> (single selection), not simultaneously
<code>CAPTURE=Value,Min,Max,Mean</code> — so this layout cannot also produce Mode A's
aggregate statistics at the same time. This has not yet been deliberately
tested against the alternative (whether the PandA capture UI actually
supports selecting <code>Value</code> together with <code>Min</code>/<code>Max</code>/<code>Mean</code> on the same
field) — if that turns out to work, a single layout could serve both modes.
Once Mode B testing is far enough along to save a named layout (see
"Saving / restoring layouts" below), settle this explicitly and record the
answer plus, if two layouts are needed, the load procedure to switch between
them.</p>
</article>
</div>
<script>var target=document.getElementById(location.hash.slice(1));target&&target.name&&(target.checked=target.name.startsWith("__tabbed_"))</script>
</div>
<button type="button" class="md-top md-icon" data-md-component="top" hidden>
<svg xmlns="http://www.w3.org/2000/svg" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" class="lucide lucide-circle-arrow-up" viewBox="0 0 24 24"><circle cx="12" cy="12" r="10"/><path d="m16 12-4-4-4 4M12 16V8"/></svg>
Back to top
</button>
</main>
<footer class="md-footer">
<div class="md-footer-meta md-typeset">
<div class="md-footer-meta__inner md-grid">
<div class="md-copyright">
<div class="md-copyright__highlight">
Copyright &copy; Paul Scherrer Institute
</div>
Made with
<a href="https://zensical.org/" target="_blank" rel="noopener">
Zensical
</a>
</div>
</div>
</div>
</footer>
</div>
<div class="md-dialog" data-md-component="dialog">
<div class="md-dialog__inner md-typeset"></div>
</div>
<script id="__config" type="application/json">{"annotate":null,"base":"..","features":["navigation.tabs","navigation.sections","navigation.top","content.code.copy","search.highlight"],"search":"../assets/javascripts/workers/search.5df7522c.min.js","tags":null,"translations":{"clipboard.copied":"Copied to clipboard","clipboard.copy":"Copy to clipboard","search.result.more.one":"1 more on this page","search.result.more.other":"# more on this page","search.result.none":"No matching documents","search.result.one":"1 matching document","search.result.other":"# matching documents","search.result.placeholder":"Type to start searching","search.result.term.missing":"Missing","select.version":"Select version"},"version":null}</script>
<script src="../assets/javascripts/bundle.21aa498e.min.js"></script>
</body>
</html>