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.
This commit is contained in:
2026-04-18 12:02:27 -04:00
parent fc1fcae0b0
commit 632a227985
16 changed files with 441 additions and 620 deletions
+12 -19
View File
@@ -4,23 +4,16 @@
+edev|avia0|mesh()|livoxGen1()|livoxProto1(SMO_IP)|3JEDK380010Z39|| +edev|avia0|mesh()|livoxGen1()|livoxProto1(SMO_IP)|3JEDK380010Z39||
+edev|avia0|pcloudIntensity()|livoxGen1()|livoxProto1(SMO_IP)|3JEDK380010Z39|| +edev|avia0|pcloudIntensity()|livoxGen1()|livoxProto1(SMO_IP)|3JEDK380010Z39||
+edev|avia0|pcloudAmbience()|livoxGen1()|livoxProto1(SMO_IP)|3JEDK380010Z39|| /* pcloudAmbience with:
/* Be positively disposed to dim ambience, but don't be so positively drawn that * - postrin: be positively disposed to dim ambience (low passband counts),
* your eyelids droop and your pupils dilate, in human terms (i.e: no * but don't be so positively drawn that your eyelids droop and your pupils
* distraction). And no stupefaction from it either. * 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( +edev|avia0|
from-stimbuff=pcloudAmbience| postrin(interest-pc=85)|
interest-pc=85| negtrin(interest-pc=85|distraction-pc=90|intolerable-pc=95)|
passband-count-lt-val=8 pcloudAmbience(passband-count-lt-val=8|passband-count-gt-val=120)|
) livoxGen1()|livoxProto1(SMO_IP)|
|livoxGen1()|livoxProto1(SMO_IP)|3JEDK380010Z39|| 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
+37 -40
View File
@@ -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 for postrin or negtrin data and present it to SMO with appropriate
importance classifications. importance classifications.
**Intrinsic parameters may appear only on dedicated intrinsic qualeIfaceApi **Intrinsic parameters live inside `postrin(...)` / `negtrin(...)`
specs.** The only supported forms are **`negtrin(...)`** and **`postrin(...)`**. segments attached to a nontrin DAP line.** A DAP line may declare at most
You cannot attach intrinsic threshold params to a sensory or other one `postrin(...)` segment and at most one `negtrin(...)` segment, and both
non-intrinsic qualeIfaceApi (for example `pcloudIntensity`, `pcloudAmbience`, must precede the qualeIfaceApi segment. Ordering between postrin and
`mesh`, `pcloud`, and so on). Those lines are for sensory streams only. negtrin is free.
A dedicated intrinsic line must name the sensory stimbuff it derives from via Intrinsic threshold params (and the deprecated `from-stimbuff` marker)
**`from-stimbuff=<qualeIfaceApi>`** (non-empty). Policy validation rejects must not appear on the qualeIfaceApi params themselves — policy
intrinsic params and passband-style comparators on sensory lines, and rejects validation rejects them there. The nontrin qualeIfaceApi
dedicated lines that omit `from-stimbuff`. (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 On **`postrin(...)`** or **`negtrin(...)`**, the intrinsic family
positive) is fixed by the API name. The **`negtrin-`** and **`postrin-`** (positive vs negative) is fixed by the segment name, so only the
prefixes on parameter names are **optional**: you may use short forms unprefixed short forms **`interest-...`**, **`distraction-...`**,
**`interest-...`**, **`distraction-...`**, **`stupefaction-` / `stupefying-...`**, **`stupefaction-` / `stupefying-...`**, and **`intolerable-...`** (with
and **`intolerable-...`** with the unit suffix rules below. Prefixed names such the unit suffix rules below) are accepted.
as **`negtrin-interest-pc`** remain valid when you prefer explicit spelling.
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 ## Unit Suffix Rules
@@ -59,20 +62,18 @@ parameter takes precedence.
Shorthand params without a unit suffix are not permitted. The following Shorthand params without a unit suffix are not permitted. The following
forms are invalid because they omit units: forms are invalid because they omit units:
- `[<pos|neg>trin-]<interest|distraction|stupef<action|ying>|intolerable>` - `<interest|distraction|stupef<action|ying>|intolerable>`
On dedicated **`postrin(...)`** / **`negtrin(...)`** lines, the short forms Inside **`postrin(...)`** / **`negtrin(...)`** segments, the short forms
**`interest-...`**, **`distraction-...`**, etc. still require those unit **`interest-...`**, **`distraction-...`**, etc. still require those unit
suffixes. suffixes.
## Interest Threshold Parameters ## Interest Threshold Parameters
**Parameter forms (only inside `negtrin(...)` or `postrin(...)`):** **Parameter forms (only inside `postrin(...)` or `negtrin(...)`
segments):**
- Prefixed: `postrin-interest-<percentage|pc|threshold|thresh|thr>`, - `interest-<percentage|pc|threshold|thresh|thr>`
`negtrin-interest-<percentage|pc|threshold|thresh|thr>`
- Short (prefix optional because the line is already negtrin or postrin):
`interest-<percentage|pc|threshold|thresh|thr>`
**Description:** **Description:**
These parameters denote the value at which the stimbuff API library 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 for this intrinsic spec, and present it to SMO via postrinInd/negtrinInd
with the "importance" argument set to "INTERESTING". 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 ## Distraction Threshold Parameters
**Parameter forms (only inside `negtrin(...)` or `postrin(...)`):** **Parameter forms (only inside `postrin(...)` or `negtrin(...)`
segments):**
- Prefixed: `postrin-distraction-<percentage|pc|threshold|thresh|thr>`, - `distraction-<percentage|pc|threshold|thresh|thr>`
`negtrin-distraction-<percentage|pc|threshold|thresh|thr>`
- Short: `distraction-<percentage|pc|threshold|thresh|thr>` (family is
fixed by the `negtrin` / `postrin` API name)
**Description:** **Description:**
These parameters denote the value at which the stimbuff API library These parameters denote the value at which the stimbuff API library
ought to construct a stencil and deliver it to SMO via 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) ## Stupefying Threshold Parameters (Postrin)
**Parameter forms (only inside `postrin(...)`):** **Parameter forms (only inside `postrin(...)` segments):**
- Prefixed: `[postrin-]stupefaction-<percentage|pc|threshold|thresh|thr>`, - `stupefaction-<percentage|pc|threshold|thresh|thr>`
`[postrin-]stupefying-<percentage|pc|threshold|thresh|thr>` - `stupefying-<percentage|pc|threshold|thresh|thr>`
- Short: `stupefaction-...`, `stupefying-...` with unit suffix
**Description:** **Description:**
These parameters denote the value at which the stimbuff API library 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) ## Intolerable Threshold Parameters (Negtrin)
**Parameter forms (only inside `negtrin(...)`):** **Parameter forms (only inside `negtrin(...)` segments):**
- Prefixed: `[negtrin-]intolerable-<percentage|pc|threshold|thresh|thr>` - `intolerable-<percentage|pc|threshold|thresh|thr>`
- Short: `intolerable-<percentage|pc|threshold|thresh|thr>`
**Description:** **Description:**
These parameters denote the value at which the stimbuff API library These parameters denote the value at which the stimbuff API library
+29 -28
View File
@@ -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 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 DB searches for methods to handle (increase or decrease) the stimval. For
example, for a negtrin whose intrinsic pipeline is declared with a dedicated example, for a negtrin raised from a `negtrin(...)` segment attached to a
`negtrin(...)` spec (and whose sensory data may come from intensity or another nontrin DAP line (e.g. `pcloudAmbience`), we don't search DB by trying to
stimbuff via `from-stimbuff`), we don't search DB by trying to match all match all possible body spots with their stimvals. Rather, we search for
possible body spots with their stimvals. Rather, we search for all the body all the body spots that are described in the stencil. This optimizes DB
spots that are described in the stencil. This optimizes DB searches searches and also makes negtrin relieving/postrin satisfying searches
and also makes negtrin relieving/postrin satisfying searches more explicit more explicit and obviously scoped items of knowledge. The eventual
and obviously scoped items of knowledge. The eventual solution is solution is automatically classified as being a method to relieve/satisfy
automatically classified as being a method to relieve/satisfy intrins at intrins at this point on the body and not just hazily generally for "all
this point on the body and not just hazily generally for "all over the over the body". We get to make the new item of knowledge more specific.
body". We get to make the new item of knowledge more specific.
## Format and Allocation Model ## 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 stimbuff libraries to be inimical enough for security measures like that to
be non-negotiable. be non-negotiable.
## n-stencils QualeIfaceApi Parameter ## n-stencils Intrin-Segment Parameter
Where a stim buff supports it, a qualeIfaceApi parameter called `n-stencils` Where a stim buff supports it, a parameter called `n-stencils` tells the
tells the stimbuff how many stencils it can allocate and deliver to SMO stimbuff how many stencils it can allocate and deliver to SMO
simultaneously for **intrinsic** delivery. Intrinsic rate limiting and stencil simultaneously for **intrinsic** delivery. `n-stencils` belongs on the
counts apply to **dedicated** intrinsic qualeIfaceApi specs (`negtrin(...)`, `postrin(...)` / `negtrin(...)` segment it rate-limits — **not** on the
`postrin(...)`) when the implementation attaches them—not to sensory-only nontrin qualeIfaceApi params. Postrin and negtrin get independent
lines such as `pcloudIntensity` or `pcloudAmbience` (see budgets.
`docs/design/intrin-thresholds.md`).
The stimbuff must wait until SMO returns stencils to it via postrinEventRdy or The stimbuff must wait until SMO returns stencils to it via postrinEventRdy or
negtrinEventRdy before delivering new intrin events. Stimbuffs can deliver 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. raising new intrins.
**Specification:** **Specification:**
- The parameter is specified as part of the `quale-iface-api-params` in the - The parameter is specified inside a `postrin(...)` or `negtrin(...)`
DAP specification segment on a DAP line
- The value is an integer representing the maximum number of stencils that - The value is an integer representing the maximum number of stencils
can be allocated simultaneously that can be allocated simultaneously for that segment's intrin path
- This parameter controls rate limiting for intrin events - This parameter controls rate limiting for intrin events
- Stimbuffs must respect this limit and wait for stencil returns before - Stimbuffs must respect this limit and wait for stencil returns before
allocating new ones 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`; The Livox Gen1 **`pcloudAmbience`** sensory line does **not** use
ambience floats are delivered in the stimulus frame buffer. If Livox adds `n-stencils`; ambience floats are delivered in the stimulus frame buffer.
`n-stencils` for intrinsic pipelines, it would appear on **`negtrin(...)`** / If Livox adds `n-stencils` for its intrinsic pipelines, it would appear
**`postrin(...)`** lines (with `from-stimbuff`), not on `pcloudAmbience` itself. inside the `postrin(...)` / `negtrin(...)` segments attached to
`pcloudAmbience`, not on `pcloudAmbience` itself.
**Invalid (sensory qualeIfaceApi must not carry intrin-oriented params):** **Invalid (sensory qualeIfaceApi must not carry intrin-oriented params):**
``` ```
+7 -6
View File
@@ -43,9 +43,12 @@ Each stim-buff-api is designed to work with specific stim-iface libraries that u
**Stim-Buff-API**: `livoxGen1-pcloud` **Stim-Buff-API**: `livoxGen1-pcloud`
**Quale-Iface-API**: `pcloudAmbience` - Delivers per-dagram average intensity floats (sensory stream only). **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 Passband comparators (`passband-count-lt-val`, `passband-count-gt-val`) belong on
`pcloudAmbience`. Use separate DAP lines with qualeIfaceApi **`negtrin(...)`** or **`postrin(...)`** `pcloudAmbience(...)` itself, and both may appear together — `lt` drives the
and `from-stimbuff=pcloudAmbience`; see `docs/design/intrin-thresholds.md` and the `PcloudAmbienceIntrinStimulusBuffer` path in the LivoxGen1 stim buff API. 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) ### 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 | | Stim Feature | Stim-Buff-API | Quale-Iface-API | Description |
|--------------|---------------|----------------|-------------| |--------------|---------------|----------------|-------------|
| Point Cloud Intensity | `livoxGen1-pcloudIntensity` | `pcloudIntensity` | Light intensity/reflectivity data | | 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`) | | 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`. |
| 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 Coordinates | `livoxGen1-pcloud` | `pcloud` | Spatial coordinate data | | Point Cloud Coordinates | `livoxGen1-pcloud` | `pcloud` | Spatial coordinate data |
| Gyroscope | `livoxGen1-gyro` | `gyro` | Angular velocity measurements | | Gyroscope | `livoxGen1-gyro` | `gyro` | Angular velocity measurements |
| Accelerometer | `livoxGen1-accel` | `accel` | Linear acceleration measurements | | Accelerometer | `livoxGen1-accel` | `accel` | Linear acceleration measurements |
+53 -2
View File
@@ -12,6 +12,18 @@
namespace smo { namespace smo {
namespace device { 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<std::pair<std::string, std::string>> params;
};
class DeviceAttachmentSpec class DeviceAttachmentSpec
{ {
public: public:
@@ -26,6 +38,8 @@ public:
{ {
return deviceIdentifier == other.deviceIdentifier && return deviceIdentifier == other.deviceIdentifier &&
sensorType == other.sensorType && sensorType == other.sensorType &&
postrin == other.postrin &&
negtrin == other.negtrin &&
qualeIfaceApi == other.qualeIfaceApi && qualeIfaceApi == other.qualeIfaceApi &&
stimBuffApi == other.stimBuffApi && stimBuffApi == other.stimBuffApi &&
provider == other.provider && provider == other.provider &&
@@ -35,6 +49,15 @@ public:
public: public:
std::string deviceIdentifier; std::string deviceIdentifier;
char sensorType; 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<std::pair<std::string,std::string>> postrinParams;
std::string negtrin;
std::vector<std::pair<std::string,std::string>> negtrinParams;
std::string qualeIfaceApi; std::string qualeIfaceApi;
std::vector<std::pair<std::string,std::string>> qualeIfaceApiParams; std::vector<std::pair<std::string,std::string>> qualeIfaceApiParams;
std::string stimBuffApi; std::string stimBuffApi;
@@ -43,12 +66,40 @@ public:
std::vector<std::pair<std::string,std::string>> providerParams; std::vector<std::pair<std::string,std::string>> providerParams;
std::string deviceSelector; std::string deviceSelector;
static void stringifyParams(
std::ostream& os,
const std::vector<std::pair<std::string,std::string>>& params)
{
for (const auto& param : params)
{
os << param.first;
if (!param.second.empty()) {
os << "=" << param.second;
}
os << " ";
}
}
std::string stringify() const std::string stringify() const
{ {
std::ostringstream os; std::ostringstream os;
os << "Device Identifier: " << deviceIdentifier os << "Device Identifier: " << deviceIdentifier
<< ", Sensor Type: " << sensorType << ", Sensor Type: " << sensorType;
<< ", QualeIface API: " << qualeIfaceApi << ", QualeIface API Params: (";
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) for (const auto& param : qualeIfaceApiParams)
{ {
os << param.first; os << param.first;
+24
View File
@@ -0,0 +1,24 @@
#ifndef SMO_USER_INTRIN_H
#define SMO_USER_INTRIN_H
#include <cstdint>
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
+70 -138
View File
@@ -27,81 +27,41 @@ struct ParsedThresholdParam
bool wasSpecified; bool wasSpecified;
}; };
inline constexpr std::array<std::string_view, 2> kPosIntPcParamNames = { /* Canonical unprefixed threshold-param names that live inside postrin(...) /
"postrin-interest-percentage", * negtrin(...) segments attached to a nontrin DAP spec. The "-pc" and
"postrin-interest-pc", * "-percentage" variants are Percentage-unit; the "-thr", "-thresh" and
}; * "-threshold" variants are Absolute-unit.
*/
inline constexpr std::array<std::string_view, 3> kPosIntThrParamNames = { inline constexpr std::array<std::string_view, 2> kIntrinInterestPcNames = {
"postrin-interest-threshold",
"postrin-interest-thresh",
"postrin-interest-thr",
};
inline constexpr std::array<std::string_view, 2> kNegIntPcParamNames = {
"negtrin-interest-percentage",
"negtrin-interest-pc",
};
inline constexpr std::array<std::string_view, 3> 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<std::string_view, 2> kIntrinInterestPcUnprefixed = {
"interest-percentage", "interest-percentage",
"interest-pc", "interest-pc",
}; };
inline constexpr std::array<std::string_view, 3> kIntrinInterestThrUnprefixed = { inline constexpr std::array<std::string_view, 3> kIntrinInterestThrNames = {
"interest-threshold", "interest-threshold",
"interest-thresh", "interest-thresh",
"interest-thr", "interest-thr",
}; };
inline constexpr std::array<std::string_view, 2> kPosDistPcParamNames = { inline constexpr std::array<std::string_view, 2> kIntrinDistractionPcNames = {
"postrin-distraction-percentage", "distraction-percentage",
"postrin-distraction-pc", "distraction-pc",
}; };
inline constexpr std::array<std::string_view, 3> kPosDistThrParamNames = { inline constexpr std::array<std::string_view, 3> kIntrinDistractionThrNames = {
"postrin-distraction-threshold", "distraction-threshold",
"postrin-distraction-thresh", "distraction-thresh",
"postrin-distraction-thr", "distraction-thr",
}; };
inline constexpr std::array<std::string_view, 2> kNegDistPcParamNames = { inline constexpr std::array<std::string_view, 4> kIntrinStupefactionPcNames = {
"negtrin-distraction-percentage",
"negtrin-distraction-pc",
};
inline constexpr std::array<std::string_view, 3> kNegDistThrParamNames = {
"negtrin-distraction-threshold",
"negtrin-distraction-thresh",
"negtrin-distraction-thr",
};
inline constexpr std::array<std::string_view, 8> kStupefactionPcParamNames = {
"postrin-stupefaction-percentage",
"postrin-stupefaction-pc",
"postrin-stupefying-percentage",
"postrin-stupefying-pc",
"stupefaction-percentage", "stupefaction-percentage",
"stupefaction-pc", "stupefaction-pc",
"stupefying-percentage", "stupefying-percentage",
"stupefying-pc", "stupefying-pc",
}; };
inline constexpr std::array<std::string_view, 12> kStupefactionThrParamNames = { inline constexpr std::array<std::string_view, 6> kIntrinStupefactionThrNames = {
"postrin-stupefaction-threshold",
"postrin-stupefaction-thresh",
"postrin-stupefaction-thr",
"postrin-stupefying-threshold",
"postrin-stupefying-thresh",
"postrin-stupefying-thr",
"stupefaction-threshold", "stupefaction-threshold",
"stupefaction-thresh", "stupefaction-thresh",
"stupefaction-thr", "stupefaction-thr",
@@ -110,37 +70,30 @@ inline constexpr std::array<std::string_view, 12> kStupefactionThrParamNames = {
"stupefying-thr", "stupefying-thr",
}; };
inline constexpr std::array<std::string_view, 4> kIntolerablePcParamNames = { inline constexpr std::array<std::string_view, 2> kIntrinIntolerablePcNames = {
"negtrin-intolerable-percentage",
"negtrin-intolerable-pc",
"intolerable-percentage", "intolerable-percentage",
"intolerable-pc", "intolerable-pc",
}; };
inline constexpr std::array<std::string_view, 6> kIntolerableThrParamNames = { inline constexpr std::array<std::string_view, 3> kIntrinIntolerableThrNames = {
"negtrin-intolerable-threshold",
"negtrin-intolerable-thresh",
"negtrin-intolerable-thr",
"intolerable-threshold", "intolerable-threshold",
"intolerable-thresh", "intolerable-thresh",
"intolerable-thr", "intolerable-thr",
}; };
inline constexpr std::array<std::string_view, 10> kForbiddenUnitlessIntrinParamNames = { /* Unitless stems are invalid — authors must name the unit via -pc/-percentage
"postrin-interest", * or -thr/-thresh/-threshold. We reject bare "interest", "distraction", etc.
"negtrin-interest", */
"postrin-distraction", inline constexpr std::array<std::string_view, 5> kForbiddenUnitlessIntrinStems = {
"negtrin-distraction", "interest",
"postrin-stupefaction", "distraction",
"postrin-stupefying",
"stupefaction", "stupefaction",
"stupefying", "stupefying",
"negtrin-intolerable",
"intolerable", "intolerable",
}; };
template <std::size_t N> template <std::size_t N>
bool arrayContains( inline bool arrayContains(
const std::array<std::string_view, N>& names, const std::array<std::string_view, N>& names,
std::string_view candidate) std::string_view candidate)
{ {
@@ -152,7 +105,7 @@ bool arrayContains(
} }
template <typename NameCollectionT> template <typename NameCollectionT>
bool namesContain( inline bool namesContain(
const NameCollectionT& names, const NameCollectionT& names,
std::string_view candidate) std::string_view candidate)
{ {
@@ -163,101 +116,80 @@ bool namesContain(
return false; 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) /* Intrin threshold params (interest-*, distraction-*, stupefaction-*,
{ * intolerable-*) must appear only inside postrin(...) / negtrin(...) segments
return namesContain(kPosIntPcParamNames, name) * on a DAP spec, never on qualeIfaceApi params. The deprecated `from-stimbuff`
|| namesContain(kPosIntThrParamNames, name) * marker is also rejected — postrin/negtrin are now directly attached to the
|| namesContain(kNegIntPcParamNames, name) * nontrin stimbuff they trigger from.
|| 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<std::pair<std::string, std::string>>& 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).
*/ */
inline void validateIntrinsQualeApiPolicy( inline void validateNoIntrinParamsOnQualeIface(
const std::string& qualeIfaceApi, const std::string& qualeIfaceApi,
const std::vector<std::pair<std::string, std::string>>& params) const std::vector<std::pair<std::string, std::string>>& params)
{ {
if (isDedicatedIntrinsQualeIfaceApi(qualeIfaceApi))
{
if (!hasNonEmptyFromStimbuffParam(params))
{
throw std::runtime_error(
"qualeIfaceApi '" + qualeIfaceApi + "' requires a non-empty "
"'from-stimbuff=<stimbuffQualeIfaceApi>' parameter naming the "
"sensory stimbuff that feeds this intrinsic pipeline.");
}
return;
}
for (const auto& [name, value] : params) for (const auto& [name, value] : params)
{ {
(void)value; (void)value;
if (isKnownIntrinsPipelineParamName(name)) if (isKnownIntrinThresholdParamName(name))
{ {
throw std::runtime_error( throw std::runtime_error(
"Intrinsic threshold and passband comparator params must not " "Intrinsic threshold param '" + name + "' is not valid on "
"appear on qualeIfaceApi '" + qualeIfaceApi + "'. Use dedicated " "qualeIfaceApi '" + qualeIfaceApi + "'. Declare it inside a "
"negtrin(...) or postrin(...) lines with from-stimbuff=... " "postrin(...) or negtrin(...) segment attached to this DAP "
"(offending param: '" + name + "')."); "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") if (name == "from-stimbuff")
{ {
throw std::runtime_error( throw std::runtime_error(
"'from-stimbuff' is only valid on negtrin(...) or postrin(...) " "'from-stimbuff' is no longer supported. postrin(...) and "
"qualeIfaceApi specs."); "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<std::pair<std::string, std::string>>& params) const std::vector<std::pair<std::string, std::string>>& params)
{ {
for (const auto& [name, value] : params) for (const auto& [name, value] : params)
{ {
(void)value; (void)value;
if (arrayContains(kForbiddenUnitlessIntrinParamNames, name)) if (arrayContains(kForbiddenUnitlessIntrinStems, name))
{ {
throw std::runtime_error( throw std::runtime_error(
"Intrinsic threshold param '" + name std::string(intrinKind) + "(...) param '" + name + "' is "
+ "' is invalid without a unit suffix. Use a " "invalid without a unit suffix. Use '-pc'/'-percentage' or "
"'-percentage'/'-pc' or '-threshold'/'-thresh'/'-thr' variant."); "'-thr'/'-thresh'/'-threshold'.");
} }
} }
} }
@@ -51,6 +51,8 @@ void yyerror(const char *message)
smo::device::ExtrospectorDevAttachmentSpec* extrospectorSpec; smo::device::ExtrospectorDevAttachmentSpec* extrospectorSpec;
std::vector<std::pair<std::string,std::string>>* paramVector; std::vector<std::pair<std::string,std::string>>* paramVector;
std::pair<std::string,std::string>* param; std::pair<std::string,std::string>* param;
smo::device::DapSegment* DapSpecSegment;
std::vector<smo::device::DapSegment>* DapSegmentVector;
} }
%token <str> STRING %token <str> STRING
@@ -64,6 +66,8 @@ void yyerror(const char *message)
%type <sensorSpec> spec_body %type <sensorSpec> spec_body
%type <interoceptorSpec> interoceptor_spec %type <interoceptorSpec> interoceptor_spec
%type <extrospectorSpec> extrospector_spec %type <extrospectorSpec> extrospector_spec
%type <DapSpecSegment> specifier_segment
%type <DapSegmentVector> specifier_segments
%% %%
@@ -106,20 +110,91 @@ extrospector_spec:
; ;
spec_body: 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<std::vector<smo::device::DapSegment>>($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(); $$ = new smo::device::DeviceAttachmentSpec();
$$->deviceIdentifier = std::string($1); $$->deviceIdentifier = std::string($1);
$$->sensorType = '\0'; // This will be set by the parent rule $$->sensorType = '\0'; // This will be set by the parent rule
$$->qualeIfaceApi = std::string($3);
$$->qualeIfaceApiParams = std::move(*$5); for (size_t i = 0; i < nIntrins; ++i)
$$->stimBuffApi = std::string($8); {
$$->stimBuffApiParams = std::move(*$10); auto& seg = (*segments)[i];
$$->provider = std::string($13); if (seg.name == "postrin")
$$->providerParams = std::move(*$15); {
$$->deviceSelector = std::string($18); if (!$$->postrin.empty())
delete $5; {
delete $10; throw std::runtime_error(
delete $15; "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<smo::device::DapSegment>();
$$->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;
} }
; ;
+2 -5
View File
@@ -514,8 +514,6 @@ static const StimBuffApiDesc livoxGen1ApiDesc = {
{.name = "mesh"}, {.name = "mesh"},
{.name = "pcloudIntensity"}, {.name = "pcloudIntensity"},
{.name = "pcloudAmbience"}, {.name = "pcloudAmbience"},
{.name = "negtrin"},
{.name = "postrin"},
{.name = "gyro"}, {.name = "gyro"},
{.name = "accel"} {.name = "accel"}
}, },
@@ -649,7 +647,7 @@ extern "C" void livoxGen1_attachDeviceReq(
} }
try { try {
smo::intrin::validateIntrinsQualeApiPolicy( smo::intrin::validateNoIntrinParamsOnQualeIface(
desc->qualeIfaceApi, desc->qualeIfaceApiParams); desc->qualeIfaceApi, desc->qualeIfaceApiParams);
} }
catch (const std::runtime_error& e) catch (const std::runtime_error& e)
@@ -664,8 +662,7 @@ extern "C" void livoxGen1_attachDeviceReq(
// Unknown qualeIfaceApi // Unknown qualeIfaceApi
std::cerr << __func__ << ": Unsupported qualeIfaceApi '" std::cerr << __func__ << ": Unsupported qualeIfaceApi '"
<< qualeIfaceApi << "' for LivoxGen1. " << qualeIfaceApi << "' for LivoxGen1. "
"Supported values: mesh, pcloudIntensity, pcloudAmbience, " "Supported values: mesh, pcloudIntensity, pcloudAmbience"
"negtrin, postrin"
<< std::endl; << std::endl;
cb.callbackFn(false, desc); cb.callbackFn(false, desc);
return; return;
+3 -2
View File
@@ -15,7 +15,8 @@ class StimulusProducer;
/** /**
* MeshStimulusBuffer is a specialized StimulusBuffer for mesh data. * MeshStimulusBuffer is a specialized StimulusBuffer for mesh data.
* Intrinsic threshold params are not allowed on mesh qualeIfaceApi lines; * 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 class MeshStimulusBuffer
: public StimulusBuffer : public StimulusBuffer
@@ -34,7 +35,7 @@ public:
inputEngineConstraints, outputEngineConstraints, inputEngineConstraints, outputEngineConstraints,
callbacks, flags) callbacks, flags)
{ {
intrin::validateIntrinsQualeApiPolicy( intrin::validateNoIntrinParamsOnQualeIface(
deviceAttachmentSpec->qualeIfaceApi, deviceAttachmentSpec->qualeIfaceApi,
deviceAttachmentSpec->qualeIfaceApiParams); deviceAttachmentSpec->qualeIfaceApiParams);
} }
@@ -1,251 +0,0 @@
#ifndef _LIVOX_GEN1_PCLOUD_AMBIENCE_INTRIN_STIMULUS_BUFFER_H
#define _LIVOX_GEN1_PCLOUD_AMBIENCE_INTRIN_STIMULUS_BUFFER_H
#include <memory>
#include <cstdint>
#include <cstddef>
#include <optional>
#include <string>
#include <user/stimulusBuffer.h>
#include <user/stagingBuffer.h>
#include <user/deviceAttachmentSpec.h>
#include <user/intrinThresholdParams.h>
#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<uint32_t>(thresholdParam.value)
: 0U,
.interestThreshold = intrin::resolveThresholdValue(
thresholdParam, nDgramsPerFrame),
};
}
inline std::optional<ParsedAmbienceIntrinConfig> 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<device::DeviceAttachmentSpec>& 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<ParamComparator>& 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<device::DeviceAttachmentSpec>& 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<ParamComparator> passbandCountComparator;
size_t nDgramsPerFrame;
};
} // namespace stim_buff
} // namespace smo
#endif // _LIVOX_GEN1_PCLOUD_AMBIENCE_INTRIN_STIMULUS_BUFFER_H
@@ -7,32 +7,10 @@
#include <stdexcept> #include <stdexcept>
#include <string> #include <string>
#include <user/deviceAttachmentSpec.h> #include <user/deviceAttachmentSpec.h>
#include <vector>
namespace smo { namespace smo {
namespace stim_buff { namespace stim_buff {
inline std::string parseRequiredFromStimbuffQualeIfaceName(
const std::vector<std::pair<std::string, std::string>>& 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 enum ParamComparatorOp
{ {
OP_CMP_GT, OP_CMP_GT,
@@ -58,7 +36,19 @@ struct ParamComparator
} }
}; };
inline std::optional<ParamComparator> parseOptionalPcloudAmbienceParamComparator( struct PcloudAmbiencePassbandComparators
{
std::optional<ParamComparator> lt;
std::optional<ParamComparator> 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<device::DeviceAttachmentSpec>& deviceAttachmentSpec) const std::shared_ptr<device::DeviceAttachmentSpec>& deviceAttachmentSpec)
{ {
const auto& params = deviceAttachmentSpec->qualeIfaceApiParams; const auto& params = deviceAttachmentSpec->qualeIfaceApiParams;
@@ -68,30 +58,23 @@ inline std::optional<ParamComparator> parseOptionalPcloudAmbienceParamComparator
const int ltVal = device::DeviceAttachmentSpec::parseOptionalParamAsInt( const int ltVal = device::DeviceAttachmentSpec::parseOptionalParamAsInt(
params, "passband-count-lt-val", kParamNotSpecified); params, "passband-count-lt-val", kParamNotSpecified);
if (gtVal != kParamNotSpecified && ltVal != kParamNotSpecified) PcloudAmbiencePassbandComparators out;
{
throw std::runtime_error(
"Only one of 'passband-count-gt-val' or 'passband-count-lt-val' "
"may be specified for a PcloudAmbience intrinsic pipeline");
}
if (gtVal != kParamNotSpecified) if (gtVal != kParamNotSpecified)
{ {
return std::optional<ParamComparator>(ParamComparator{ out.gt = ParamComparator{
.op = OP_CMP_GT, .op = OP_CMP_GT,
.value = static_cast<uint32_t>(gtVal), .value = static_cast<uint32_t>(gtVal),
}); };
} }
if (ltVal != kParamNotSpecified) if (ltVal != kParamNotSpecified)
{ {
return std::optional<ParamComparator>(ParamComparator{ out.lt = ParamComparator{
.op = OP_CMP_LT, .op = OP_CMP_LT,
.value = static_cast<uint32_t>(ltVal), .value = static_cast<uint32_t>(ltVal),
}); };
} }
return std::nullopt; return out;
} }
} // namespace stim_buff } // namespace stim_buff
@@ -1,23 +1,58 @@
#ifndef _LIVOX_GEN1_PCLOUD_AMBIENCE_STIMULUS_BUFFER_H #ifndef _LIVOX_GEN1_PCLOUD_AMBIENCE_STIMULUS_BUFFER_H
#define _LIVOX_GEN1_PCLOUD_AMBIENCE_STIMULUS_BUFFER_H #define _LIVOX_GEN1_PCLOUD_AMBIENCE_STIMULUS_BUFFER_H
#include <memory>
#include <cstddef> #include <cstddef>
#include <user/stimulusBuffer.h> #include <cstdint>
#include <user/stagingBuffer.h> #include <memory>
#include <optional>
#include <string>
#include <user/deviceAttachmentSpec.h> #include <user/deviceAttachmentSpec.h>
#include <user/intrin.h>
#include <user/intrinThresholdParams.h> #include <user/intrinThresholdParams.h>
#include <user/stagingBuffer.h>
#include <user/stimulusBuffer.h>
#include "pcloudAmbienceQualeIfaceApi.h"
namespace smo { namespace smo {
namespace stim_buff { namespace stim_buff {
class StimulusProducer; class StimulusProducer;
inline intrin::IntrinConfig parseAmbienceIntrinConfigFromParams(
std::string_view intrinKind,
const std::vector<std::pair<std::string, std::string>>& 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<uint32_t>(threshold.value)
: 0U,
.threshold = intrin::resolveThresholdValue(
threshold, nDgramsPerFrame),
};
}
/** /**
* Sensory PcloudAmbience buffer: per-dgram ambience floats only. * Sensory PcloudAmbience buffer: per-dgram ambience floats. A DAP spec may
* Intrinsic thresholds and passband comparators are only valid on dedicated * optionally attach a postrin(...) and/or a negtrin(...) specifier to this
* negtrin/postrin qualeIfaceApi specs (PcloudAmbienceIntrinStimulusBuffer); * qualeIfaceApi; when present, interest thresholds from those specifiers and
* see docs/design/intrin-thresholds.md. * 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 class PcloudAmbienceStimulusBuffer
: public StimulusBuffer : public StimulusBuffer
@@ -38,9 +73,46 @@ public:
callbacks, flags), callbacks, flags),
nDgramsPerFrame(nDgramsPerFrame_) nDgramsPerFrame(nDgramsPerFrame_)
{ {
intrin::validateIntrinsQualeApiPolicy( intrin::validateNoIntrinParamsOnQualeIface(
deviceAttachmentSpec->qualeIfaceApi, deviceAttachmentSpec->qualeIfaceApi,
deviceAttachmentSpec->qualeIfaceApiParams); 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; ~PcloudAmbienceStimulusBuffer() = default;
@@ -52,8 +124,24 @@ public:
PcloudAmbienceStimulusBuffer& operator=( PcloudAmbienceStimulusBuffer& operator=(
PcloudAmbienceStimulusBuffer&&) = delete; 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: public:
size_t nDgramsPerFrame; size_t nDgramsPerFrame;
std::optional<intrin::IntrinConfig> postrinInterestConfig;
std::optional<intrin::IntrinConfig> negtrinInterestConfig;
std::optional<ParamComparator> passbandCountLtComparator;
std::optional<ParamComparator> passbandCountGtComparator;
}; };
} // namespace stim_buff } // namespace stim_buff
@@ -33,7 +33,7 @@ public:
inputEngineConstraints, outputEngineConstraints, inputEngineConstraints, outputEngineConstraints,
callbacks, flags) callbacks, flags)
{ {
intrin::validateIntrinsQualeApiPolicy( intrin::validateNoIntrinParamsOnQualeIface(
deviceAttachmentSpec->qualeIfaceApi, deviceAttachmentSpec->qualeIfaceApi,
deviceAttachmentSpec->qualeIfaceApiParams); deviceAttachmentSpec->qualeIfaceApiParams);
} }
@@ -14,24 +14,6 @@
#include "livoxGen1.h" #include "livoxGen1.h"
#include "pcloudStimulusProducer.h" #include "pcloudStimulusProducer.h"
namespace {
void requirePcloudAmbienceFromStimbuff(
const std::shared_ptr<smo::device::DeviceAttachmentSpec>& 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 smo {
namespace stim_buff { namespace stim_buff {
@@ -151,8 +133,7 @@ bool PcloudStimulusProducer::supportsQualeIfaceApi(
const std::string& qualeIfaceApi) const std::string& qualeIfaceApi)
{ {
return qualeIfaceApi == "mesh" || qualeIfaceApi == "pcloudIntensity" || return qualeIfaceApi == "mesh" || qualeIfaceApi == "pcloudIntensity" ||
qualeIfaceApi == "pcloudAmbience" || qualeIfaceApi == "negtrin" || qualeIfaceApi == "pcloudAmbience";
qualeIfaceApi == "postrin";
} }
bool PcloudStimulusProducer::exportsQualeIfaceApi( bool PcloudStimulusProducer::exportsQualeIfaceApi(
@@ -248,11 +229,6 @@ PcloudStimulusProducer::getAttachedStimulusBuffer(
if (std::dynamic_pointer_cast<PcloudAmbienceStimulusBuffer>(buffer)) if (std::dynamic_pointer_cast<PcloudAmbienceStimulusBuffer>(buffer))
{ return buffer; } { return buffer; }
} }
else if (qualeIfaceApi == "negtrin" || qualeIfaceApi == "postrin")
{
if (std::dynamic_pointer_cast<PcloudAmbienceIntrinStimulusBuffer>(buffer))
{ return buffer; }
}
// Type mismatch - return nullptr // Type mismatch - return nullptr
return nullptr; return nullptr;
@@ -284,22 +260,6 @@ void PcloudStimulusProducer::destroyAttachedStimulusBuffer(
ambienceBuff.reset(); ambienceBuff.reset();
ambienceStimulusBuffer.store(nullptr, std::memory_order_release); 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 // Call base class implementation to remove from attachedStimulusBuffers
StimulusProducer::destroyAttachedStimulusBuffer(buffer); StimulusProducer::destroyAttachedStimulusBuffer(buffer);
@@ -384,38 +344,12 @@ PcloudStimulusProducer::getOrCreateAttachedStimulusBuffer(
this->start(); this->start();
return ambienceStimBuff; return ambienceStimBuff;
} }
else if (qualeIfaceApi == "negtrin" || qualeIfaceApi == "postrin")
{
requirePcloudAmbienceFromStimbuff(deviceAttachmentSpec);
auto intrinBuff = std::make_shared<PcloudAmbienceIntrinStimulusBuffer>(
*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 else
{ {
throw std::runtime_error( throw std::runtime_error(
"Unsupported qualeIfaceApi: '" + qualeIfaceApi + "' for " "Unsupported qualeIfaceApi: '" + qualeIfaceApi + "' for "
"PcloudStimulusProducer. " "PcloudStimulusProducer. "
"Supported values: mesh, pcloudIntensity, pcloudAmbience, " "Supported values: mesh, pcloudIntensity, pcloudAmbience");
"negtrin, postrin");
} }
} }
@@ -15,7 +15,6 @@
#include "meshStimulusBuffer.h" #include "meshStimulusBuffer.h"
#include "pcloudIntensityStimulusBuffer.h" #include "pcloudIntensityStimulusBuffer.h"
#include "pcloudAmbienceStimulusBuffer.h" #include "pcloudAmbienceStimulusBuffer.h"
#include "pcloudAmbienceIntrinStimulusBuffer.h"
namespace smo { namespace smo {
namespace stim_buff { namespace stim_buff {
@@ -105,10 +104,6 @@ public:
intensityStimulusBuffer; intensityStimulusBuffer;
std::atomic<std::shared_ptr<PcloudAmbienceStimulusBuffer>> std::atomic<std::shared_ptr<PcloudAmbienceStimulusBuffer>>
ambienceStimulusBuffer; ambienceStimulusBuffer;
std::atomic<std::shared_ptr<PcloudAmbienceIntrinStimulusBuffer>>
negtrinAmbienceIntrinStimulusBuffer;
std::atomic<std::shared_ptr<PcloudAmbienceIntrinStimulusBuffer>>
postrinAmbienceIntrinStimulusBuffer;
private: private:
class ProduceFrameReq; class ProduceFrameReq;