Compound Property

Introduced in version 3.8.1

A compound property is a virtual, user-defined property hosted on the System device whose value is computed from the properties of other devices through a small arithmetic expression. Compounds let an experiment present one user-facing knob (for example a single Frequency control) that internally distributes the value across several hardware properties (for example two channels of an RF generator).

Status: manual configuration only. There is no Hardware Wizard support for compound properties yet. They must be added by hand to the CFG file by editing the [Compound] section as described below. Once present, compounds appear under @System like any other property and can be referenced from PPL scripts and connected to GUI controls.

Every compound property is described by up to three keys in the [Compound] section. <Name> is the user-chosen name of the compound (it must not clash with an existing System property such as Shots, XReps etc.).

Entry

Value

Description

<Name>

<unit>, [read/(write)/rwrite/wread], [(nohshake)/hshake], [fu increment], [fu min_increment], [n significant digit], [fu minvalue], [fu maxvalue]

Property metadata in the same comma-separated form used by the Dummy device par[n0] entries (see wildcard rules). The compound's name is prepended automatically. Optional — if omitted, the compound becomes a unit-less property with no enforced limits.

<Name>.definition

<forward expression>

Required. The arithmetic expression evaluated to produce the compound's read value. Identifiers of the form PropName@DeviceName are resolved to that device property's native value. Operators + - * / and parentheses are supported. Numeric literals may carry an SI prefix and an ignored unit body (for example 0.25MHz is read as 0.25 × 106; recognized prefixes are p n u m k M G T).

<Name>.inverse[n1]

<Target@Device>: <expression>

One inverse rule per key, numbered contiguously from 1 (.inverse1, .inverse2, ...). When the compound is written, every rule is evaluated in order and the result is written to the named target property through the normal device queue. Inside the right-hand expression, the bare compound name resolves to the value being written; all other identifiers resolve to the current value of the referenced property. Without any inverse rule the compound is read-only.

A single Frequency knob on the System device whose value is the sum of two RF-generator channels, and whose inverse splits any write equally between them:

Configuration file example:

[Compound] Frequency = Hz, rw, nohshake, 1MHz, 1Hz, 0, 0, 10GHz Frequency.definition = Freq1@RFGen + Freq2@RFGen Frequency.inverse1 = Freq1@RFGen: Frequency / 2 Frequency.inverse2 = Freq2@RFGen: Frequency / 2

With this configuration:

Action

Result

Read Frequency@System

Returns Freq1@RFGen + Freq2@RFGen, recomputed automatically whenever either underlying property changes.

Write Frequency@System = X

Triggers Freq1@RFGen = X/2 and Freq2@RFGen = X/2 in sequence.

Expressions may call built-in functions, written as a name immediately followed by a parenthesized, comma-separated argument list. Each argument is itself a full expression.

Comparison operators >, <, >=, <=, == and != are also available. They have the lowest precedence (evaluated after + - * /) and produce 1 when the comparison is true or 0 when false, so they can feed a condition or be used arithmetically — for example if(Mode@DEV > 3, 1, 2) or (A@D == 0) * 5.

Function

Description

if(cond, then, else)

Inline conditional. Returns then when cond evaluates to a non-zero value, or else otherwise. Only the taken branch is evaluated, so identifiers used only in the untaken branch need not resolve. Example: if(Gate@AWG, 1, 2) yields 1 when Gate@AWG is non-zero and 2 when it is 0.

sel(in, out0, out1, ..., default)

Integer selector. The first argument in is rounded to the nearest integer i; the function returns the i-th output (out0 for i = 0, out1 for i = 1, ...) when i is in range, or the trailing default otherwise. Only the selected branch is evaluated, so identifiers used only in the unchosen branches need not resolve. Useful for mapping a discrete mode/index property to a floating-point setting. Example: sel(Mode@AWG, 0, 5, 12) yields 0 when Mode@AWG is 0, 5 when it is 1, and 12 for any other value.

clamp(x, lo, hi)

Constrain x to the range [lo, hi]: returns lo when x < lo, hi when x > hi, otherwise x. Errors if lo exceeds hi. Handy in an inverse rule to keep a computed write inside a device's safe range, e.g. Att1@RFGen: clamp(Power / 2, 0, 30).

min(a, b, ...), max(a, b, ...)

Smallest or largest of one or more arguments (all are evaluated). Useful to enforce a floor or ceiling, or to combine several channels — for example max(Freq1@RFGen, Freq2@RFGen), or clamping a computed write with min(Power/2, 30).

pdb2lin(x), plin2db(x)

Power decibel conversions: pdb2lin(x) = 10x/10 and plin2db(x) = 10 × log10(x). Use these when the dB value is a power ratio (the usual convention for attenuator and gain specifications). plin2db requires a positive argument.

vdb2lin(x), vlin2db(x)

Voltage/amplitude decibel conversions: vdb2lin(x) = 10x/20 and vlin2db(x) = 20 × log10(x). Use these when the dB value is an amplitude/field ratio. vlin2db requires a positive argument.

pow(x, y)

Power (exponentiation).

Introduced in version 3.8.9

x raised to y. There is no ^ operator in the expression syntax, so this is the only way to take a power. Example: pow(2, 10) gives 1024.

sqrt(x), exp(x), log(x), log10(x), abs(x), atan(x)

General math.

Introduced in version 3.8.9

Square root, natural exponential, natural and base-10 logarithm, absolute value and arc tangent (result in radians). sqrt rejects a negative argument and the logarithms require a positive one.

deg2rad(x), rad2deg(x)

Convert between degrees and radians (× π/180 and × 180/π). Handy when a GUI control is in degrees but the driver property expects radians, or vice versa.

pcal_AWG(calIndex, amplitude)

Power calibration, forward from AWG amplitude. Maps a normalized AWG amplitude in [0, 1] to the power delivered at the amplifier output, in dBm, using the calibration at position calIndex (0-based) in the spectrometer's [PowerCalibrations] table: UnitPower + SystemGain + 20 × log10(amplitude) through the measured compression curve (identity when no .pcal curve is loaded). Example: pcal_AWG(0, 0.7). Amplitude 0 yields negative infinity (no signal).

pcal_dBm(calIndex, driveDbm)

Power calibration, forward from drive power. As pcal_AWG but the AWG drive is given directly as a power in dBm (driveDbm = UnitPower + 20 × log10(amplitude)); returns the amplifier output power in dBm.

pcal_inv_AWG(calIndex, outputDbm)

Power calibration, inverse to AWG amplitude. Given a desired amplifier output power outputDbm, returns the AWG amplitude in [0, 1] required to produce it (the inverse of pcal_AWG). Not clamped: a result above 1 means the requested power is unreachable at full scale.

pcal_inv_dBm(calIndex, outputDbm)

Power calibration, inverse to drive power. Given a desired amplifier output power outputDbm, returns the required AWG drive power in dBm (the inverse of pcal_dBm).

pcal_maxamp(calIndex)

Maximum linear AWG amplitude.

Introduced in version 3.8.9

The highest normalized AWG amplitude that still lands inside the calibrated power range of calibration calIndex. Use it as the amplitude ceiling in your program.

pcal_maxamp_bw(calIndex, bandwidth)

Maximum linear AWG amplitude across a band.

Introduced in version 3.8.9

As pcal_maxamp, but additionally keeping a pulse of the given bandwidth (Hz, full width) linear across the calibration's frequency model — the frequency correction boosts the edges of the band and spends part of the same headroom doing so. Equal to pcal_maxamp when the calibration has no frequency model. Take the lower of the two.

For all pcal_* functions calIndex is the 0-based position in the [PowerCalibrations] table; an out-of-range index is reported as an evaluation error in the message log. The drive side is the AWG (amplitude in [0, 1] or drive power in dBm) and the amplifier output side is always power in dBm. Note the names are case-sensitive — use pcal_dBm, not pcal_dbm. See Power and Frequency Calibration for the calibration model, the .pcal curve, and how to find the maximum linear AWG level.

Topic

Notes

Live updates

The read value of every compound is re-evaluated automatically once per server update tick whenever any of its referenced properties has changed since the last tick. The user-set value is preserved across these refreshes so that GUI input remains stable while the displayed read value tracks the underlying hardware.

Initialization order

The System device is constructed before any hardware driver, so referenced devices need not exist when the [Compound] section is parsed. Resolution happens on the first server update tick after device initialization completes.

Self-reference

A compound whose forward expression refers to itself (for example Gain.definition = Gain@System * 2) is rejected at load time with an error in the message log.

Multi-step writes

Inverse rules are dispatched in numeric order and each goes through the standard WriteProperty queue of its target device. There is no transactional rollback — if rule n fails, rules 1...n-1 have already been applied.

Saved overrides

User-customized MinValue, MaxValue, UserIncrement and scale settings made through the GUI are persisted in the [System] section of the CFG file just like any other device property, and are restored on the next load.

Parsing errors

Any forward or inverse expression that fails to parse causes the offending compound (or rule) to be skipped and an error to be posted to the message log. Other compounds in the section are unaffected.

See also: System device, device setting wildcards, CFG file format.