Commit: a0b6c178ccc7b57615321204efae91100c038804
Parent: 4264df96ac72f7082e07ca3295954a470179f07c
Author: Randy Palamar
Date: Tue, 13 Oct 2026 11:50:45 -0600
das: FORCES: consider tx_rx_orientation, remove naming ambiquity
I still feel like this is pointless complexity when FORCES only
supports 2D reconstruction but others insist that the interface
should match the 3D interface even if it doesn't make sense. It is
still just a single compile time index so oh well.
fixes #80
Diffstat:
5 files changed, 52 insertions(+), 88 deletions(-)
diff --git a/math.c b/math.c
@@ -795,38 +795,6 @@ baseband_chirp(Arena *arena, f32 min_frequency, f32 max_frequency, f32 sampling_
return result;
}
-function iv3
-das_output_dimension(iv3 points)
-{
- iv3 result;
- result.x = Max(points.x, 1);
- result.y = Max(points.y, 1);
- result.z = Max(points.z, 1);
-
- switch (iv3_dimension(result)) {
- case 1:{
- if (result.y > 1) result.x = result.y;
- if (result.z > 1) result.x = result.z;
- result.y = result.z = 1;
- }break;
-
- case 2:{
- if (result.x > 1) {
- if (result.z > 1) result.y = result.z;
- } else {
- result.x = result.z;
- }
- result.z = 1;
- }break;
-
- case 3:{}break;
-
- InvalidDefaultCase;
- }
-
- return result;
-}
-
function m4
das_transform_1d(v3 p1, v3 p2)
{
@@ -902,22 +870,6 @@ das_transform_3d(v3 min_coordinate, v3 max_coordinate)
return result;
}
-function m4
-das_transform(v3 min_coordinate, v3 max_coordinate, iv3 *points)
-{
- m4 result;
-
- *points = das_output_dimension(*points);
-
- switch (iv3_dimension(*points)) {
- case 1:{result = das_transform_1d( min_coordinate, max_coordinate); }break;
- case 2:{result = das_transform_2d_xz(XY(min_coordinate), XY(max_coordinate), 0);}break;
- case 3:{result = das_transform_3d( min_coordinate, max_coordinate); }break;
- }
-
- return result;
-}
-
function v3
plane_normal_from_transform(m4 transform)
{
diff --git a/shaders/das.glsl b/shaders/das.glsl
@@ -343,13 +343,18 @@ RESULT_TYPE FORCES(const vec3 world_point)
{
RESULT_TYPE result = RESULT_TYPE(0);
- const vec3 xdc_world_point = (xdc_transform * vec4(world_point, 1)).xyz;
-
+ const u8 tx_rx_orientation = tx_rx_orientation_for_acquisition(0);
+ const s32 rx_axis = s32(RX_ORIENTATION(tx_rx_orientation) == RCAOrientation_Rows);
+ const f32 element_pitch = xdc_element_pitch[rx_axis];
// 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;
+ const f32 origin_offset = FocusDepth * tan(radians(TransmitAngle));
+
+ vec3 xdc_world_point = (xdc_transform * vec4(world_point, 1)).xyz;
+ f32 lateral_coord = xdc_world_point[rx_axis];
+ f32 axial_coord = xdc_world_point.z;
+ f32 tx_out_of_plane_delta = world_point[rx_axis ^ 1] + origin_offset;
+ f32 z_delta_squared = axial_coord * axial_coord;
+ f32 transmit_oopz_squared = tx_out_of_plane_delta * tx_out_of_plane_delta + z_delta_squared;
#define AcquisitionChunkSize 16
const u32 AcquisitionChunkCount = (AcquisitionCount + (AcquisitionChunkSize - 1)) / AcquisitionChunkSize;
@@ -360,18 +365,18 @@ RESULT_TYPE FORCES(const vec3 world_point)
if (((AcquisitionCount + u32(Sparse)) % AcquisitionChunkSize) != 0 && (index >= AcquisitionCount))
break;
f32 tx_channel = Sparse ? f32(S16(HeapBase + SparseElements - 2 * u32(Sparse)).x[index]) : f32(index);
- f32 transmit_x_delta = xdc_world_point.x - xdc_element_pitch.x * tx_channel;
- transmit_indices[transmit] = sample_index(sqrt(transmit_yz_squared + transmit_x_delta * transmit_x_delta));
+ f32 transmit_delta = lateral_coord - element_pitch * tx_channel;
+ transmit_indices[transmit] = sample_index(sqrt(transmit_oopz_squared + transmit_delta * transmit_delta));
}
for (u32 channel = 0; channel < batch_channel_count(); channel += 1u) {
- f32 rx_channel = f32(channel_offset + 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);
+ f32 rx_channel = f32(channel_offset + channel);
+ f32 receive_delta = lateral_coord - rx_channel * element_pitch;
+ f32 a_arg = abs(FNumber * receive_delta / axial_coord);
if (a_arg < 0.5f) {
u64 rf_pointer = rf_data_pointer(channel, acquisition);
- f32 receive_index = sqrt(receive_x_delta * receive_x_delta + z_delta_squared) * SamplingFrequency / SpeedOfSound;
+ f32 receive_index = sqrt(receive_delta * receive_delta + z_delta_squared) * SamplingFrequency / SpeedOfSound;
f32 apodization = apodize(a_arg);
for (u32 transmit = 0; transmit < AcquisitionChunkSize; transmit += 1u, rf_pointer += AcquisitionByteStride) {
if (((AcquisitionCount + u32(Sparse)) % AcquisitionChunkSize) != 0 &&
@@ -391,26 +396,30 @@ RESULT_TYPE READI_FORCES(const vec3 world_point)
{
RESULT_TYPE result = RESULT_TYPE(0);
- const vec3 xdc_world_point = (xdc_transform * vec4(world_point, 1)).xyz;
-
+ const u8 tx_rx_orientation = tx_rx_orientation_for_acquisition(0);
+ const s32 rx_axis = s32(RX_ORIENTATION(tx_rx_orientation) == RCAOrientation_Rows);
+ const f32 element_pitch = xdc_element_pitch[rx_axis];
// 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;
+ const f32 origin_offset = FocusDepth * tan(radians(TransmitAngle));
+
+ vec3 xdc_world_point = (xdc_transform * vec4(world_point, 1)).xyz;
+ f32 lateral_coord = xdc_world_point[rx_axis];
+ f32 axial_coord = xdc_world_point.z;
+ f32 tx_out_of_plane_delta = world_point[rx_axis ^ 1] + origin_offset;
+ f32 z_delta_squared = axial_coord * axial_coord;
+ f32 transmit_oopz_squared = tx_out_of_plane_delta * tx_out_of_plane_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);
for (f32 chunk_channel = 0; chunk_channel < f32(batch_channel_count()); chunk_channel += 1.f) {
- 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);
+ f32 rx_channel = f32(channel_offset) + chunk_channel;
+ f32 receive_delta = lateral_coord - rx_channel * element_pitch;
+ f32 a_arg = abs(FNumber * receive_delta / axial_coord);
if (a_arg < 0.5f) {
- u64 channel_rf_pointer = rf_data_pointer(u32(chunk_channel), 0);
- f32 receive_index = sample_index(sqrt(receive_x_delta * receive_x_delta + z_delta_squared));
+ f32 receive_index = sample_index(sqrt(receive_delta * receive_delta + z_delta_squared));
f32 apodization = apodize(a_arg);
// NOTE(tkh): Iterating over groups of tx elements, each group is AcquisitionCount
@@ -418,16 +427,15 @@ RESULT_TYPE READI_FORCES(const vec3 world_point)
// acquisition, the second element in each group is beamformed using the second acquisition, etc.
for (s32 tx_group = 0; tx_group < s32(ReadiGroupCount); tx_group++) {
f32 group_apodization = apodization * F16(HeapBase + Hadamard).x[hadamard_offset + tx_group];
- u64 rf_pointer = channel_rf_pointer;
- for (f32 tx_event = 0; tx_event < f32(AcquisitionCount); tx_event += 1.f) {
- f32 tx_element = f32(tx_group) * f32(AcquisitionCount) + tx_event;
- f32 transmit_x_delta = xdc_world_point.x - xdc_element_pitch.x * tx_element;
- f32 transmit_index = sqrt(transmit_yz_squared + transmit_x_delta * transmit_x_delta) * SamplingFrequency / SpeedOfSound;
+ u64 rf_pointer = rf_data_pointer(u32(chunk_channel), 0);
+ for (f32 tx_event = 0; tx_event < f32(AcquisitionCount); tx_event += 1.f, rf_pointer += AcquisitionByteStride) {
+ f32 tx_element = f32(tx_group) * f32(AcquisitionCount) + tx_event;
+ f32 transmit_delta = lateral_coord - element_pitch * tx_element;
+ f32 transmit_index = sqrt(transmit_oopz_squared + transmit_delta * transmit_delta) * SamplingFrequency / SpeedOfSound;
SAMPLE_TYPE value = group_apodization * sample_rf(rf_pointer, receive_index + transmit_index);
- result += RESULT_STORE(value);
- rf_pointer += AcquisitionByteStride;
+ result += RESULT_STORE(value);
}
}
}
diff --git a/tests/test_common.c b/tests/test_common.c
@@ -13,6 +13,9 @@
#include "external/zemp_bp.h"
+#define RXOrientation(tx_rx) ((ZBP_RCAOrientation)((tx_rx) >> 0 & 0x0F))
+#define TXOrientation(tx_rx) ((ZBP_RCAOrientation)((tx_rx) >> 4 & 0x0F))
+
typedef struct {
ZBP_DataKind kind;
ZBP_DataCompressionKind compression_kind;
diff --git a/tests/throughput.c b/tests/throughput.c
@@ -87,12 +87,17 @@ execute_study(Arena *arena, Stream path, Options *options)
die("failed to load parameters file: %s\n", (char *)path.data);
stream_reset(&path, path_work_index);
- v3 min_coordinate = (v3){{g_lateral_extent.x, g_axial_extent.x, 0}};
- v3 max_coordinate = (v3){{g_lateral_extent.y, g_axial_extent.y, 0}};
- bp.das_voxel_transform = das_transform(min_coordinate, max_coordinate, &g_output_points);
-
- bp.output_points.xyz = g_output_points;
- bp.output_points.w = 1;
+ v2 min_coordinate = (v2){{g_lateral_extent.x, g_axial_extent.x}};
+ v2 max_coordinate = (v2){{g_lateral_extent.y, g_axial_extent.y}};
+ if (RXOrientation(bp.transmit_receive_orientation) == ZBP_RCAOrientation_Rows)
+ bp.das_voxel_transform = das_transform_2d_yz(min_coordinate, max_coordinate, 0);
+ else
+ bp.das_voxel_transform = das_transform_2d_xz(min_coordinate, max_coordinate, 0);
+
+ bp.output_points.x = g_output_points.x;
+ bp.output_points.y = g_output_points.z;
+ bp.output_points.z = 1;
+ bp.output_points.w = 1;
bp.f_number = g_f_number;
bp.interpolation_mode = BeamformerInterpolationMode_Cubic;
diff --git a/util.h b/util.h
@@ -183,10 +183,6 @@ typedef struct {
str8 unparsed;
} NumberConversion;
-#define XZ(v) (v2){.x = v.x, .y = v.z}
-#define YZ(v) (v2){.x = v.y, .y = v.z}
-#define XY(v) (v2){.x = v.x, .y = v.y}
-
/* TODO(rnp): delete raylib */
typedef struct {
v3 origin;