Connect Independent Transmitters and Receivers#
Place transmitters and receivers independently, then choose which receivers participate in each transmitter’s paths. Physical placement and receiver connectivity are separate choices.
Before you start. Create the base sensor and output in Set Up a Sensor. The USD Python tabs assume that base exists; the subset tab modifies the multistatic pattern.
Choose receiver connections#
A receiver pattern defines element poses. A mapping chooses receiver elements from that pattern; it does not place or move them. Every transmitter element in the selected transmitter pattern uses that pattern’s mapping mode:
Lines identify receiver connections. nton selects all receivers;
zero-bloom 1to1 pairs equal flat indices. The illustrated map group
[2, 0, 2] places receiver 2 in two distinct slots. Radius-one bloom
expands a grid pair to nine ordered slots, with the center in slot 4.
These illustrative connections are schematic, not to scale.#
Use
ntonwhen every active receiver should participate. The first recipe below uses this mode for two transmitters and three receivers.Use zero-bloom
1to1for paired elements with equal transmitter and receiver counts. Pairing follows flat indices; co-location requires matching poses as in Sample a Depth Grid.Use
mapfor an explicit ordered receiver group. The second recipe below selects a subset without changing the receiver geometry.
A selected connection still needs a valid path through the scene. Receiver orientation defines the frame used by receiver-relative geometry; it does not define a receiving cone. The full mapping rules are on USD Attributes, and Sample a Finite-Width Beam adds receiver bloom to matched grids.
Transmitter and receiver patterns#
Free patterns allow two transmitters and three receivers to be positioned
independently. Both elements belong to one stored transmitter pattern, so each
emits the same 3 by 3 Grid ray bundle in its own frame.
nton evaluates every receiver, allocating 2 * 9 * 3 = 54 slots.
The example uses a 3.9 mm electromagnetic wavelength. The configuration below separates transmitter and receiver geometry to demonstrate bistatic path lengths:
#usda 1.0
(
defaultPrim = "World"
metersPerUnit = 1
upAxis = "Z"
timeCodesPerSecond = 10
subLayers = [@generic_wpm_base.usda@]
)
over "World"
{
over "Sensor"
{
# About 77 GHz in vacuum; material response depends on wavelength.
float omni:sensor:GenericWpm:wavelengthNm = 3900000
token[] omni:sensor:GenericWpm:tx:patternTypes = ["free"]
uint[] omni:sensor:GenericWpm:freePatterns:tx:numElementsPerPattern = [2]
float3[] omni:sensor:GenericWpm:freePatterns:tx:positions = [(0, -0.5, 0), (0, 0.5, 0)]
float4[] omni:sensor:GenericWpm:freePatterns:tx:orientations = [(0, 0, 0, 1), (0, 0, 0, 1)]
token[] omni:sensor:GenericWpm:rx:patternTypes = ["free"]
uint[] omni:sensor:GenericWpm:freePatterns:rx:numElementsPerPattern = [3]
float3[] omni:sensor:GenericWpm:freePatterns:rx:positions = [(0, -0.4, 0), (0, 0, 0), (0, 0.4, 0)]
float4[] omni:sensor:GenericWpm:freePatterns:rx:orientations = [(0, 0, 0, 1), (0, 0, 0, 1), (0, 0, 0, 1)]
# Each transmitter emits the same nine transmitter-relative rays.
token[] omni:sensor:GenericWpm:ray:patternTypes = ["grid"]
token[] omni:sensor:GenericWpm:gridPatterns:ray:modes = ["SPHERICAL"]
uint[] omni:sensor:GenericWpm:gridPatterns:ray:numRows = [3]
uint[] omni:sensor:GenericWpm:gridPatterns:ray:numCols = [3]
float[] omni:sensor:GenericWpm:gridPatterns:ray:startRowUnits = [-5]
float[] omni:sensor:GenericWpm:gridPatterns:ray:endRowUnits = [5]
float[] omni:sensor:GenericWpm:gridPatterns:ray:startColUnits = [-5]
float[] omni:sensor:GenericWpm:gridPatterns:ray:endColUnits = [5]
token[] omni:sensor:GenericWpm:gridPatterns:ray:layouts = ["rowMajor"]
float3[] omni:sensor:GenericWpm:gridPatterns:ray:positions = [(0, 0, 0)]
float4[] omni:sensor:GenericWpm:gridPatterns:ray:orientations = [(0, 0, 0, 1)]
uint[] omni:sensor:GenericWpm:tx:rayPatternIndices = [0]
token[] omni:sensor:GenericWpm:tx:mappingModes = ["nton"]
}
}
sensor = stage.GetPrimAtPath("/World/Sensor")
ns = "omni:sensor:GenericWpm:"
# About 77 GHz in vacuum; material response depends on wavelength.
sensor.CreateAttribute(ns + "wavelengthNm", Sdf.ValueTypeNames.Float, custom=False).Set(3900000)
for role, positions in (("tx", [(0, -0.5, 0), (0, 0.5, 0)]), ("rx", [(0, -0.4, 0), (0, 0, 0), (0, 0.4, 0)])):
sensor.CreateAttribute(ns + role + ":patternTypes", Sdf.ValueTypeNames.TokenArray, custom=False).Set(["free"])
free = ns + "freePatterns:" + role + ":"
sensor.CreateAttribute(free + "numElementsPerPattern", Sdf.ValueTypeNames.UIntArray, custom=False).Set(
[len(positions)]
)
sensor.CreateAttribute(free + "positions", Sdf.ValueTypeNames.Float3Array, custom=False).Set(positions)
sensor.CreateAttribute(free + "orientations", Sdf.ValueTypeNames.Float4Array, custom=False).Set(
[(0, 0, 0, 1)] * len(positions)
)
# Each transmitter emits the same nine transmitter-relative rays.
sensor.CreateAttribute(ns + "ray:patternTypes", Sdf.ValueTypeNames.TokenArray, custom=False).Set(["grid"])
ray = ns + "gridPatterns:ray:"
sensor.CreateAttribute(ray + "modes", Sdf.ValueTypeNames.TokenArray, custom=False).Set(["SPHERICAL"])
sensor.CreateAttribute(ray + "numRows", Sdf.ValueTypeNames.UIntArray, custom=False).Set([3])
sensor.CreateAttribute(ray + "numCols", Sdf.ValueTypeNames.UIntArray, custom=False).Set([3])
sensor.CreateAttribute(ray + "startRowUnits", Sdf.ValueTypeNames.FloatArray, custom=False).Set([-5])
sensor.CreateAttribute(ray + "endRowUnits", Sdf.ValueTypeNames.FloatArray, custom=False).Set([5])
sensor.CreateAttribute(ray + "startColUnits", Sdf.ValueTypeNames.FloatArray, custom=False).Set([-5])
sensor.CreateAttribute(ray + "endColUnits", Sdf.ValueTypeNames.FloatArray, custom=False).Set([5])
sensor.CreateAttribute(ray + "layouts", Sdf.ValueTypeNames.TokenArray, custom=False).Set(["rowMajor"])
sensor.CreateAttribute(ray + "positions", Sdf.ValueTypeNames.Float3Array, custom=False).Set([(0, 0, 0)])
sensor.CreateAttribute(ray + "orientations", Sdf.ValueTypeNames.Float4Array, custom=False).Set([(0, 0, 0, 1)])
sensor.CreateAttribute(ns + "tx:rayPatternIndices", Sdf.ValueTypeNames.UIntArray, custom=False).Set([0])
sensor.CreateAttribute(ns + "tx:mappingModes", Sdf.ValueTypeNames.TokenArray, custom=False).Set(["nton"])
One recorded run produced two valid contributions: the central ray of each transmitter reached its nearest receiver, with a 0.1 m transmitter–receiver baseline. Other ray slots were invalid despite intersecting geometry. The exact valid count is specific to that run and scene.
Half of the total path distance is not generally range in this layout.
RxRelativePosM expresses the hit in each receiving element’s frame.
The output remains scalar transport data, not coherent I/Q or radar detections.
The stationary scene does not exercise nonzero path velocity.
Selecting a receiver subset#
Use map to choose explicit receivers and their output order. A receiver ID
is an element index in the active receiver pattern. A receiver slot is its
position in the selected group; these two indices need not match.
This layer selects receiver IDs [2, 0] from page 0, reducing capacity to
2 * 9 * 2 = 36 slots while preserving both nearest receivers:
#usda 1.0
(
defaultPrim = "World"
metersPerUnit = 1
upAxis = "Z"
timeCodesPerSecond = 10
subLayers = [@generic_wpm_multistatic.usda@]
)
over "World"
{
over "Sensor"
{
token[] omni:sensor:GenericWpm:tx:mappingModes = ["map"]
uint[] omni:sensor:GenericWpm:tx:mappingTargetRxGroups = [0]
uint[] omni:sensor:GenericWpm:txRxMappings:numRxGroupsPerPages = [1]
uint[] omni:sensor:GenericWpm:txRxMappings:numRxPerGroups = [2]
# Receiver-slot order is explicit and local to the selected Rx pattern.
uint[] omni:sensor:GenericWpm:txRxMappings:rxIndices = [2, 0]
}
}
sensor = stage.GetPrimAtPath("/World/Sensor")
ns = "omni:sensor:GenericWpm:"
sensor.GetAttribute(ns + "tx:mappingModes").Set(["map"])
sensor.CreateAttribute(ns + "tx:mappingTargetRxGroups", Sdf.ValueTypeNames.UIntArray, custom=False).Set([0])
mapping = ns + "txRxMappings:"
sensor.CreateAttribute(mapping + "numRxGroupsPerPages", Sdf.ValueTypeNames.UIntArray, custom=False).Set([1])
sensor.CreateAttribute(mapping + "numRxPerGroups", Sdf.ValueTypeNames.UIntArray, custom=False).Set([2])
# Receiver-slot order is explicit and local to the selected Rx pattern.
sensor.CreateAttribute(mapping + "rxIndices", Sdf.ValueTypeNames.UIntArray, custom=False).Set([2, 0])
In that recorded run, two contributions remained valid. Receiver slot 0 means receiver 2 and slot 1 means receiver 0; output order follows the authored list.
Attribute meanings are on USD Attributes; complete types and defaults are in Sensor API Schemas.
Verification#
Confirm capacity 54 for the all-receivers layout and 36 for the ordered subset.
Use logical receiver-slot indices to interpret
RxRelativePosM: subset slot 0 addresses receiver 2 and slot 1 addresses receiver 0.Compare total path distances, not half-distance range, for separated emission and receiver origins.
Potential issues#
Use the Generic wpm checks for confirming checks and recovery.
A mapping selection is rejected. Every group on the selected page must fit the active receiver pattern, including groups unused by this transmitter. Refer to receiver mapping.
Receiver-slot positions appear reordered. Slots follow the authored group order, including duplicate receiver indices. Refer to Outputs.