SignalConverter
SignalConverter converts a quantity in both directions at once.6 minute read
SignalConverter converts a quantity in both directions at once. One port
multiplies by a gain and adds an offset; the other subtracts the offset and
divides. The two are exact inverses, so a value sent out and read back comes
home unchanged.
Use it wherever a quantity crosses a unit boundary both ways — a setpoint going out to hardware and a measurement coming back.
flowchart LR
i1(["input"]) --> B["SignalConverter"]
i2(["inverseInput"]) --> B
p1(["gain"]) --> B
p2(["offset"]) --> B
p3(["inverseOutputReference"]) --> B
p4(["doReferencing"]) --> B
p5(["doReferencingAll"]) --> B
p6(["invert"]) --> B
B --> o1(["output"])
B --> o2(["inverseOutput"])
invertdecides which port multiplies. With it false — the default —inverseOutputmultiplies andoutputdivides. With it true they swap. Everything else about the block is the same.
Automatic referencing does not work correctly at the default
invertsetting. It computes an offset that suits the other direction, so unless your gain is 1 the result will be wrong. Set the offset by hand, or setinvertto true, until this is fixed.
Signals
Inputs
| Path | Unit | Range | Description |
|---|---|---|---|
input |
input unit | unbounded | With invert false this port divides: output = (input − offset) ÷ gain. |
inverseInput |
output unit | unbounded | With invert false this port multiplies: inverseOutput = gain × inverseInput + offset. |
Outputs
| Path | Unit | Description |
|---|---|---|
output |
output unit | The result on the input side. Reads 0 on any channel whose gain is 0, deliberately. |
inverseOutput |
input unit | The result on the inverseInput side. This is the port that referencing aims at. |
Parameters
| Path | Unit | Default | Range | Effect |
|---|---|---|---|---|
gain |
- | 1.0 | unbounded | The conversion factor, per channel. Shared by both directions, which is what makes them exact inverses. A gain of 0 makes the dividing side output 0 rather than failing. |
offset |
output unit | 0.0 | unbounded | Added on the multiplying side, subtracted on the dividing side, per channel. Referencing overwrites this. |
inverseOutputReference |
input unit | zeros | unbounded | The value referencing aims inverseOutput at, per channel. |
doReferencing |
- | false | - | Per channel: solve for the offset that puts inverseOutput at its reference. Cleared automatically. See the caveat above. |
doReferencingAll |
- | false | - | The same for every channel at once. Also cleared automatically. |
invert |
- | false | - | Swaps which port multiplies and which divides. |
All are persistent and survive a controller restart. doReferencing and
doReferencingAll are persistent too, so a configuration saved while one was
set will re-reference on the next startup, against whatever the inputs happen
to read then. Save your configuration after referencing, not during.
No parameters exist below this block.
Setup
- Connect the two directions. With
invertfalse, the multiplying direction isinverseInputtoinverseOutput. - Set
gainper channel, from the units: it is the multiplying side’s output unit divided by its input unit. - Leave
offsetat 0 for now. - Feed a known value into
inverseInputand confirminverseOutputis that value times the gain. - Feed
inverseOutputback intoinputand confirmoutputreturns the original. The round trip should be exact. - Set the offset by hand from a known reference point — see the caveat about automatic referencing above.
Tuning
- There is nothing dynamic here. Both parameters take effect on the next cycle, with no fade and no filtering.
- Set the gain from the units first, and check the round trip before worrying about the offset.
- Set the offset from one known point: put the machine at a position you can
measure, read
inverseInput, and compute the offset that makesinverseOutputread the true value. - If you use
doReferencing, verify the result rather than trusting it — readoffsetback and checkinverseOutputreally lands oninverseOutputReference. At a gain of 1 it will; away from 1 it will not. - Check both directions after every change. The two share one gain and one offset, so a change intended for one affects the other.
- Use a gain of 0 deliberately if you want a channel’s dividing side muted — it outputs 0 rather than failing.
- Nothing here depends on the task rate.
The dashed line is the round trip. It should always be the identity.
| Symptom | Cause | Action |
|---|---|---|
| Referencing put the offset in the wrong place | The referencing formula suits invert true, and the default is false |
Set the offset by hand, or set invert to true |
| Referencing looked right at a gain of 1 | Expected: the two formulas agree only there | Verify at your real gain |
| The round trip does not return the input | The gain or offset changed between the two directions | They are shared, so check nothing else wrote them |
| One direction is scaled and the other is not | That is what the block does — one multiplies, one divides | Check invert |
| The multiplying and dividing ports are the wrong way round | invert is set the other way |
Flip it |
| A channel’s output is 0 | Its gain is 0, so the dividing side outputs 0 deliberately |
Set a non-zero gain |
| The offset changed by itself | A referencing trigger fired | Check whether doReferencing was saved as true |
| Referencing happened at startup | The triggers are persistent, and one was saved as true | Save your configuration after referencing completes |
| Only some channels converted | gain and offset are per channel |
Set every channel |
| The outputs are invalid | An input that is not a valid number passes straight through | Fix the upstream signal |
| I need the two directions scaled differently | Not possible — they share one gain, which is what makes them exact inverses | Use two Gain blocks |
| I need a non-linear conversion | Wrong block | Use Lookup |
| I need to switch it off | Not possible — there is no enable | Set gain to 1 and offset to 0 |
| I need more channels | The count is fixed when the controller is built | Rebuild, or use a second instance |
A starting point for a two-channel pass-through:
gain = [1.0, 1.0]
offset = [0.0, 0.0]
invert = false
Limits and errors
| Limit | Set by | What happens | Reported |
|---|---|---|---|
| Conversion | gain, offset |
One port multiplies and adds, the other subtracts and divides. Exact inverses | output, inverseOutput |
| Direction | invert |
Chooses which port does which | Not reported |
| A gain of 0 | Checked | The dividing side outputs 0 rather than failing | Not reported |
| Automatic referencing | doReferencing |
Computes an offset for the wrong direction when invert is false. Correct only at a gain of 1 |
Not reported — read offset and inverseOutput back |
| Referencing triggers | Fixed | Self-clearing, and persistent — a saved true fires once at startup |
Not reported |
offset after referencing |
Fixed | Overwritten. A later configuration reload restores the saved value, not the referenced one | Read it back |
| Values that are not numbers | Nothing | Not checked. They pass through both paths | Not reported |
| State | None | The block has no memory, deliberately — some parent blocks run it twice in one cycle | Not applicable |
| Enable | None | There is no enable, disable or isEnabled | Not applicable |
| Channel count | Fixed at build time | Cannot be changed from the tree | Not reported |
This block logs nothing, ever. Every condition above shows as a value on a trace, or not at all.
Verified against motorcortex-control3 3.30.0 (bc348fd).