Block type: intervalTest
| Parameter | Description |
|---|---|
| input ports | 1 input port(s) declared. |
| Parameter | Description |
|---|---|
| output ports | 1 output port(s) declared. |
Outputs 1 when the input lies within [LowerLimit, UpperLimit], else 0.
| Module | nflow_blocks |
| Library | Logic / Bit Operations |
| Type | intervalTest |
| Label | Interval Test |
Description
Tests whether the input u lies inside a static interval. The bounds LowerLimit and UpperLimit are parameters; IntervalClosedLeft and IntervalClosedRight select whether each end is inclusive (>=/<=) or exclusive (>/<). The test is applied element-wise over a vector input.
Pure algebraic feedthrough (no state): the output at each step depends only on the current input.
Ports
Input(s)
| Port | Role | Side | Position |
|---|---|---|---|
| Port_1 | Numeric signal read by the block. | left | x=0, y=40 |
Output(s)
| Port | Role | Side | Position |
|---|---|---|---|
| Port_1 | Numeric signal produced by the block. | right | x=100, y=40 |
Parameters
| Parameter | Default value |
|---|---|
LowerLimit |
0 |
UpperLimit |
1 |
IntervalClosedLeft |
1 |
IntervalClosedRight |
1 |
Block Characteristics
| Block type | intervalTest |
| Family | Logic / Bit Operations |
| Rendered size | 100 x 80 |
| Phases | ALGEBRAIC |
| Internal state or history | no |
| Signal data type | double numeric values |
Algorithms
Equation or Rule
$$y = \begin{cases} 1 & \text{lo} \le u \le \text{up} \\ 0 & \text{otherwise} \end{cases}$$Extended Capabilities
Code generation: supported for C and Rust.
Implementation Sources
modules/nflow_blocks/libraries/logic/library.json{
"id": "builtin.logic",
"title": "Logic / Bit Operations",
"version": "0.1.0",
"format": "nflow-2",
"builtin": true,
"blocks": [
{
"type": "and",
"label": "AND",
"icon": "and.svg",
"phases": [
"ALGEBRAIC"
],
"width": 80,
"height": 80,
"inputs": [
{
"x": 0,
"y": 20,
"side": "left"
},
{
"x": 0,
"y": 60,
"side": "left"
}
],
"outputs": [
{
"x": 80,
"y": 40,
"side": "right"
}
],
"render": {
"type": "math",
"bodyClass": "block-body",
"mathGroupClass": "and-math",
"formula": "\\text{AND}",
"textSize": "16px"
}
},
{
"type": "or",
"label": "OR",
"icon": "or.svg",
"phases": [
"ALGEBRAIC"
],
"width": 80,
"height": 80,
"inputs": [
{
"x": 0,
"y": 20,
"side": "left"
},
{
"x": 0,
"y": 60,
"side": "left"
}
],
"outputs": [
{
"x": 80,
"y": 40,
"side": "right"
}
],
"render": {
"type": "math",
"bodyClass": "block-body",
"mathGroupClass": "or-math",
"formula": "\\text{OR}",
"textSize": "16px"
}
},
{
"type": "xor",
"label": "XOR",
"icon": "xor.svg",
"phases": [
"ALGEBRAIC"
],
"width": 80,
"height": 80,
"inputs": [
{
"x": 0,
"y": 20,
"side": "left"
},
{
"x": 0,
"y": 60,
"side": "left"
}
],
"outputs": [
{
"x": 80,
"y": 40,
"side": "right"
}
],
"render": {
"type": "math",
"bodyClass": "block-body",
"mathGroupClass": "xor-math",
"formula": "\\text{XOR}",
"textSize": "16px"
}
},
{
"type": "not",
"label": "NOT",
"icon": "not.svg",
"phases": [
"ALGEBRAIC"
],
"width": 80,
"height": 80,
"inputs": [
{
"x": 0,
"y": 40,
"side": "left"
}
],
"outputs": [
{
"x": 80,
"y": 40,
"side": "right"
}
],
"render": {
"type": "math",
"bodyClass": "block-body",
"mathGroupClass": "not-math",
"formula": "\\text{NOT}",
"textSize": "16px"
}
},
{
"type": "compareToConstant",
"label": "Compare Const",
"icon": "compareToConstant.svg",
"phases": [
"ALGEBRAIC"
],
"width": 100,
"height": 80,
"inputs": [
{
"x": 0,
"y": 40,
"side": "left"
}
],
"outputs": [
{
"x": 100,
"y": 40,
"side": "right"
}
],
"defaultParams": {
"relop": "ge",
"const": 0
},
"render": {
"type": "image",
"src": "exports/compareToConstant.svg",
"svgMode": "element",
"preserveAspectRatio": "none",
"x": 0,
"y": 0,
"width": 100,
"height": 80
}
},
{
"type": "intervalTest",
"label": "Interval Test",
"icon": "intervalTest.svg",
"phases": [
"ALGEBRAIC"
],
"width": 100,
"height": 80,
"inputs": [
{
"x": 0,
"y": 40,
"side": "left"
}
],
"outputs": [
{
"x": 100,
"y": 40,
"side": "right"
}
],
"defaultParams": {
"LowerLimit": 0,
"UpperLimit": 1,
"IntervalClosedLeft": 1,
"IntervalClosedRight": 1
},
"render": {
"type": "image",
"src": "exports/intervalTest.svg",
"svgMode": "element",
"preserveAspectRatio": "none",
"x": 0,
"y": 0,
"width": 100,
"height": 80
}
},
{
"type": "intervalTestDynamic",
"label": "Interval Test Dynamic",
"icon": "intervalTestDynamic.svg",
"phases": [
"ALGEBRAIC"
],
"width": 100,
"height": 80,
"inputs": [
{
"x": 0,
"y": 20,
"side": "left"
},
{
"x": 0,
"y": 40,
"side": "left"
},
{
"x": 0,
"y": 60,
"side": "left"
}
],
"outputs": [
{
"x": 100,
"y": 40,
"side": "right"
}
],
"defaultParams": {
"IntervalClosedLeft": 1,
"IntervalClosedRight": 1
},
"render": {
"type": "image",
"src": "exports/intervalTestDynamic.svg",
"svgMode": "element",
"preserveAspectRatio": "none",
"x": 0,
"y": 0,
"width": 100,
"height": 80
}
},
{
"type": "bitwiseOperator",
"label": "Bitwise Operator",
"icon": "bitwiseOperator.svg",
"phases": [
"ALGEBRAIC"
],
"width": 90,
"height": 80,
"inputs": [
{
"x": 0,
"y": 40,
"side": "left"
}
],
"outputs": [
{
"x": 90,
"y": 40,
"side": "right"
}
],
"defaultParams": {
"Operation": "AND",
"BitMask": 0,
"NumBits": 32
},
"render": {
"type": "image",
"src": "exports/bitwiseOperator.svg",
"svgMode": "element",
"preserveAspectRatio": "none",
"x": 0,
"y": 0,
"width": 90,
"height": 80
}
},
{
"type": "bitSet",
"label": "Bit Set",
"icon": "bitSet.svg",
"phases": [
"ALGEBRAIC"
],
"width": 80,
"height": 80,
"inputs": [
{
"x": 0,
"y": 40,
"side": "left"
}
],
"outputs": [
{
"x": 80,
"y": 40,
"side": "right"
}
],
"defaultParams": {
"BitIndex": 0,
"NumBits": 32
},
"render": {
"type": "image",
"src": "exports/bitSet.svg",
"svgMode": "element",
"preserveAspectRatio": "none",
"x": 0,
"y": 0,
"width": 80,
"height": 80
}
},
{
"type": "bitClear",
"label": "Bit Clear",
"icon": "bitClear.svg",
"phases": [
"ALGEBRAIC"
],
"width": 80,
"height": 80,
"inputs": [
{
"x": 0,
"y": 40,
"side": "left"
}
],
"outputs": [
{
"x": 80,
"y": 40,
"side": "right"
}
],
"defaultParams": {
"BitIndex": 0,
"NumBits": 32
},
"render": {
"type": "image",
"src": "exports/bitClear.svg",
"svgMode": "element",
"preserveAspectRatio": "none",
"x": 0,
"y": 0,
"width": 80,
"height": 80
}
},
{
"type": "extractBits",
"label": "Extract Bits",
"icon": "extractBits.svg",
"phases": [
"ALGEBRAIC"
],
"width": 90,
"height": 80,
"inputs": [
{
"x": 0,
"y": 40,
"side": "left"
}
],
"outputs": [
{
"x": 90,
"y": 40,
"side": "right"
}
],
"defaultParams": {
"StartBit": 0,
"NumBitsToExtract": 8,
"NumBits": 32,
"OutputScaling": "keepWeight"
},
"render": {
"type": "image",
"src": "exports/extractBits.svg",
"svgMode": "element",
"preserveAspectRatio": "none",
"x": 0,
"y": 0,
"width": 90,
"height": 80
}
},
{
"type": "shiftArithmetic",
"label": "Shift Arithmetic",
"icon": "shiftArithmetic.svg",
"phases": [
"ALGEBRAIC"
],
"width": 90,
"height": 80,
"inputs": [
{
"x": 0,
"y": 40,
"side": "left"
}
],
"outputs": [
{
"x": 90,
"y": 40,
"side": "right"
}
],
"defaultParams": {
"ShiftDirection": "Left",
"ShiftNumber": 1
},
"render": {
"type": "image",
"src": "exports/shiftArithmetic.svg",
"svgMode": "element",
"preserveAspectRatio": "none",
"x": 0,
"y": 0,
"width": 90,
"height": 80
}
},
{
"type": "combinatorialLogic",
"label": "Combinatorial Logic",
"icon": "combinatorialLogic.svg",
"phases": [
"ALGEBRAIC"
],
"width": 90,
"height": 80,
"inputs": [
{
"x": 0,
"y": 40,
"side": "left"
}
],
"outputs": [
{
"x": 90,
"y": 40,
"side": "right"
}
],
"defaultParams": {
"TruthTable": [
0,
1,
1,
0
]
},
"render": {
"type": "image",
"src": "exports/combinatorialLogic.svg",
"svgMode": "element",
"preserveAspectRatio": "none",
"x": 0,
"y": 0,
"width": 90,
"height": 80
}
},
{
"type": "compareToZero",
"label": "Compare Zero",
"icon": "compareToZero.svg",
"phases": [
"ALGEBRAIC"
],
"width": 100,
"height": 80,
"inputs": [
{
"x": 0,
"y": 40,
"side": "left"
}
],
"outputs": [
{
"x": 100,
"y": 40,
"side": "right"
}
],
"defaultParams": {
"relop": "ne"
},
"render": {
"type": "image",
"src": "exports/compareToZero.svg",
"svgMode": "element",
"preserveAspectRatio": "none",
"x": 0,
"y": 0,
"width": 100,
"height": 80
}
},
{
"type": "relationalOperator",
"label": "Relational",
"icon": "relationalOperator.svg",
"phases": [
"ALGEBRAIC"
],
"width": 100,
"height": 80,
"inputs": [
{
"x": 0,
"y": 30,
"side": "left"
},
{
"x": 0,
"y": 50,
"side": "left"
}
],
"outputs": [
{
"x": 100,
"y": 40,
"side": "right"
}
],
"defaultParams": {
"Operator": "ge"
},
"render": {
"type": "image",
"src": "exports/relationalOperator.svg",
"svgMode": "element",
"preserveAspectRatio": "none",
"x": 0,
"y": 0,
"width": 100,
"height": 80
}
},
{
"type": "switchCase",
"label": "Switch Case",
"icon": "switchCase.svg",
"phases": [
"ALGEBRAIC"
],
"width": 100,
"height": 80,
"inputs": [
{
"x": 0,
"y": 40,
"side": "left"
}
],
"outputs": [
{
"x": 100,
"y": 30,
"side": "right"
},
{
"x": 100,
"y": 50,
"side": "right"
}
],
"defaultParams": {
"CaseConditions": "{1}",
"ShowDefaultCase": "on"
},
"render": {
"type": "image",
"src": "exports/switchCase.svg",
"svgMode": "element",
"preserveAspectRatio": "none",
"x": 0,
"y": 0,
"width": 100,
"height": 80
}
},
{
"type": "if",
"label": "If",
"icon": "if.svg",
"phases": [
"ALGEBRAIC"
],
"width": 100,
"height": 80,
"inputs": [
{
"x": 0,
"y": 30,
"side": "left"
},
{
"x": 0,
"y": 50,
"side": "left"
}
],
"outputs": [
{
"x": 100,
"y": 30,
"side": "right"
},
{
"x": 100,
"y": 50,
"side": "right"
}
],
"defaultParams": {
"IfExpression": "u1 > 0",
"ElseIfExpressions": "",
"ShowElse": "on"
},
"render": {
"type": "image",
"src": "exports/if.svg",
"svgMode": "element",
"preserveAspectRatio": "none",
"x": 0,
"y": 0,
"width": 100,
"height": 80
}
},
{
"type": "logicalOperator",
"label": "Logical Operator",
"icon": "logicalOperator.svg",
"phases": [
"ALGEBRAIC"
],
"width": 80,
"height": 80,
"inputs": [
{
"x": 0,
"y": 30,
"side": "left"
},
{
"x": 0,
"y": 50,
"side": "left"
}
],
"outputs": [
{
"x": 80,
"y": 40,
"side": "right"
}
],
"defaultParams": {
"Operator": "AND"
},
"render": {
"type": "image",
"src": "exports/logicalOperator.svg",
"svgMode": "element",
"preserveAspectRatio": "none",
"x": 0,
"y": 0,
"width": 80,
"height": 80
}
}
]
}
modules/nflow_blocks/src/cpp/logic/intervalTest.cpp//=============================================================================
// Copyright (c) 2016-present Allan CORNET (Nelson)
//=============================================================================
// This file is part of Nelson.
//=============================================================================
// LICENCE_BLOCK_BEGIN
// SPDX-License-Identifier: LGPL-3.0-or-later
// LICENCE_BLOCK_END
//=============================================================================
#include "SimEngineTypes.hpp"
#include "BlockRegistry.hpp"
#include "FieldNames.hpp"
#include "NFlowBlockDescriptor.hpp"
#include "NFlowCodegenHelpers.hpp"
#include <cmath>
#include "logic_blocks.hpp"
//=============================================================================
// intervalTest: output 1.0 when the input lies between LowerLimit and
// UpperLimit, else 0.0. The interval is closed on the left/right according to
// IntervalClosedLeft / IntervalClosedRight (both closed by default). Pure
// algebraic feedthrough, element-wise over the input width.
//=============================================================================
bool
Nelson::NFlow::handleIntervalTest(SimCtx& ctx, const Block& b, Phase phase)
{
if (phase != Phase::ALGEBRAIC) {
return false;
}
if (!hasInput(ctx, b.nid, 0)) {
return false;
}
nflow::BlockDescriptor bd(b, ctx.variables);
const double lo = bd.paramDouble("LowerLimit", 0.0);
const double up = bd.paramDouble("UpperLimit", 1.0);
const bool cl = bd.paramDouble("IntervalClosedLeft", 1.0) != 0.0;
const bool cr = bd.paramDouble("IntervalClosedRight", 1.0) != 0.0;
SigView u = getInputSig(ctx, b.nid, 0);
return emitElementwise(ctx, b.nid, [&](int i) -> double {
const double x = sigAt(u, i);
const bool okLo = cl ? (x >= lo) : (x > lo);
const bool okUp = cr ? (x <= up) : (x < up);
return (okLo && okUp) ? 1.0 : 0.0;
});
}
//=============================================================================
Nelson::NFlow::BlockCodegenTemplate
Nelson::NFlow::getCodeGenCIntervalTest()
{
BlockCodegenTemplate t;
t.emitStep = [](const BlockCodegenArgs& a) {
nflow::BlockDescriptor bd(*a.block, *a.variables);
const double lo = bd.paramDouble("LowerLimit", 0.0);
const double up = bd.paramDouble("UpperLimit", 1.0);
const bool cl = bd.paramDouble("IntervalClosedLeft", 1.0) != 0.0;
const bool cr = bd.paramDouble("IntervalClosedRight", 1.0) != 0.0;
const std::string x = a.in[0];
const std::string opL = cl ? " >= " : " > ";
const std::string opR = cr ? " <= " : " < ";
a.line("out_" + a.id + " = (" + x + opL + a.fmt(lo) + " && " + x + opR + a.fmt(up)
+ ") ? 1.0 : 0.0;");
};
return t;
}
//=============================================================================
Nelson::NFlow::BlockCodegenTemplate
Nelson::NFlow::getCodeGenRustIntervalTest()
{
BlockCodegenTemplate t;
t.emitStep = [](const BlockCodegenArgs& a) {
nflow::BlockDescriptor bd(*a.block, *a.variables);
const double lo = bd.paramDouble("LowerLimit", 0.0);
const double up = bd.paramDouble("UpperLimit", 1.0);
const bool cl = bd.paramDouble("IntervalClosedLeft", 1.0) != 0.0;
const bool cr = bd.paramDouble("IntervalClosedRight", 1.0) != 0.0;
const std::string x = a.in[0];
const std::string opL = cl ? " >= " : " > ";
const std::string opR = cr ? " <= " : " < ";
a.line("out_" + a.id + " = if " + x + opL + a.fmt(lo) + " && " + x + opR + a.fmt(up)
+ " { 1.0_f64 } else { 0.0_f64 };");
};
return t;
}
//=============================================================================
d.blocks={ struct('id','c','type','constant','inputs',0,'outputs',1,'params',struct('Value',0.5)), struct('id','it','type','intervalTest','inputs',1,'outputs',1,'params',struct('LowerLimit',0,'UpperLimit',1)), struct('id','w','type','toWorkspace','inputs',1,'outputs',0,'params',struct('VariableName','y','SaveFormat','Array')) };
d.connections={ struct('from','c','to','it','fromIndex',0,'toIndex',0), struct('from','it','to','w','fromIndex',0,'toIndex',0) };
d.sampleTime=0.1; d.duration=0.2; d.solver='discrete'; d.variables=struct();
r=jsondecode(__nflow_simulate__(jsonencode(d)));
| Version | Description |
|---|---|
| 1.0.0 | initial version |