Commit: d72274eb5a685c634e5129fbdfdea6f618c22f08
Parent: 8d401201b9f05b3c786f9454c3e496d9087af8d7
Author: Randy Palamar
Date: Thu, 27 Aug 2026 06:26:46 -0700
das: FORCES: correct transit y distance calculation
The orignal calculation is incorrect when the number of channels
in the transmit direction is not equal to the number of channels
in the receive direction. Currently we only pass the number of
channels in the receive direction so the beamformer has no way of
knowing this.
However the calculation itself was meant to find the distance from
the beamformed point to center of the transmit focus (which was
implicitly assumed to be at the world origin). To do that we
needed to undo the transformation of the world point to an xdc
relative world point. We don't need to do that if we don't
transform the world point to begin with (see both HERCULES and RCA
algorithms). Additionally we should have been adding an offset to
the transmit focus which may be non zero if we are walking the
focus (e.g. Walking FORCES).
Both of these are now done here but the live imaging code may not
be sending the transmit focus when doing FORCES imaging. The
throughput test here already does (if the saved parameters contain
it).
Diffstat:
2 files changed, 19 insertions(+), 14 deletions(-)
diff --git a/beamformer_core.c b/beamformer_core.c
@@ -916,9 +916,6 @@ plan_compute_pipeline(BeamformerComputePlan *cp, BeamformerParameterBlock *pb, A
memory_copy(cp->das_voxel_transform.E, cp->voxel_transform.E, sizeof(cp->voxel_transform));
u32 id = pb->parameters.acquisition_kind;
- if (id == BeamformerAcquisitionKind_UFORCES || id == BeamformerAcquisitionKind_FORCES)
- cp->das_voxel_transform = m4_mul(cp->xdc_transform, cp->das_voxel_transform);
-
db->Sparse = id == BeamformerAcquisitionKind_UFORCES || id == BeamformerAcquisitionKind_UHERCULES;
db->SingleFocus = pb->parameters.single_focus;
db->SingleOrientation = pb->parameters.single_orientation;
diff --git a/shaders/das.glsl b/shaders/das.glsl
@@ -287,18 +287,22 @@ RESULT_TYPE HERCULES(const vec3 world_point)
return result;
}
-RESULT_TYPE FORCES(const vec3 xdc_world_point)
+RESULT_TYPE FORCES(const vec3 world_point)
{
RESULT_TYPE result = RESULT_TYPE(0);
- float z_delta_squared = xdc_world_point.z * xdc_world_point.z;
- float transmit_y_delta = xdc_world_point.y - xdc_element_pitch.y * ChannelCount / 2;
- float transmit_yz_squared = transmit_y_delta * transmit_y_delta + z_delta_squared;
+ const vec3 xdc_world_point = (xdc_transform * vec4(world_point, 1)).xyz;
+
+ // TODO(rnp): the sign of the origin offset might be flipped
+ f32 origin_offset = FocusDepth * tan(radians(TransmitAngle));
+ f32 transmit_y_delta = world_point.y + origin_offset;
+ f32 z_delta_squared = xdc_world_point.z * xdc_world_point.z;
+ f32 transmit_yz_squared = transmit_y_delta * transmit_y_delta + z_delta_squared;
for (f32 chunk_channel = 0; chunk_channel < f32(ChunkChannelCount); chunk_channel += 1.f) {
- float rx_channel = f32(channel_offset) + chunk_channel;
- float receive_x_delta = xdc_world_point.x - rx_channel * xdc_element_pitch.x;
- float a_arg = abs(FNumber * receive_x_delta / xdc_world_point.z);
+ f32 rx_channel = f32(channel_offset) + chunk_channel;
+ f32 receive_x_delta = xdc_world_point.x - rx_channel * xdc_element_pitch.x;
+ f32 a_arg = abs(FNumber * receive_x_delta / xdc_world_point.z);
if (a_arg < 0.5f) {
u64 rf_pointer = RFData + InputDataKindByteSize * (u32(chunk_channel) * SampleCount * AcquisitionCount + u32(Sparse) * SampleCount);
@@ -320,13 +324,17 @@ RESULT_TYPE FORCES(const vec3 xdc_world_point)
return result;
}
-RESULT_TYPE READI_FORCES(const vec3 xdc_world_point)
+RESULT_TYPE READI_FORCES(const vec3 world_point)
{
RESULT_TYPE result = RESULT_TYPE(0);
- float z_delta_squared = xdc_world_point.z * xdc_world_point.z;
- float transmit_y_delta = xdc_world_point.y - xdc_element_pitch.y * ChannelCount / 2;
- float transmit_yz_squared = transmit_y_delta * transmit_y_delta + z_delta_squared;
+ const vec3 xdc_world_point = (xdc_transform * vec4(world_point, 1)).xyz;
+
+ // TODO(rnp): the sign of the origin offset might be flipped
+ f32 origin_offset = FocusDepth * tan(radians(TransmitAngle));
+ f32 transmit_y_delta = world_point.y + origin_offset;
+ f32 z_delta_squared = xdc_world_point.z * xdc_world_point.z;
+ f32 transmit_yz_squared = transmit_y_delta * transmit_y_delta + z_delta_squared;
// NOTE(tkh): The row we use matches the acquisition group, the column is the element group we are beamforming.
s32 hadamard_offset = s32(readi_group) * s32(ReadiGroupCount);