ogl_beamforming

Ultrasound Beamforming Implemented with OpenGL
git clone anongit@rnpnr.xyz:ogl_beamforming.git
Log | Files | Refs | Feed | Submodules | README | LICENSE

Commit: 4264df96ac72f7082e07ca3295954a470179f07c
Parent: 3fb13d375d5ed70351d03c75862ea717e64d371a
Author: Randy Palamar
Date:   Tue, 13 Oct 2026 11:06:54 -0600

tests: common: basic support for HeaderV3

Diffstat:
Mtests/test_common.c | 264+++++++++++++++++++++++++++++++++++++++++++++++++------------------------------
1 file changed, 163 insertions(+), 101 deletions(-)

diff --git a/tests/test_common.c b/tests/test_common.c @@ -38,6 +38,130 @@ die_(char *function_name, char *format, ...) os_exit(1); } +function void +beamformer_simple_parameters_equip_channel_mapping(BeamformerSimpleParameters *bp, u8 *raw, u32 channel_count, i32 channel_mapping_offset) +{ + if (channel_mapping_offset != -1) { + memory_copy(bp->channel_mapping, raw + channel_mapping_offset, sizeof(*bp->channel_mapping) * channel_count); + } else { + for EachIndex(channel_count, it) + bp->channel_mapping[it] = it; + } +} + +function void +beamformer_simple_parameters_equip_emission(BeamformerSimpleParameters *bp, u8 *raw, i32 emission_descriptors_offset) +{ + // NOTE/TODO(rnp): only considering the first data frame right now + ZBP_EmissionDescriptor *ed = (ZBP_EmissionDescriptor *)(raw + emission_descriptors_offset); + switch (ed->emission_kind) { + + case ZBP_EmissionKind_Sine:{ + ZBP_EmissionSineParameters *ep = (ZBP_EmissionSineParameters *)(raw + ed->parameters_offset); + bp->emission_parameters.kind = BeamformerEmissionKind_Sine; + bp->emission_parameters.sine.cycles = ep->cycles; + bp->emission_parameters.sine.frequency = ep->frequency; + }break; + + case ZBP_EmissionKind_Chirp:{ + ZBP_EmissionChirpParameters *ep = (ZBP_EmissionChirpParameters *)(raw + ed->parameters_offset); + bp->emission_parameters.kind = BeamformerEmissionKind_Chirp; + bp->emission_parameters.chirp.duration = ep->duration; + bp->emission_parameters.chirp.min_frequency = ep->min_frequency; + bp->emission_parameters.chirp.max_frequency = ep->max_frequency; + }break; + + InvalidDefaultCase; + static_assert(ZBP_EmissionKind_Count == (ZBP_EmissionKind_Chirp + 1), ""); + } +} + +function void +beamformer_simple_parameters_equip_acquisition(BeamformerSimpleParameters *bp, u8 *raw, + BeamformerAcquisitionKind acquisition_kind, + i32 acquisition_parameters_offset) +{ + switch (acquisition_kind) { + case ZBP_AcquisitionKind_FORCES:{ + ZBP_FORCESParameters *p = (ZBP_FORCESParameters *)(raw + acquisition_parameters_offset); + bp->transmit_receive_orientation = p->transmit_focus.transmit_receive_orientation; + bp->focal_vector.E[0] = p->transmit_focus.steering_angle; + bp->focal_vector.E[1] = p->transmit_focus.focal_depth; + + bp->single_focus = 1; + bp->single_orientation = 1; + }break; + + case ZBP_AcquisitionKind_HERCULES:{ + ZBP_HERCULESParameters *p = (ZBP_HERCULESParameters *)(raw + acquisition_parameters_offset); + bp->transmit_receive_orientation = p->transmit_focus.transmit_receive_orientation; + bp->focal_vector.E[0] = p->transmit_focus.steering_angle; + bp->focal_vector.E[1] = p->transmit_focus.focal_depth; + + bp->single_focus = 1; + bp->single_orientation = 1; + }break; + + case ZBP_AcquisitionKind_UFORCES:{ + ZBP_uFORCESParameters *p = (ZBP_uFORCESParameters *)(raw + acquisition_parameters_offset); + bp->transmit_receive_orientation = p->transmit_focus.transmit_receive_orientation; + bp->focal_vector.E[0] = p->transmit_focus.steering_angle; + bp->focal_vector.E[1] = p->transmit_focus.focal_depth; + + bp->single_focus = 1; + bp->single_orientation = 1; + + memory_copy(bp->sparse_elements, raw + p->sparse_elements_offset, + sizeof(*bp->sparse_elements) * bp->acquisition_count); + }break; + + case ZBP_AcquisitionKind_UHERCULES:{ + ZBP_uHERCULESParameters *p = (ZBP_uHERCULESParameters *)(raw + acquisition_parameters_offset); + bp->transmit_receive_orientation = p->transmit_focus.transmit_receive_orientation; + bp->focal_vector.E[0] = p->transmit_focus.steering_angle; + bp->focal_vector.E[1] = p->transmit_focus.focal_depth; + + bp->single_focus = 1; + bp->single_orientation = 1; + + memory_copy(bp->sparse_elements, raw + p->sparse_elements_offset, + sizeof(*bp->sparse_elements) * bp->acquisition_count); + }break; + + case ZBP_AcquisitionKind_RCA_TPW:{ + ZBP_TPWParameters *p = (ZBP_TPWParameters *)(raw + acquisition_parameters_offset); + + memory_copy(bp->transmit_receive_orientations, raw + p->transmit_receive_orientations_offset, + sizeof(*bp->transmit_receive_orientations) * bp->acquisition_count); + memory_copy(bp->steering_angles, raw + p->tilting_angles_offset, + sizeof(*bp->steering_angles) * bp->acquisition_count); + + for EachIndex(bp->acquisition_count, it) + bp->focal_depths[it] = inf32(); + }break; + + case ZBP_AcquisitionKind_RCA_VLS:{ + ZBP_VLSParameters *p = (ZBP_VLSParameters *)(raw + acquisition_parameters_offset); + + memory_copy(bp->transmit_receive_orientations, raw + p->transmit_receive_orientations_offset, + sizeof(*bp->transmit_receive_orientations) * bp->acquisition_count); + + f32 *focal_depths = (f32 *)(raw + p->focal_depths_offset); + f32 *origin_offsets = (f32 *)(raw + p->origin_offsets_offset); + + for EachIndex(bp->acquisition_count, it) { + f32 sign = Sign(focal_depths[it]); + f32 depth = focal_depths[it]; + f32 origin = origin_offsets[it]; + bp->steering_angles[it] = atan2_f32(origin, -depth) * 180.0f / PI; + bp->focal_depths[it] = sign * sqrt_f32(depth * depth + origin * origin); + } + }break; + + InvalidDefaultCase; + } +} + function b32 beamformer_simple_parameters_from_zbp_file(Arena *arena, BeamformerSimpleParameters *bp, char *path, ZBP_Data *raw_data, u32 *data_frame_count) { @@ -45,6 +169,11 @@ beamformer_simple_parameters_from_zbp_file(Arena *arena, BeamformerSimpleParamet if (raw.length < (i64)sizeof(ZBP_BaseHeader) || ((ZBP_BaseHeader *)raw.data)->magic != ZBP_HeaderMagic) return 0; + read_only BeamformerSamplingMode zbp_sampling_mode_to_beamformer[] = { + [ZBP_SamplingMode_Standard] = BeamformerSamplingMode_4X, + [ZBP_SamplingMode_Bandpass] = BeamformerSamplingMode_2X, + }; + switch (((ZBP_BaseHeader *)raw.data)->major) { case 1:{ @@ -119,33 +248,22 @@ beamformer_simple_parameters_from_zbp_file(Arena *arena, BeamformerSimpleParamet case 2:{ ZBP_HeaderV2 *header = (ZBP_HeaderV2 *)raw.data; - bp->sample_count = header->sample_count; bp->channel_count = header->channel_count; bp->acquisition_count = header->receive_event_count; - - read_only BeamformerSamplingMode zbp_sampling_mode_to_beamformer[] = { - [ZBP_SamplingMode_Standard] = BeamformerSamplingMode_4X, - [ZBP_SamplingMode_Bandpass] = BeamformerSamplingMode_2X, - }; - bp->sampling_mode = zbp_sampling_mode_to_beamformer[header->sampling_mode]; - + bp->sampling_mode = zbp_sampling_mode_to_beamformer[header->sampling_mode]; bp->acquisition_kind = (BeamformerAcquisitionKind)header->acquisition_mode; bp->decode_mode = (BeamformerDecodeMode)header->decode_mode; bp->sampling_frequency = header->sampling_frequency; bp->demodulation_frequency = header->demodulation_frequency; bp->speed_of_sound = header->speed_of_sound; bp->time_offset = header->time_offset; - bp->contrast_mode = (BeamformerContrastMode)header->contrast_mode; + bp->data_kind = (BeamformerDataKind)header->raw_data_kind; - if (header->channel_mapping_offset != -1) { - memory_copy(bp->channel_mapping, raw.data + header->channel_mapping_offset, - sizeof(*bp->channel_mapping) * bp->channel_count); - } else { - for EachIndex(bp->channel_count, it) - bp->channel_mapping[it] = it; - } + beamformer_simple_parameters_equip_channel_mapping(bp, raw.data, bp->channel_count, header->channel_mapping_offset); + beamformer_simple_parameters_equip_emission(bp, raw.data, header->emission_descriptors_offset); + beamformer_simple_parameters_equip_acquisition(bp, raw.data, bp->acquisition_kind, header->acquisition_parameters_offset); memory_copy(bp->xdc_transform.E, header->transducer_transform_matrix, sizeof(bp->xdc_transform)); memory_copy(bp->xdc_element_pitch.E, header->transducer_element_pitch, sizeof(bp->xdc_element_pitch)); @@ -154,7 +272,6 @@ beamformer_simple_parameters_from_zbp_file(Arena *arena, BeamformerSimpleParamet if (data_frame_count) *data_frame_count = header->raw_data_dimension[2]; //if (data_frame_count) *data_frame_count = header->raw_data_dimension[3]; - bp->data_kind = (BeamformerDataKind)header->raw_data_kind; raw_data->kind = header->raw_data_kind; raw_data->compression_kind = header->raw_data_compression_kind; @@ -169,97 +286,42 @@ beamformer_simple_parameters_from_zbp_file(Arena *arena, BeamformerSimpleParamet raw_data->bytes.length *= beamformer_data_kind_byte_size[header->raw_data_kind]; } } + }break; - // NOTE(rnp): only look at the first emission descriptor, other cases aren't currently relevant - { - ZBP_EmissionDescriptor *ed = (ZBP_EmissionDescriptor *)(raw.data + header->emission_descriptors_offset); - switch (ed->emission_kind) { - - case ZBP_EmissionKind_Sine:{ - ZBP_EmissionSineParameters *ep = (ZBP_EmissionSineParameters *)(raw.data + ed->parameters_offset); - bp->emission_parameters.kind = BeamformerEmissionKind_Sine; - bp->emission_parameters.sine.cycles = ep->cycles; - bp->emission_parameters.sine.frequency = ep->frequency; - }break; - - case ZBP_EmissionKind_Chirp:{ - ZBP_EmissionChirpParameters *ep = (ZBP_EmissionChirpParameters *)(raw.data + ed->parameters_offset); - bp->emission_parameters.kind = BeamformerEmissionKind_Chirp; - bp->emission_parameters.chirp.duration = ep->duration; - bp->emission_parameters.chirp.min_frequency = ep->min_frequency; - bp->emission_parameters.chirp.max_frequency = ep->max_frequency; - }break; - - InvalidDefaultCase; - static_assert(ZBP_EmissionKind_Count == (ZBP_EmissionKind_Chirp + 1), ""); - } - } - - switch (header->acquisition_mode) { - case ZBP_AcquisitionKind_FORCES:{}break; - - case ZBP_AcquisitionKind_HERCULES:{ - ZBP_HERCULESParameters *p = (ZBP_HERCULESParameters *)(raw.data + header->acquisition_parameters_offset); - bp->transmit_receive_orientation = p->transmit_focus.transmit_receive_orientation; - bp->focal_vector.E[0] = p->transmit_focus.steering_angle; - bp->focal_vector.E[1] = p->transmit_focus.focal_depth; - - bp->single_focus = 1; - bp->single_orientation = 1; - }break; - - case ZBP_AcquisitionKind_UFORCES:{ - ZBP_uFORCESParameters *p = (ZBP_uFORCESParameters *)(raw.data + header->acquisition_parameters_offset); - memory_copy(bp->sparse_elements, raw.data + p->sparse_elements_offset, - sizeof(*bp->sparse_elements) * bp->acquisition_count); - }break; - - case ZBP_AcquisitionKind_UHERCULES:{ - ZBP_uHERCULESParameters *p = (ZBP_uHERCULESParameters *)(raw.data + header->acquisition_parameters_offset); - bp->transmit_receive_orientation = p->transmit_focus.transmit_receive_orientation; - bp->focal_vector.E[0] = p->transmit_focus.steering_angle; - bp->focal_vector.E[1] = p->transmit_focus.focal_depth; - - bp->single_focus = 1; - bp->single_orientation = 1; - - memory_copy(bp->sparse_elements, raw.data + p->sparse_elements_offset, - sizeof(*bp->sparse_elements) * bp->acquisition_count); - }break; - - case ZBP_AcquisitionKind_RCA_TPW:{ - ZBP_TPWParameters *p = (ZBP_TPWParameters *)(raw.data + header->acquisition_parameters_offset); - - memory_copy(bp->transmit_receive_orientations, raw.data + p->transmit_receive_orientations_offset, - sizeof(*bp->transmit_receive_orientations) * bp->acquisition_count); - memory_copy(bp->steering_angles, raw.data + p->tilting_angles_offset, - sizeof(*bp->steering_angles) * bp->acquisition_count); - - for EachIndex(bp->acquisition_count, it) - bp->focal_depths[it] = inf32(); - }break; + case 3:{ + ZBP_HeaderV3 *header = (ZBP_HeaderV3 *)raw.data; + bp->sample_count = header->sample_count; + bp->channel_count = header->channel_count; + bp->acquisition_count = header->receive_event_count; + bp->sampling_mode = zbp_sampling_mode_to_beamformer[header->sampling_mode]; + bp->acquisition_kind = (BeamformerAcquisitionKind)header->acquisition_mode; + bp->decode_mode = (BeamformerDecodeMode)header->decode_mode; + bp->sampling_frequency = header->sampling_frequency; + bp->speed_of_sound = header->speed_of_sound; + bp->contrast_mode = (BeamformerContrastMode)header->contrast_mode; + bp->data_kind = (BeamformerDataKind)header->raw_data_kind; - case ZBP_AcquisitionKind_RCA_VLS:{ - ZBP_VLSParameters *p = (ZBP_VLSParameters *)(raw.data + header->acquisition_parameters_offset); + memory_copy(bp->xdc_element_pitch.E, header->transducer_element_pitch, sizeof(bp->xdc_element_pitch)); + memory_copy(bp->raw_data_dimensions.E, header->raw_data_dimension, sizeof(bp->raw_data_dimensions)); + if (data_frame_count) *data_frame_count = header->raw_data_dimension[2]; - memory_copy(bp->transmit_receive_orientations, raw.data + p->transmit_receive_orientations_offset, - sizeof(*bp->transmit_receive_orientations) * bp->acquisition_count); + beamformer_simple_parameters_equip_channel_mapping(bp, raw.data, bp->channel_count, header->channel_mapping_offset); + beamformer_simple_parameters_equip_emission(bp, raw.data, header->emission_descriptors_offset); + beamformer_simple_parameters_equip_acquisition(bp, raw.data, bp->acquisition_kind, header->acquisition_parameters_offset); - f32 *focal_depths = (f32 *)(raw.data + p->focal_depths_offset); - f32 *origin_offsets = (f32 *)(raw.data + p->origin_offsets_offset); + // TODO(rnp): for now we only consider first data frame + memory_copy(&bp->time_offset, raw.data + header->time_delays_offset, sizeof(f32)); + memory_copy(&bp->demodulation_frequency , raw.data + header->demodulation_frequencies_offset, sizeof(f32)); - for EachIndex(bp->acquisition_count, it) { - f32 sign = Sign(focal_depths[it]); - f32 depth = focal_depths[it]; - f32 origin = origin_offsets[it]; - bp->steering_angles[it] = atan2_f32(origin, -depth) * 180.0f / PI; - bp->focal_depths[it] = sign * sqrt_f32(depth * depth + origin * origin); - } - }break; + // TODO(rnp): for now we only consider first tile + memory_copy(&bp->xdc_transform, raw.data + header->transducer_transforms_offset, sizeof(m4)); - InvalidDefaultCase; + raw_data->kind = header->raw_data_kind; + raw_data->compression_kind = header->raw_data_compression_kind; + if (header->raw_data_offset != -1) { + raw_data->bytes.length = header->raw_data_size; + raw_data->bytes.data = raw.data + header->raw_data_offset; } - }break; default:{return 0;}break;