From 632a2279852b3243c7ee2f1a09906046cb5a0755 Mon Sep 17 00:00:00 2001 From: Hayodea Hekol Date: Sat, 18 Apr 2026 12:02:27 -0400 Subject: [PATCH] DAPS: Add intrin specs to nontrin specs We no longer do intrin specs as a separate stimbuff; rather now we do them as a specifier segment within the pipeline spec of a normal nontrin spec. --- devices/avia0.dapss | 31 +-- docs/design/intrin-thresholds.md | 77 +++--- docs/design/stencils.md | 57 ++-- docs/livox-gen1-lidar-dap-spec.md | 13 +- include/user/deviceAttachmentSpec.h | 55 +++- include/user/intrin.h | 24 ++ include/user/intrinThresholdParams.h | 208 +++++---------- .../deviceAttachmentPipeSpecp.yy | 97 ++++++- stimBuffApis/livoxGen1/livoxGen1.cpp | 7 +- stimBuffApis/livoxGen1/meshStimulusBuffer.h | 5 +- .../pcloudAmbienceIntrinStimulusBuffer.h | 251 ------------------ .../livoxGen1/pcloudAmbienceQualeIfaceApi.h | 55 ++-- .../livoxGen1/pcloudAmbienceStimulusBuffer.h | 104 +++++++- .../livoxGen1/pcloudIntensityStimulusBuffer.h | 2 +- .../livoxGen1/pcloudStimulusProducer.cpp | 70 +---- .../livoxGen1/pcloudStimulusProducer.h | 5 - 16 files changed, 441 insertions(+), 620 deletions(-) create mode 100644 include/user/intrin.h delete mode 100644 stimBuffApis/livoxGen1/pcloudAmbienceIntrinStimulusBuffer.h diff --git a/devices/avia0.dapss b/devices/avia0.dapss index d78bf20..c17a939 100644 --- a/devices/avia0.dapss +++ b/devices/avia0.dapss @@ -4,23 +4,16 @@ +edev|avia0|mesh()|livoxGen1()|livoxProto1(SMO_IP)|3JEDK380010Z39|| +edev|avia0|pcloudIntensity()|livoxGen1()|livoxProto1(SMO_IP)|3JEDK380010Z39|| -+edev|avia0|pcloudAmbience()|livoxGen1()|livoxProto1(SMO_IP)|3JEDK380010Z39|| -/* Be positively disposed to dim ambience, but don't be so positively drawn that - * your eyelids droop and your pupils dilate, in human terms (i.e: no - * distraction). And no stupefaction from it either. +/* pcloudAmbience with: + * - postrin: be positively disposed to dim ambience (low passband counts), + * but don't be so positively drawn that your eyelids droop and your pupils + * dilate, in human terms (i.e: no distraction). And no stupefaction either. + * - negtrin: be negatively disposed to high ambience (high passband counts), + * to the point of feeling un-ignorable pain when it's sufficiently high. */ -+edev|avia0|postrin( - from-stimbuff=pcloudAmbience| - interest-pc=85| - passband-count-lt-val=8 - ) - |livoxGen1()|livoxProto1(SMO_IP)|3JEDK380010Z39|| -/* Be negatively disposed to high ambience, to the point of feeling un-ignorable - * pain when it's sufficiently high. - */ -+edev|avia0|negtrin( - from-stimbuff=pcloudAmbience| - interest-pc=85|distraction-pc=90|intolerable-pc=95| - passband-count-gt-val=120 - ) - |livoxGen1()|livoxProto1(SMO_IP)|3JEDK380010Z39 ++edev|avia0| + postrin(interest-pc=85)| + negtrin(interest-pc=85|distraction-pc=90|intolerable-pc=95)| + pcloudAmbience(passband-count-lt-val=8|passband-count-gt-val=120)| + livoxGen1()|livoxProto1(SMO_IP)| + 3JEDK380010Z39|| diff --git a/docs/design/intrin-thresholds.md b/docs/design/intrin-thresholds.md index b2228db..fb66dda 100644 --- a/docs/design/intrin-thresholds.md +++ b/docs/design/intrin-thresholds.md @@ -9,30 +9,33 @@ allow the stimbuff API library to determine when to construct a stencil for postrin or negtrin data and present it to SMO with appropriate importance classifications. -**Intrinsic parameters may appear only on dedicated intrinsic qualeIfaceApi -specs.** The only supported forms are **`negtrin(...)`** and **`postrin(...)`**. -You cannot attach intrinsic threshold params to a sensory or other -non-intrinsic qualeIfaceApi (for example `pcloudIntensity`, `pcloudAmbience`, -`mesh`, `pcloud`, and so on). Those lines are for sensory streams only. +**Intrinsic parameters live inside `postrin(...)` / `negtrin(...)` +segments attached to a nontrin DAP line.** A DAP line may declare at most +one `postrin(...)` segment and at most one `negtrin(...)` segment, and both +must precede the qualeIfaceApi segment. Ordering between postrin and +negtrin is free. -A dedicated intrinsic line must name the sensory stimbuff it derives from via -**`from-stimbuff=`** (non-empty). Policy validation rejects -intrinsic params and passband-style comparators on sensory lines, and rejects -dedicated lines that omit `from-stimbuff`. +Intrinsic threshold params (and the deprecated `from-stimbuff` marker) +must not appear on the qualeIfaceApi params themselves — policy +validation rejects them there. The nontrin qualeIfaceApi +(e.g. `pcloudAmbience`) still owns its own sensory params (such as +passband comparators); only the threshold classifications (`interest-*`, +`distraction-*`, `stupefaction-*` / `stupefying-*`, `intolerable-*`) live +in the postrin/negtrin segments. -## Dedicated `negtrin` / `postrin` qualeIfaceApi names +## `postrin` / `negtrin` segments -On **`negtrin(...)`** or **`postrin(...)`**, the intrinsic family (negative vs -positive) is fixed by the API name. The **`negtrin-`** and **`postrin-`** -prefixes on parameter names are **optional**: you may use short forms -**`interest-...`**, **`distraction-...`**, **`stupefaction-` / `stupefying-...`**, -and **`intolerable-...`** with the unit suffix rules below. Prefixed names such -as **`negtrin-interest-pc`** remain valid when you prefer explicit spelling. +On **`postrin(...)`** or **`negtrin(...)`**, the intrinsic family +(positive vs negative) is fixed by the segment name, so only the +unprefixed short forms **`interest-...`**, **`distraction-...`**, +**`stupefaction-` / `stupefying-...`**, and **`intolerable-...`** (with +the unit suffix rules below) are accepted. -Example (negtrin driven by ambience, short interest param, passband comparator): +Example (negtrin segment attached to ambience, with passband comparator +on the nontrin qualeIfaceApi): ```` -+edev|avia0|negtrin(from-stimbuff=pcloudAmbience|interest-pc=85|passband-count-gt-val=120)|livoxGen1()|livoxProto1(SMO_IP)|3JEDK380010Z39 ++edev|avia0|negtrin(interest-pc=85)|pcloudAmbience(passband-count-gt-val=120)|livoxGen1()|livoxProto1(SMO_IP)|3JEDK380010Z39|| ```` ## Unit Suffix Rules @@ -59,20 +62,18 @@ parameter takes precedence. Shorthand params without a unit suffix are not permitted. The following forms are invalid because they omit units: -- `[trin-]|intolerable>` +- `|intolerable>` -On dedicated **`postrin(...)`** / **`negtrin(...)`** lines, the short forms +Inside **`postrin(...)`** / **`negtrin(...)`** segments, the short forms **`interest-...`**, **`distraction-...`**, etc. still require those unit suffixes. ## Interest Threshold Parameters -**Parameter forms (only inside `negtrin(...)` or `postrin(...)`):** +**Parameter forms (only inside `postrin(...)` or `negtrin(...)` +segments):** -- Prefixed: `postrin-interest-`, - `negtrin-interest-` -- Short (prefix optional because the line is already negtrin or postrin): - `interest-` +- `interest-` **Description:** These parameters denote the value at which the stimbuff API library @@ -80,32 +81,29 @@ should construct a stencil for the postrin or negtrin that it delivers for this intrinsic spec, and present it to SMO via postrinInd/negtrinInd with the "importance" argument set to "INTERESTING". -**Example (dedicated negtrin line):** +**Example (negtrin segment attached to ambience):** ```` -+edev|avia0|negtrin(from-stimbuff=pcloudAmbience|interest-pc=85|passband-count-gt-val=120)|livoxGen1()|livoxProto1(SMO_IP)|3JEDK380010Z39 ++edev|avia0|negtrin(interest-pc=85)|pcloudAmbience(passband-count-gt-val=120)|livoxGen1()|livoxProto1(SMO_IP)|3JEDK380010Z39|| ```` ## Distraction Threshold Parameters -**Parameter forms (only inside `negtrin(...)` or `postrin(...)`):** +**Parameter forms (only inside `postrin(...)` or `negtrin(...)` +segments):** -- Prefixed: `postrin-distraction-`, - `negtrin-distraction-` -- Short: `distraction-` (family is - fixed by the `negtrin` / `postrin` API name) +- `distraction-` **Description:** These parameters denote the value at which the stimbuff API library ought to construct a stencil and deliver it to SMO via -negtrinInd/postrinInd with the importance argument set to "DISTRACTION". +postrinInd/negtrinInd with the importance argument set to "DISTRACTION". ## Stupefying Threshold Parameters (Postrin) -**Parameter forms (only inside `postrin(...)`):** +**Parameter forms (only inside `postrin(...)` segments):** -- Prefixed: `[postrin-]stupefaction-`, - `[postrin-]stupefying-` -- Short: `stupefaction-...`, `stupefying-...` with unit suffix +- `stupefaction-` +- `stupefying-` **Description:** These parameters denote the value at which the stimbuff API library @@ -114,10 +112,9 @@ the importance argument set to "STUPEFYING". ## Intolerable Threshold Parameters (Negtrin) -**Parameter forms (only inside `negtrin(...)`):** +**Parameter forms (only inside `negtrin(...)` segments):** -- Prefixed: `[negtrin-]intolerable-` -- Short: `intolerable-` +- `intolerable-` **Description:** These parameters denote the value at which the stimbuff API library diff --git a/docs/design/stencils.md b/docs/design/stencils.md index 610f7a9..5afe425 100644 --- a/docs/design/stencils.md +++ b/docs/design/stencils.md @@ -18,16 +18,15 @@ descriptors tell it exactly which body spots are raising it. This direct information also assists SMO in narrowing down the scope of its DB searches for methods to handle (increase or decrease) the stimval. For -example, for a negtrin whose intrinsic pipeline is declared with a dedicated -`negtrin(...)` spec (and whose sensory data may come from intensity or another -stimbuff via `from-stimbuff`), we don't search DB by trying to match all -possible body spots with their stimvals. Rather, we search for all the body -spots that are described in the stencil. This optimizes DB searches -and also makes negtrin relieving/postrin satisfying searches more explicit -and obviously scoped items of knowledge. The eventual solution is -automatically classified as being a method to relieve/satisfy intrins at -this point on the body and not just hazily generally for "all over the -body". We get to make the new item of knowledge more specific. +example, for a negtrin raised from a `negtrin(...)` segment attached to a +nontrin DAP line (e.g. `pcloudAmbience`), we don't search DB by trying to +match all possible body spots with their stimvals. Rather, we search for +all the body spots that are described in the stencil. This optimizes DB +searches and also makes negtrin relieving/postrin satisfying searches +more explicit and obviously scoped items of knowledge. The eventual +solution is automatically classified as being a method to relieve/satisfy +intrins at this point on the body and not just hazily generally for "all +over the body". We get to make the new item of knowledge more specific. ## Format and Allocation Model @@ -60,15 +59,14 @@ But it's fine because we don't expect the relationship between SMO and stimbuff libraries to be inimical enough for security measures like that to be non-negotiable. -## n-stencils QualeIfaceApi Parameter +## n-stencils Intrin-Segment Parameter -Where a stim buff supports it, a qualeIfaceApi parameter called `n-stencils` -tells the stimbuff how many stencils it can allocate and deliver to SMO -simultaneously for **intrinsic** delivery. Intrinsic rate limiting and stencil -counts apply to **dedicated** intrinsic qualeIfaceApi specs (`negtrin(...)`, -`postrin(...)`) when the implementation attaches them—not to sensory-only -lines such as `pcloudIntensity` or `pcloudAmbience` (see -`docs/design/intrin-thresholds.md`). +Where a stim buff supports it, a parameter called `n-stencils` tells the +stimbuff how many stencils it can allocate and deliver to SMO +simultaneously for **intrinsic** delivery. `n-stencils` belongs on the +`postrin(...)` / `negtrin(...)` segment it rate-limits — **not** on the +nontrin qualeIfaceApi params. Postrin and negtrin get independent +budgets. The stimbuff must wait until SMO returns stencils to it via postrinEventRdy or negtrinEventRdy before delivering new intrin events. Stimbuffs can deliver @@ -77,23 +75,26 @@ have been given to SMO, they must wait until SMO returns a stencil before raising new intrins. **Specification:** -- The parameter is specified as part of the `quale-iface-api-params` in the - DAP specification -- The value is an integer representing the maximum number of stencils that - can be allocated simultaneously +- The parameter is specified inside a `postrin(...)` or `negtrin(...)` + segment on a DAP line +- The value is an integer representing the maximum number of stencils + that can be allocated simultaneously for that segment's intrin path - This parameter controls rate limiting for intrin events - Stimbuffs must respect this limit and wait for stencil returns before allocating new ones -**Example (generic dedicated intrin line—shape only; names depend on device):** +**Example (generic shape — names depend on device):** ``` -+idev|my-device|negtrin(from-stimbuff=someSensoryQuale|n-stencils=4)|someStimBuffApi()|livoxProto1()|SERIAL ++idev|my-device + |negtrin(interest-pc=85|n-stencils=4)|someSensoryQuale()|someStimBuffApi() + |livoxProto1()|SERIAL ``` -The Livox Gen1 **`pcloudAmbience`** sensory line does **not** use `n-stencils`; -ambience floats are delivered in the stimulus frame buffer. If Livox adds -`n-stencils` for intrinsic pipelines, it would appear on **`negtrin(...)`** / -**`postrin(...)`** lines (with `from-stimbuff`), not on `pcloudAmbience` itself. +The Livox Gen1 **`pcloudAmbience`** sensory line does **not** use +`n-stencils`; ambience floats are delivered in the stimulus frame buffer. +If Livox adds `n-stencils` for its intrinsic pipelines, it would appear +inside the `postrin(...)` / `negtrin(...)` segments attached to +`pcloudAmbience`, not on `pcloudAmbience` itself. **Invalid (sensory qualeIfaceApi must not carry intrin-oriented params):** ``` diff --git a/docs/livox-gen1-lidar-dap-spec.md b/docs/livox-gen1-lidar-dap-spec.md index 5c26849..83a71f3 100644 --- a/docs/livox-gen1-lidar-dap-spec.md +++ b/docs/livox-gen1-lidar-dap-spec.md @@ -43,9 +43,12 @@ Each stim-buff-api is designed to work with specific stim-iface libraries that u **Stim-Buff-API**: `livoxGen1-pcloud` **Quale-Iface-API**: `pcloudAmbience` - Delivers per-dagram average intensity floats (sensory stream only). -Intrinsic thresholds, passband comparators, and `from-stimbuff` wiring are **not** configured on -`pcloudAmbience`. Use separate DAP lines with qualeIfaceApi **`negtrin(...)`** or **`postrin(...)`** -and `from-stimbuff=pcloudAmbience`; see `docs/design/intrin-thresholds.md` and the `PcloudAmbienceIntrinStimulusBuffer` path in the LivoxGen1 stim buff API. +Passband comparators (`passband-count-lt-val`, `passband-count-gt-val`) belong on +`pcloudAmbience(...)` itself, and both may appear together — `lt` drives the +postrin path, `gt` drives the negtrin path. Intrinsic threshold params +(`interest-*`, `distraction-*`, `stupefaction-*`/`stupefying-*`, `intolerable-*`) +live inside `postrin(...)` / `negtrin(...)` segments attached to the same DAP +line. See `docs/design/intrin-thresholds.md`. ### 3. Point Cloud Coordinate Data Device (Extrospector) @@ -155,9 +158,7 @@ The `livoxProto1` provider accepts the following parameters: | Stim Feature | Stim-Buff-API | Quale-Iface-API | Description | |--------------|---------------|----------------|-------------| | Point Cloud Intensity | `livoxGen1-pcloudIntensity` | `pcloudIntensity` | Light intensity/reflectivity data | -| Point Cloud Ambience | `livoxGen1-pcloud` | `pcloudAmbience` | Per-dagram average intensity vector (`float` × `n-dgrams-per-frame`) | -| Ambience-driven intrinsic (negtrin) | `livoxGen1-pcloud` | `negtrin` | Dedicated intrin pipeline; requires `from-stimbuff=pcloudAmbience` and intrin params per `docs/design/intrin-thresholds.md` | -| Ambience-driven intrinsic (postrin) | `livoxGen1-pcloud` | `postrin` | Same as negtrin row, for positive intrinsics | +| Point Cloud Ambience | `livoxGen1-pcloud` | `pcloudAmbience` | Per-dagram average intensity vector (`float` × `n-dgrams-per-frame`). Accepts `postrin(...)` / `negtrin(...)` segments on the same DAP line for intrin thresholds; see `docs/design/intrin-thresholds.md`. | | Point Cloud Coordinates | `livoxGen1-pcloud` | `pcloud` | Spatial coordinate data | | Gyroscope | `livoxGen1-gyro` | `gyro` | Angular velocity measurements | | Accelerometer | `livoxGen1-accel` | `accel` | Linear acceleration measurements | diff --git a/include/user/deviceAttachmentSpec.h b/include/user/deviceAttachmentSpec.h index a72201c..049fd05 100644 --- a/include/user/deviceAttachmentSpec.h +++ b/include/user/deviceAttachmentSpec.h @@ -12,6 +12,18 @@ namespace smo { namespace device { +/* Carrier used by the DAP spec parser to pass one parenthesized segment + * (e.g. postrin(interest-pc=85) or pcloudAmbience(...)) up the reduction + * stack; the spec_body reduction classifies segments and populates the + * DeviceAttachmentSpec. Defined here so both the parser header and consumers + * of the generated header see the type. + */ +struct DapSegment +{ + std::string name; + std::vector> params; +}; + class DeviceAttachmentSpec { public: @@ -26,6 +38,8 @@ public: { return deviceIdentifier == other.deviceIdentifier && sensorType == other.sensorType && + postrin == other.postrin && + negtrin == other.negtrin && qualeIfaceApi == other.qualeIfaceApi && stimBuffApi == other.stimBuffApi && provider == other.provider && @@ -35,6 +49,15 @@ public: public: std::string deviceIdentifier; char sensorType; + /* postrin/negtrin hold the literal segment name ("postrin" / + * "negtrin") when present, empty string when the DAP spec omits the + * corresponding intrin specifier. Params vectors carry the params from + * within the postrin(...)/negtrin(...) segment. + */ + std::string postrin; + std::vector> postrinParams; + std::string negtrin; + std::vector> negtrinParams; std::string qualeIfaceApi; std::vector> qualeIfaceApiParams; std::string stimBuffApi; @@ -43,12 +66,40 @@ public: std::vector> providerParams; std::string deviceSelector; + static void stringifyParams( + std::ostream& os, + const std::vector>& params) + { + for (const auto& param : params) + { + os << param.first; + if (!param.second.empty()) { + os << "=" << param.second; + } + os << " "; + } + } + std::string stringify() const { std::ostringstream os; os << "Device Identifier: " << deviceIdentifier - << ", Sensor Type: " << sensorType - << ", QualeIface API: " << qualeIfaceApi << ", QualeIface API Params: ("; + << ", Sensor Type: " << sensorType; + + if (!postrin.empty()) + { + os << ", Postrin Params: ("; + stringifyParams(os, postrinParams); + os << ")"; + } + if (!negtrin.empty()) + { + os << ", Negtrin Params: ("; + stringifyParams(os, negtrinParams); + os << ")"; + } + + os << ", QualeIface API: " << qualeIfaceApi << ", QualeIface API Params: ("; for (const auto& param : qualeIfaceApiParams) { os << param.first; diff --git a/include/user/intrin.h b/include/user/intrin.h new file mode 100644 index 0000000..4d350ec --- /dev/null +++ b/include/user/intrin.h @@ -0,0 +1,24 @@ +#ifndef SMO_USER_INTRIN_H +#define SMO_USER_INTRIN_H + +#include + +namespace smo { +namespace intrin { + +/* Generic intrin threshold pair. 'percentage' holds the raw percentage value + * (0-100) when the user specified the threshold in percentage form, else 0. + * 'threshold' is always the resolved absolute count (computed against the + * relevant capacity parameter, e.g. nDgramsPerFrame, when the input was + * percentage-based). + */ +struct IntrinConfig +{ + uint32_t percentage; + uint32_t threshold; +}; + +} // namespace intrin +} // namespace smo + +#endif // SMO_USER_INTRIN_H diff --git a/include/user/intrinThresholdParams.h b/include/user/intrinThresholdParams.h index ca74605..5075c37 100644 --- a/include/user/intrinThresholdParams.h +++ b/include/user/intrinThresholdParams.h @@ -27,81 +27,41 @@ struct ParsedThresholdParam bool wasSpecified; }; -inline constexpr std::array kPosIntPcParamNames = { - "postrin-interest-percentage", - "postrin-interest-pc", -}; - -inline constexpr std::array kPosIntThrParamNames = { - "postrin-interest-threshold", - "postrin-interest-thresh", - "postrin-interest-thr", -}; - -inline constexpr std::array kNegIntPcParamNames = { - "negtrin-interest-percentage", - "negtrin-interest-pc", -}; - -inline constexpr std::array kNegIntThrParamNames = { - "negtrin-interest-threshold", - "negtrin-interest-thresh", - "negtrin-interest-thr", -}; - -// Short interest-* names: only valid on dedicated postrin/negtrin qualeIfaceApi -// lines (docs/design/intrin-thresholds.md); never on sensory qualeIfaceApi specs. -inline constexpr std::array kIntrinInterestPcUnprefixed = { +/* Canonical unprefixed threshold-param names that live inside postrin(...) / + * negtrin(...) segments attached to a nontrin DAP spec. The "-pc" and + * "-percentage" variants are Percentage-unit; the "-thr", "-thresh" and + * "-threshold" variants are Absolute-unit. + */ +inline constexpr std::array kIntrinInterestPcNames = { "interest-percentage", "interest-pc", }; -inline constexpr std::array kIntrinInterestThrUnprefixed = { +inline constexpr std::array kIntrinInterestThrNames = { "interest-threshold", "interest-thresh", "interest-thr", }; -inline constexpr std::array kPosDistPcParamNames = { - "postrin-distraction-percentage", - "postrin-distraction-pc", +inline constexpr std::array kIntrinDistractionPcNames = { + "distraction-percentage", + "distraction-pc", }; -inline constexpr std::array kPosDistThrParamNames = { - "postrin-distraction-threshold", - "postrin-distraction-thresh", - "postrin-distraction-thr", +inline constexpr std::array kIntrinDistractionThrNames = { + "distraction-threshold", + "distraction-thresh", + "distraction-thr", }; -inline constexpr std::array kNegDistPcParamNames = { - "negtrin-distraction-percentage", - "negtrin-distraction-pc", -}; - -inline constexpr std::array kNegDistThrParamNames = { - "negtrin-distraction-threshold", - "negtrin-distraction-thresh", - "negtrin-distraction-thr", -}; - -inline constexpr std::array kStupefactionPcParamNames = { - "postrin-stupefaction-percentage", - "postrin-stupefaction-pc", - "postrin-stupefying-percentage", - "postrin-stupefying-pc", +inline constexpr std::array kIntrinStupefactionPcNames = { "stupefaction-percentage", "stupefaction-pc", "stupefying-percentage", "stupefying-pc", }; -inline constexpr std::array kStupefactionThrParamNames = { - "postrin-stupefaction-threshold", - "postrin-stupefaction-thresh", - "postrin-stupefaction-thr", - "postrin-stupefying-threshold", - "postrin-stupefying-thresh", - "postrin-stupefying-thr", +inline constexpr std::array kIntrinStupefactionThrNames = { "stupefaction-threshold", "stupefaction-thresh", "stupefaction-thr", @@ -110,37 +70,30 @@ inline constexpr std::array kStupefactionThrParamNames = { "stupefying-thr", }; -inline constexpr std::array kIntolerablePcParamNames = { - "negtrin-intolerable-percentage", - "negtrin-intolerable-pc", +inline constexpr std::array kIntrinIntolerablePcNames = { "intolerable-percentage", "intolerable-pc", }; -inline constexpr std::array kIntolerableThrParamNames = { - "negtrin-intolerable-threshold", - "negtrin-intolerable-thresh", - "negtrin-intolerable-thr", +inline constexpr std::array kIntrinIntolerableThrNames = { "intolerable-threshold", "intolerable-thresh", "intolerable-thr", }; -inline constexpr std::array kForbiddenUnitlessIntrinParamNames = { - "postrin-interest", - "negtrin-interest", - "postrin-distraction", - "negtrin-distraction", - "postrin-stupefaction", - "postrin-stupefying", +/* Unitless stems are invalid — authors must name the unit via -pc/-percentage + * or -thr/-thresh/-threshold. We reject bare "interest", "distraction", etc. + */ +inline constexpr std::array kForbiddenUnitlessIntrinStems = { + "interest", + "distraction", "stupefaction", "stupefying", - "negtrin-intolerable", "intolerable", }; template -bool arrayContains( +inline bool arrayContains( const std::array& names, std::string_view candidate) { @@ -152,7 +105,7 @@ bool arrayContains( } template -bool namesContain( +inline bool namesContain( const NameCollectionT& names, std::string_view candidate) { @@ -163,101 +116,80 @@ bool namesContain( return false; } -inline bool isDedicatedIntrinsQualeIfaceApi(std::string_view qualeIfaceApi) +inline bool isKnownIntrinThresholdParamName(std::string_view name) { - return qualeIfaceApi == "negtrin" || qualeIfaceApi == "postrin"; + return namesContain(kIntrinInterestPcNames, name) + || namesContain(kIntrinInterestThrNames, name) + || namesContain(kIntrinDistractionPcNames, name) + || namesContain(kIntrinDistractionThrNames, name) + || namesContain(kIntrinStupefactionPcNames, name) + || namesContain(kIntrinStupefactionThrNames, name) + || namesContain(kIntrinIntolerablePcNames, name) + || namesContain(kIntrinIntolerableThrNames, name); } -inline bool isKnownIntrinsPipelineParamName(std::string_view name) -{ - return namesContain(kPosIntPcParamNames, name) - || namesContain(kPosIntThrParamNames, name) - || namesContain(kNegIntPcParamNames, name) - || namesContain(kNegIntThrParamNames, name) - || namesContain(kIntrinInterestPcUnprefixed, name) - || namesContain(kIntrinInterestThrUnprefixed, name) - || namesContain(kPosDistPcParamNames, name) - || namesContain(kPosDistThrParamNames, name) - || namesContain(kNegDistPcParamNames, name) - || namesContain(kNegDistThrParamNames, name) - || namesContain(kStupefactionPcParamNames, name) - || namesContain(kStupefactionThrParamNames, name) - || namesContain(kIntolerablePcParamNames, name) - || namesContain(kIntolerableThrParamNames, name) - || name == "passband-count-gt-val" - || name == "passband-count-lt-val"; -} - -inline bool hasNonEmptyFromStimbuffParam( - const std::vector>& params) -{ - for (const auto& [key, value] : params) - { - if (key == "from-stimbuff" && !value.empty()) - { return true; } - } - - return false; -} - -/** - * Enforces dedicated-only intrin specs: intrinsic thresholds and passband - * comparators appear only on negtrin/postrin qualeIfaceApi specs with - * from-stimbuff. Embedding intrins in other qualeIfaceApi parameter lists is - * rejected (docs/design/intrin-thresholds.md). +/* Intrin threshold params (interest-*, distraction-*, stupefaction-*, + * intolerable-*) must appear only inside postrin(...) / negtrin(...) segments + * on a DAP spec, never on qualeIfaceApi params. The deprecated `from-stimbuff` + * marker is also rejected — postrin/negtrin are now directly attached to the + * nontrin stimbuff they trigger from. */ -inline void validateIntrinsQualeApiPolicy( +inline void validateNoIntrinParamsOnQualeIface( const std::string& qualeIfaceApi, const std::vector>& params) { - if (isDedicatedIntrinsQualeIfaceApi(qualeIfaceApi)) - { - if (!hasNonEmptyFromStimbuffParam(params)) - { - throw std::runtime_error( - "qualeIfaceApi '" + qualeIfaceApi + "' requires a non-empty " - "'from-stimbuff=' parameter naming the " - "sensory stimbuff that feeds this intrinsic pipeline."); - } - - return; - } - for (const auto& [name, value] : params) { (void)value; - if (isKnownIntrinsPipelineParamName(name)) + if (isKnownIntrinThresholdParamName(name)) { throw std::runtime_error( - "Intrinsic threshold and passband comparator params must not " - "appear on qualeIfaceApi '" + qualeIfaceApi + "'. Use dedicated " - "negtrin(...) or postrin(...) lines with from-stimbuff=... " - "(offending param: '" + name + "')."); + "Intrinsic threshold param '" + name + "' is not valid on " + "qualeIfaceApi '" + qualeIfaceApi + "'. Declare it inside a " + "postrin(...) or negtrin(...) segment attached to this DAP " + "spec."); + } + + if (arrayContains(kForbiddenUnitlessIntrinStems, name)) + { + throw std::runtime_error( + "Intrinsic threshold param '" + name + "' on qualeIfaceApi '" + + qualeIfaceApi + "' is invalid without a unit suffix and " + "does not belong on qualeIfaceApi params anyway. Use a " + "postrin(...) or negtrin(...) segment with a '-pc' or " + "'-thr' suffix."); } if (name == "from-stimbuff") { throw std::runtime_error( - "'from-stimbuff' is only valid on negtrin(...) or postrin(...) " - "qualeIfaceApi specs."); + "'from-stimbuff' is no longer supported. postrin(...) and " + "negtrin(...) are now attached directly to the nontrin DAP " + "spec they trigger from; remove 'from-stimbuff' from " + "qualeIfaceApi '" + qualeIfaceApi + "'."); } } } -inline void validateNoForbiddenUnitlessIntrinParams( +/* Accepts only unprefixed threshold names and the two passband modifiers that + * are defined on the sensory side. Rejects unit-less stems so that mis-typed + * params fail loudly. + */ +inline void validateIntrinSegmentParams( + std::string_view intrinKind, // "postrin" or "negtrin" const std::vector>& params) { for (const auto& [name, value] : params) { (void)value; - if (arrayContains(kForbiddenUnitlessIntrinParamNames, name)) + if (arrayContains(kForbiddenUnitlessIntrinStems, name)) { throw std::runtime_error( - "Intrinsic threshold param '" + name - + "' is invalid without a unit suffix. Use a " - "'-percentage'/'-pc' or '-threshold'/'-thresh'/'-thr' variant."); + std::string(intrinKind) + "(...) param '" + name + "' is " + "invalid without a unit suffix. Use '-pc'/'-percentage' or " + "'-thr'/'-thresh'/'-threshold'."); } } } diff --git a/smocore/deviceManager/deviceAttachmentPipeSpecp.yy b/smocore/deviceManager/deviceAttachmentPipeSpecp.yy index f93d09a..1d8093e 100644 --- a/smocore/deviceManager/deviceAttachmentPipeSpecp.yy +++ b/smocore/deviceManager/deviceAttachmentPipeSpecp.yy @@ -51,6 +51,8 @@ void yyerror(const char *message) smo::device::ExtrospectorDevAttachmentSpec* extrospectorSpec; std::vector>* paramVector; std::pair* param; + smo::device::DapSegment* DapSpecSegment; + std::vector* DapSegmentVector; } %token STRING @@ -64,6 +66,8 @@ void yyerror(const char *message) %type spec_body %type interoceptor_spec %type extrospector_spec +%type specifier_segment +%type specifier_segments %% @@ -106,20 +110,91 @@ extrospector_spec: ; spec_body: - STRING PIPE STRING LPAREN opt_params RPAREN PIPE STRING LPAREN opt_params RPAREN PIPE STRING LPAREN opt_params RPAREN PIPE STRING { + STRING PIPE specifier_segments PIPE STRING { + auto segments = std::unique_ptr>($3); + + if (segments->size() < 3 || segments->size() > 5) + { + throw std::runtime_error( + "DAP spec for device '" + std::string($1) + "' must declare " + "qualeIfaceApi, stimBuffApi and provider (optionally preceded " + "by up to one postrin and one negtrin segment); got " + + std::to_string(segments->size()) + " pre-devSelector segments."); + } + + const size_t nIntrins = segments->size() - 3; + $$ = new smo::device::DeviceAttachmentSpec(); $$->deviceIdentifier = std::string($1); $$->sensorType = '\0'; // This will be set by the parent rule - $$->qualeIfaceApi = std::string($3); - $$->qualeIfaceApiParams = std::move(*$5); - $$->stimBuffApi = std::string($8); - $$->stimBuffApiParams = std::move(*$10); - $$->provider = std::string($13); - $$->providerParams = std::move(*$15); - $$->deviceSelector = std::string($18); - delete $5; - delete $10; - delete $15; + + for (size_t i = 0; i < nIntrins; ++i) + { + auto& seg = (*segments)[i]; + if (seg.name == "postrin") + { + if (!$$->postrin.empty()) + { + throw std::runtime_error( + "DAP spec for device '" + $$->deviceIdentifier + + "' declares more than one postrin segment."); + } + $$->postrin = seg.name; + $$->postrinParams = std::move(seg.params); + } + else if (seg.name == "negtrin") + { + if (!$$->negtrin.empty()) + { + throw std::runtime_error( + "DAP spec for device '" + $$->deviceIdentifier + + "' declares more than one negtrin segment."); + } + $$->negtrin = seg.name; + $$->negtrinParams = std::move(seg.params); + } + else + { + throw std::runtime_error( + "DAP spec segment before qualeIfaceApi must be 'postrin' " + "or 'negtrin' (got '" + seg.name + "') for device '" + + $$->deviceIdentifier + "'."); + } + } + + auto& qSeg = (*segments)[nIntrins]; + auto& sSeg = (*segments)[nIntrins + 1]; + auto& pSeg = (*segments)[nIntrins + 2]; + + $$->qualeIfaceApi = std::move(qSeg.name); + $$->qualeIfaceApiParams = std::move(qSeg.params); + $$->stimBuffApi = std::move(sSeg.name); + $$->stimBuffApiParams = std::move(sSeg.params); + $$->provider = std::move(pSeg.name); + $$->providerParams = std::move(pSeg.params); + $$->deviceSelector = std::string($5); + } + ; + +specifier_segments: + specifier_segment { + $$ = new std::vector(); + $$->push_back(std::move(*$1)); + delete $1; + } + | specifier_segments PIPE specifier_segment { + $$ = $1; + $$->push_back(std::move(*$3)); + delete $3; + } + ; + +specifier_segment: + STRING LPAREN opt_params RPAREN { + $$ = new smo::device::DapSegment(); + $$->name = std::string($1); + $$->params = std::move(*$3); + delete $3; } ; diff --git a/stimBuffApis/livoxGen1/livoxGen1.cpp b/stimBuffApis/livoxGen1/livoxGen1.cpp index c051434..f5784c3 100644 --- a/stimBuffApis/livoxGen1/livoxGen1.cpp +++ b/stimBuffApis/livoxGen1/livoxGen1.cpp @@ -514,8 +514,6 @@ static const StimBuffApiDesc livoxGen1ApiDesc = { {.name = "mesh"}, {.name = "pcloudIntensity"}, {.name = "pcloudAmbience"}, - {.name = "negtrin"}, - {.name = "postrin"}, {.name = "gyro"}, {.name = "accel"} }, @@ -649,7 +647,7 @@ extern "C" void livoxGen1_attachDeviceReq( } try { - smo::intrin::validateIntrinsQualeApiPolicy( + smo::intrin::validateNoIntrinParamsOnQualeIface( desc->qualeIfaceApi, desc->qualeIfaceApiParams); } catch (const std::runtime_error& e) @@ -664,8 +662,7 @@ extern "C" void livoxGen1_attachDeviceReq( // Unknown qualeIfaceApi std::cerr << __func__ << ": Unsupported qualeIfaceApi '" << qualeIfaceApi << "' for LivoxGen1. " - "Supported values: mesh, pcloudIntensity, pcloudAmbience, " - "negtrin, postrin" + "Supported values: mesh, pcloudIntensity, pcloudAmbience" << std::endl; cb.callbackFn(false, desc); return; diff --git a/stimBuffApis/livoxGen1/meshStimulusBuffer.h b/stimBuffApis/livoxGen1/meshStimulusBuffer.h index 9f42b1b..4a53492 100644 --- a/stimBuffApis/livoxGen1/meshStimulusBuffer.h +++ b/stimBuffApis/livoxGen1/meshStimulusBuffer.h @@ -15,7 +15,8 @@ class StimulusProducer; /** * MeshStimulusBuffer is a specialized StimulusBuffer for mesh data. * Intrinsic threshold params are not allowed on mesh qualeIfaceApi lines; - * use dedicated negtrin/postrin specs (docs/design/intrin-thresholds.md). + * attach postrin(...) / negtrin(...) specifiers to a pcloudAmbience nontrin + * spec instead. */ class MeshStimulusBuffer : public StimulusBuffer @@ -34,7 +35,7 @@ public: inputEngineConstraints, outputEngineConstraints, callbacks, flags) { - intrin::validateIntrinsQualeApiPolicy( + intrin::validateNoIntrinParamsOnQualeIface( deviceAttachmentSpec->qualeIfaceApi, deviceAttachmentSpec->qualeIfaceApiParams); } diff --git a/stimBuffApis/livoxGen1/pcloudAmbienceIntrinStimulusBuffer.h b/stimBuffApis/livoxGen1/pcloudAmbienceIntrinStimulusBuffer.h deleted file mode 100644 index 2f54026..0000000 --- a/stimBuffApis/livoxGen1/pcloudAmbienceIntrinStimulusBuffer.h +++ /dev/null @@ -1,251 +0,0 @@ -#ifndef _LIVOX_GEN1_PCLOUD_AMBIENCE_INTRIN_STIMULUS_BUFFER_H -#define _LIVOX_GEN1_PCLOUD_AMBIENCE_INTRIN_STIMULUS_BUFFER_H - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "pcloudAmbienceQualeIfaceApi.h" - -namespace smo { -namespace stim_buff { - -enum class IntrinStatus -{ - DISABLED, - NEGTRIN, - POSTRIN, -}; - -struct ParsedAmbienceIntrinConfig -{ - IntrinStatus status; - uint32_t interestPercentage; - uint32_t interestThreshold; -}; - -inline bool isAmbienceIntrinEnabled(IntrinStatus intrinStatus) -{ - return intrinStatus != IntrinStatus::DISABLED; -} - -inline intrin::ParsedThresholdParam parseAmbienceThresholdParam( - const std::string& paramName, - const std::string& paramValue, - intrin::ThresholdUnit unit) -{ - try - { - return intrin::ParsedThresholdParam{ - .value = std::stoi(paramValue), - .unit = unit, - .matchedName = paramName, - .wasSpecified = true, - }; - } - catch (const std::exception& e) - { - throw std::runtime_error( - "Failed to parse '" + paramName + "' param value '" + paramValue - + "' as integer: " + e.what()); - } -} - -inline ParsedAmbienceIntrinConfig buildAmbienceIntrinConfig( - IntrinStatus status, - const intrin::ParsedThresholdParam& thresholdParam, - size_t nDgramsPerFrame) -{ - return ParsedAmbienceIntrinConfig{ - .status = status, - .interestPercentage = - thresholdParam.unit == intrin::ThresholdUnit::Percentage - ? static_cast(thresholdParam.value) - : 0U, - .interestThreshold = intrin::resolveThresholdValue( - thresholdParam, nDgramsPerFrame), - }; -} - -inline std::optional tryParseAmbienceIntrinConfig( - const std::string& paramName, - const std::string& paramValue, - size_t nDgramsPerFrame, - const std::string& qualeIfaceApi) -{ - using intrin::ThresholdUnit; - - const bool apiIsPostrin = (qualeIfaceApi == "postrin"); - const bool apiIsNegtrin = (qualeIfaceApi == "negtrin"); - - if (intrin::namesContain(intrin::kPosIntPcParamNames, paramName) - || (apiIsPostrin - && intrin::namesContain( - intrin::kIntrinInterestPcUnprefixed, paramName))) - { - return buildAmbienceIntrinConfig( - IntrinStatus::POSTRIN, - parseAmbienceThresholdParam( - paramName, paramValue, ThresholdUnit::Percentage), - nDgramsPerFrame); - } - - if (intrin::namesContain(intrin::kPosIntThrParamNames, paramName) - || (apiIsPostrin - && intrin::namesContain( - intrin::kIntrinInterestThrUnprefixed, paramName))) - { - return buildAmbienceIntrinConfig( - IntrinStatus::POSTRIN, - parseAmbienceThresholdParam( - paramName, paramValue, ThresholdUnit::Absolute), - nDgramsPerFrame); - } - - if (intrin::namesContain(intrin::kNegIntPcParamNames, paramName) - || (apiIsNegtrin - && intrin::namesContain( - intrin::kIntrinInterestPcUnprefixed, paramName))) - { - return buildAmbienceIntrinConfig( - IntrinStatus::NEGTRIN, - parseAmbienceThresholdParam( - paramName, paramValue, ThresholdUnit::Percentage), - nDgramsPerFrame); - } - - if (intrin::namesContain(intrin::kNegIntThrParamNames, paramName) - || (apiIsNegtrin - && intrin::namesContain( - intrin::kIntrinInterestThrUnprefixed, paramName))) - { - return buildAmbienceIntrinConfig( - IntrinStatus::NEGTRIN, - parseAmbienceThresholdParam( - paramName, paramValue, ThresholdUnit::Absolute), - nDgramsPerFrame); - } - - return std::nullopt; -} - -inline ParsedAmbienceIntrinConfig parseAmbienceIntrinConfig( - const std::shared_ptr& deviceAttachmentSpec, - size_t nDgramsPerFrame) -{ - const auto& params = deviceAttachmentSpec->qualeIfaceApiParams; - - for (auto paramIt = params.rbegin(); paramIt != params.rend(); ++paramIt) - { - const auto& [paramName, paramValue] = *paramIt; - const auto parsedConfig = tryParseAmbienceIntrinConfig( - paramName, - paramValue, - nDgramsPerFrame, - deviceAttachmentSpec->qualeIfaceApi); - if (parsedConfig.has_value()) - { return parsedConfig.value(); } - } - - return ParsedAmbienceIntrinConfig{ - .status = IntrinStatus::DISABLED, - .interestPercentage = 0U, - .interestThreshold = 0U, - }; -} - -inline void validateAmbienceIntrinComparatorConfig( - IntrinStatus intrinStatus, - const std::optional& passbandCountComparator) -{ - if (isAmbienceIntrinEnabled(intrinStatus) - && !passbandCountComparator.has_value()) - { - throw std::runtime_error( - "A PcloudAmbience intrinsic pipeline with an intrin threshold " - "must also specify either 'passband-count-gt-val' or " - "'passband-count-lt-val'"); - } -} - -class StimulusProducer; - -/** - * Intrinsic pipeline for LivoxGen1 ambience: attaches as qualeIfaceApi - * negtrin(...) or postrin(...) with from-stimbuff=pcloudAmbience. Parses - * interest thresholds and passband comparators; sensory data still flows - * through PcloudAmbienceStimulusBuffer. - */ -class PcloudAmbienceIntrinStimulusBuffer -: public StimulusBuffer -{ -public: - explicit PcloudAmbienceIntrinStimulusBuffer( - StimulusProducer& parent, - const std::shared_ptr& deviceAttachmentSpec, - int histbuffMs, - const StagingBuffer::IOEngineConstraints& inputEngineConstraints, - const StagingBuffer::IOEngineConstraints& outputEngineConstraints, - const SmoCallbacks& callbacks, - cl_mem_flags flags, - size_t nDgramsPerFrame_) - : StimulusBuffer( - parent, deviceAttachmentSpec, histbuffMs, - inputEngineConstraints, outputEngineConstraints, - callbacks, flags), - nDgramsPerFrame(nDgramsPerFrame_) - { - intrin::validateIntrinsQualeApiPolicy( - deviceAttachmentSpec->qualeIfaceApi, - deviceAttachmentSpec->qualeIfaceApiParams); - intrin::validateNoForbiddenUnitlessIntrinParams( - deviceAttachmentSpec->qualeIfaceApiParams); - - const auto intrinConfig = parseAmbienceIntrinConfig( - deviceAttachmentSpec, nDgramsPerFrame_); - intrinStatus = intrinConfig.status; - intrinInterestPercentage = intrinConfig.interestPercentage; - intrinInterestThreshold = intrinConfig.interestThreshold; - - passbandCountComparator = parseOptionalPcloudAmbienceParamComparator( - deviceAttachmentSpec); - validateAmbienceIntrinComparatorConfig( - intrinStatus, passbandCountComparator); - } - - ~PcloudAmbienceIntrinStimulusBuffer() = default; - - bool shouldTriggerIntrinEvent(uint32_t ambiencePassbandCount) const - { - if (!isAmbienceIntrinEnabled(intrinStatus)) - { return false; } - - return ambiencePassbandCount >= intrinInterestThreshold; - } - - PcloudAmbienceIntrinStimulusBuffer( - const PcloudAmbienceIntrinStimulusBuffer&) = delete; - PcloudAmbienceIntrinStimulusBuffer& operator=( - const PcloudAmbienceIntrinStimulusBuffer&) = delete; - PcloudAmbienceIntrinStimulusBuffer(PcloudAmbienceIntrinStimulusBuffer&&) = - delete; - PcloudAmbienceIntrinStimulusBuffer& operator=( - PcloudAmbienceIntrinStimulusBuffer&&) = delete; - -public: - IntrinStatus intrinStatus; - uint32_t intrinInterestPercentage; - uint32_t intrinInterestThreshold; - std::optional passbandCountComparator; - size_t nDgramsPerFrame; -}; - -} // namespace stim_buff -} // namespace smo - -#endif // _LIVOX_GEN1_PCLOUD_AMBIENCE_INTRIN_STIMULUS_BUFFER_H diff --git a/stimBuffApis/livoxGen1/pcloudAmbienceQualeIfaceApi.h b/stimBuffApis/livoxGen1/pcloudAmbienceQualeIfaceApi.h index 70a1b2f..e9c59a6 100644 --- a/stimBuffApis/livoxGen1/pcloudAmbienceQualeIfaceApi.h +++ b/stimBuffApis/livoxGen1/pcloudAmbienceQualeIfaceApi.h @@ -7,32 +7,10 @@ #include #include #include -#include namespace smo { namespace stim_buff { -inline std::string parseRequiredFromStimbuffQualeIfaceName( - const std::vector>& params) -{ - for (auto it = params.rbegin(); it != params.rend(); ++it) - { - if (it->first != "from-stimbuff") - { continue; } - - if (it->second.empty()) - { - throw std::runtime_error( - "'from-stimbuff' must name a non-empty sensory qualeIfaceApi"); - } - - return it->second; - } - - throw std::runtime_error( - "internal: 'from-stimbuff' missing after intrin policy validation"); -} - enum ParamComparatorOp { OP_CMP_GT, @@ -58,7 +36,19 @@ struct ParamComparator } }; -inline std::optional parseOptionalPcloudAmbienceParamComparator( +struct PcloudAmbiencePassbandComparators +{ + std::optional lt; + std::optional gt; +}; + +/* Both `passband-count-lt-val` and `passband-count-gt-val` are permitted + * simultaneously on a pcloudAmbience qualeIfaceApi: the lt comparator + * typically feeds a postrin(...) segment (triggering on unusually low + * counts), and the gt comparator typically feeds a negtrin(...) segment + * (triggering on unusually high counts). + */ +inline PcloudAmbiencePassbandComparators parsePcloudAmbiencePassbandComparators( const std::shared_ptr& deviceAttachmentSpec) { const auto& params = deviceAttachmentSpec->qualeIfaceApiParams; @@ -68,30 +58,23 @@ inline std::optional parseOptionalPcloudAmbienceParamComparator const int ltVal = device::DeviceAttachmentSpec::parseOptionalParamAsInt( params, "passband-count-lt-val", kParamNotSpecified); - if (gtVal != kParamNotSpecified && ltVal != kParamNotSpecified) - { - throw std::runtime_error( - "Only one of 'passband-count-gt-val' or 'passband-count-lt-val' " - "may be specified for a PcloudAmbience intrinsic pipeline"); - } - + PcloudAmbiencePassbandComparators out; if (gtVal != kParamNotSpecified) { - return std::optional(ParamComparator{ + out.gt = ParamComparator{ .op = OP_CMP_GT, .value = static_cast(gtVal), - }); + }; } - if (ltVal != kParamNotSpecified) { - return std::optional(ParamComparator{ + out.lt = ParamComparator{ .op = OP_CMP_LT, .value = static_cast(ltVal), - }); + }; } - return std::nullopt; + return out; } } // namespace stim_buff diff --git a/stimBuffApis/livoxGen1/pcloudAmbienceStimulusBuffer.h b/stimBuffApis/livoxGen1/pcloudAmbienceStimulusBuffer.h index 3b0e347..e75dfaf 100644 --- a/stimBuffApis/livoxGen1/pcloudAmbienceStimulusBuffer.h +++ b/stimBuffApis/livoxGen1/pcloudAmbienceStimulusBuffer.h @@ -1,23 +1,58 @@ #ifndef _LIVOX_GEN1_PCLOUD_AMBIENCE_STIMULUS_BUFFER_H #define _LIVOX_GEN1_PCLOUD_AMBIENCE_STIMULUS_BUFFER_H -#include #include -#include -#include +#include +#include +#include +#include #include +#include #include +#include +#include +#include "pcloudAmbienceQualeIfaceApi.h" namespace smo { namespace stim_buff { class StimulusProducer; +inline intrin::IntrinConfig parseAmbienceIntrinConfigFromParams( + std::string_view intrinKind, + const std::vector>& params, + size_t nDgramsPerFrame) +{ + intrin::validateIntrinSegmentParams(intrinKind, params); + + const auto threshold = intrin::parseOptionalThresholdParam( + params, + intrin::kIntrinInterestPcNames, + intrin::kIntrinInterestThrNames, + /*defaultValue=*/0, + /*defaultUnit=*/intrin::ThresholdUnit::Absolute); + + return intrin::IntrinConfig{ + .percentage = + threshold.unit == intrin::ThresholdUnit::Percentage + ? static_cast(threshold.value) + : 0U, + .threshold = intrin::resolveThresholdValue( + threshold, nDgramsPerFrame), + }; +} + /** - * Sensory PcloudAmbience buffer: per-dgram ambience floats only. - * Intrinsic thresholds and passband comparators are only valid on dedicated - * negtrin/postrin qualeIfaceApi specs (PcloudAmbienceIntrinStimulusBuffer); - * see docs/design/intrin-thresholds.md. + * Sensory PcloudAmbience buffer: per-dgram ambience floats. A DAP spec may + * optionally attach a postrin(...) and/or a negtrin(...) specifier to this + * qualeIfaceApi; when present, interest thresholds from those specifiers and + * passband-count comparators from this spec's own qualeIfaceApi params are + * combined to decide when an intrin event should fire. + * + * Convention: the postrin pipeline pairs with passband-count-lt-val (a sparse + * ambient scene being "too good, stay here"); the negtrin pipeline pairs with + * passband-count-gt-val (a dense ambient scene being "unbearably much, get + * away"). Both comparators may be specified simultaneously on this qualeIface. */ class PcloudAmbienceStimulusBuffer : public StimulusBuffer @@ -38,9 +73,46 @@ public: callbacks, flags), nDgramsPerFrame(nDgramsPerFrame_) { - intrin::validateIntrinsQualeApiPolicy( + intrin::validateNoIntrinParamsOnQualeIface( deviceAttachmentSpec->qualeIfaceApi, deviceAttachmentSpec->qualeIfaceApiParams); + + const auto passbandComparators = + parsePcloudAmbiencePassbandComparators(deviceAttachmentSpec); + passbandCountLtComparator = passbandComparators.lt; + passbandCountGtComparator = passbandComparators.gt; + + if (!deviceAttachmentSpec->postrin.empty()) + { + postrinInterestConfig = parseAmbienceIntrinConfigFromParams( + "postrin", + deviceAttachmentSpec->postrinParams, + nDgramsPerFrame_); + + if (!passbandCountLtComparator.has_value()) + { + throw std::runtime_error( + "pcloudAmbience DAP spec declares a postrin(...) but no " + "'passband-count-lt-val' on the pcloudAmbience qualeIface " + "params to feed it."); + } + } + + if (!deviceAttachmentSpec->negtrin.empty()) + { + negtrinInterestConfig = parseAmbienceIntrinConfigFromParams( + "negtrin", + deviceAttachmentSpec->negtrinParams, + nDgramsPerFrame_); + + if (!passbandCountGtComparator.has_value()) + { + throw std::runtime_error( + "pcloudAmbience DAP spec declares a negtrin(...) but no " + "'passband-count-gt-val' on the pcloudAmbience qualeIface " + "params to feed it."); + } + } } ~PcloudAmbienceStimulusBuffer() = default; @@ -52,8 +124,24 @@ public: PcloudAmbienceStimulusBuffer& operator=( PcloudAmbienceStimulusBuffer&&) = delete; + bool shouldTriggerPostrinEvent(uint32_t ambiencePassbandCount) const + { + if (!postrinInterestConfig.has_value()) { return false; } + return ambiencePassbandCount >= postrinInterestConfig->threshold; + } + + bool shouldTriggerNegtrinEvent(uint32_t ambiencePassbandCount) const + { + if (!negtrinInterestConfig.has_value()) { return false; } + return ambiencePassbandCount >= negtrinInterestConfig->threshold; + } + public: size_t nDgramsPerFrame; + std::optional postrinInterestConfig; + std::optional negtrinInterestConfig; + std::optional passbandCountLtComparator; + std::optional passbandCountGtComparator; }; } // namespace stim_buff diff --git a/stimBuffApis/livoxGen1/pcloudIntensityStimulusBuffer.h b/stimBuffApis/livoxGen1/pcloudIntensityStimulusBuffer.h index 3bdae68..d369c51 100644 --- a/stimBuffApis/livoxGen1/pcloudIntensityStimulusBuffer.h +++ b/stimBuffApis/livoxGen1/pcloudIntensityStimulusBuffer.h @@ -33,7 +33,7 @@ public: inputEngineConstraints, outputEngineConstraints, callbacks, flags) { - intrin::validateIntrinsQualeApiPolicy( + intrin::validateNoIntrinParamsOnQualeIface( deviceAttachmentSpec->qualeIfaceApi, deviceAttachmentSpec->qualeIfaceApiParams); } diff --git a/stimBuffApis/livoxGen1/pcloudStimulusProducer.cpp b/stimBuffApis/livoxGen1/pcloudStimulusProducer.cpp index 3f413b7..c6b7d9a 100644 --- a/stimBuffApis/livoxGen1/pcloudStimulusProducer.cpp +++ b/stimBuffApis/livoxGen1/pcloudStimulusProducer.cpp @@ -14,24 +14,6 @@ #include "livoxGen1.h" #include "pcloudStimulusProducer.h" -namespace { - -void requirePcloudAmbienceFromStimbuff( - const std::shared_ptr& spec) -{ - const std::string fromName = - smo::stim_buff::parseRequiredFromStimbuffQualeIfaceName( - spec->qualeIfaceApiParams); - if (fromName != "pcloudAmbience") - { - throw std::runtime_error( - "LivoxGen1 PcloudAmbience intrinsic pipelines require " - "from-stimbuff=pcloudAmbience (got '" + fromName + "')."); - } -} - -} // namespace - namespace smo { namespace stim_buff { @@ -151,8 +133,7 @@ bool PcloudStimulusProducer::supportsQualeIfaceApi( const std::string& qualeIfaceApi) { return qualeIfaceApi == "mesh" || qualeIfaceApi == "pcloudIntensity" || - qualeIfaceApi == "pcloudAmbience" || qualeIfaceApi == "negtrin" || - qualeIfaceApi == "postrin"; + qualeIfaceApi == "pcloudAmbience"; } bool PcloudStimulusProducer::exportsQualeIfaceApi( @@ -248,11 +229,6 @@ PcloudStimulusProducer::getAttachedStimulusBuffer( if (std::dynamic_pointer_cast(buffer)) { return buffer; } } - else if (qualeIfaceApi == "negtrin" || qualeIfaceApi == "postrin") - { - if (std::dynamic_pointer_cast(buffer)) - { return buffer; } - } // Type mismatch - return nullptr return nullptr; @@ -284,22 +260,6 @@ void PcloudStimulusProducer::destroyAttachedStimulusBuffer( ambienceBuff.reset(); ambienceStimulusBuffer.store(nullptr, std::memory_order_release); } - auto negIntrinBuff = negtrinAmbienceIntrinStimulusBuffer.load( - std::memory_order_acquire); - if (negIntrinBuff == buffer) - { - negIntrinBuff.reset(); - negtrinAmbienceIntrinStimulusBuffer.store( - nullptr, std::memory_order_release); - } - auto posIntrinBuff = postrinAmbienceIntrinStimulusBuffer.load( - std::memory_order_acquire); - if (posIntrinBuff == buffer) - { - posIntrinBuff.reset(); - postrinAmbienceIntrinStimulusBuffer.store( - nullptr, std::memory_order_release); - } // Call base class implementation to remove from attachedStimulusBuffers StimulusProducer::destroyAttachedStimulusBuffer(buffer); @@ -384,38 +344,12 @@ PcloudStimulusProducer::getOrCreateAttachedStimulusBuffer( this->start(); return ambienceStimBuff; } - else if (qualeIfaceApi == "negtrin" || qualeIfaceApi == "postrin") - { - requirePcloudAmbienceFromStimbuff(deviceAttachmentSpec); - - auto intrinBuff = std::make_shared( - *this, deviceAttachmentSpec, histbuffMs, - openClAmbienceInputConstraints, openClAmbienceInputConstraints, - *smoHooksPtr, CL_MEM_READ_WRITE, - this->nDgramsPerStagingBufferFrame); - - this->stop(); - addAttachedStimulusBufferIfNotExists(intrinBuff); - if (qualeIfaceApi == "negtrin") - { - negtrinAmbienceIntrinStimulusBuffer.store( - intrinBuff, std::memory_order_release); - } - else - { - postrinAmbienceIntrinStimulusBuffer.store( - intrinBuff, std::memory_order_release); - } - this->start(); - return intrinBuff; - } else { throw std::runtime_error( "Unsupported qualeIfaceApi: '" + qualeIfaceApi + "' for " "PcloudStimulusProducer. " - "Supported values: mesh, pcloudIntensity, pcloudAmbience, " - "negtrin, postrin"); + "Supported values: mesh, pcloudIntensity, pcloudAmbience"); } } diff --git a/stimBuffApis/livoxGen1/pcloudStimulusProducer.h b/stimBuffApis/livoxGen1/pcloudStimulusProducer.h index 74de08b..fc55faf 100644 --- a/stimBuffApis/livoxGen1/pcloudStimulusProducer.h +++ b/stimBuffApis/livoxGen1/pcloudStimulusProducer.h @@ -15,7 +15,6 @@ #include "meshStimulusBuffer.h" #include "pcloudIntensityStimulusBuffer.h" #include "pcloudAmbienceStimulusBuffer.h" -#include "pcloudAmbienceIntrinStimulusBuffer.h" namespace smo { namespace stim_buff { @@ -105,10 +104,6 @@ public: intensityStimulusBuffer; std::atomic> ambienceStimulusBuffer; - std::atomic> - negtrinAmbienceIntrinStimulusBuffer; - std::atomic> - postrinAmbienceIntrinStimulusBuffer; private: class ProduceFrameReq;