|
| 1 | +/* See LICENSE for license details. */ |
| 2 | + |
| 3 | +#define LIB_FN function |
| 4 | +#include "ogl_beamformer_lib.c" |
| 5 | + |
| 6 | +typedef enum { |
| 7 | + #define X(type, id, ...) DASShaderKind_##type = id, |
| 8 | + DAS_SHADER_KIND_LIST |
| 9 | + #undef X |
| 10 | + DASShaderKind_Count |
| 11 | +} DASShaderKind; |
| 12 | + |
| 13 | +#include <stdarg.h> |
| 14 | +#include <stdio.h> |
| 15 | +#include <stdlib.h> |
| 16 | +#include <zstd.h> |
| 17 | + |
| 18 | +global i32 g_output_points[4] = {512, 1, 1024, 1}; |
| 19 | +global v2 g_axial_extent = {{ 5e-3f, 55e-3f}}; |
| 20 | +global v2 g_lateral_extent = {{-27e-3f, 27e-3f}}; |
| 21 | +global f32 g_f_number = 0.5f; |
| 22 | + |
| 23 | +#define ZEMP_BP_MAGIC (uint64_t)0x5042504D455AFECAull |
| 24 | +typedef struct { |
| 25 | + u64 magic; |
| 26 | + u32 version; |
| 27 | + u16 decode_mode; |
| 28 | + u16 beamform_mode; |
| 29 | + u32 raw_data_dim[4]; |
| 30 | + u32 decoded_data_dim[4]; |
| 31 | + f32 xdc_element_pitch[2]; |
| 32 | + f32 xdc_transform[16]; /* NOTE: column major order */ |
| 33 | + i16 channel_mapping[256]; |
| 34 | + f32 transmit_angles[256]; |
| 35 | + f32 focal_depths[256]; |
| 36 | + i16 sparse_elements[256]; |
| 37 | + i16 hadamard_rows[256]; |
| 38 | + f32 speed_of_sound; |
| 39 | + f32 center_frequency; |
| 40 | + f32 sampling_frequency; |
| 41 | + f32 time_offset; |
| 42 | + i32 transmit_mode; |
| 43 | +} zemp_bp_v1; |
| 44 | + |
| 45 | +#define die(...) die_((char *)__func__, __VA_ARGS__) |
| 46 | +function no_return void |
| 47 | +die_(char *function_name, char *format, ...) |
| 48 | +{ |
| 49 | + if (function_name) |
| 50 | + fprintf(stderr, "%s: ", function_name); |
| 51 | + |
| 52 | + va_list ap; |
| 53 | + |
| 54 | + va_start(ap, format); |
| 55 | + vfprintf(stderr, format, ap); |
| 56 | + va_end(ap); |
| 57 | + |
| 58 | + os_exit(1); |
| 59 | +} |
| 60 | + |
| 61 | +#if OS_LINUX |
| 62 | + |
| 63 | +#include <fcntl.h> |
| 64 | +#include <sys/stat.h> |
| 65 | +#include <unistd.h> |
| 66 | + |
| 67 | +function void os_init_timer(void) { } |
| 68 | + |
| 69 | +function f64 |
| 70 | +os_get_time(void) |
| 71 | +{ |
| 72 | + f64 result = (f64)os_get_timer_counter() / (f64)os_get_timer_frequency(); |
| 73 | + return result; |
| 74 | +} |
| 75 | + |
| 76 | +function s8 |
| 77 | +os_read_file_simp(char *fname) |
| 78 | +{ |
| 79 | + s8 result; |
| 80 | + i32 fd = open(fname, O_RDONLY); |
| 81 | + if (fd < 0) |
| 82 | + die("couldn't open file: %s\n", fname); |
| 83 | + |
| 84 | + struct stat st; |
| 85 | + if (stat(fname, &st) < 0) |
| 86 | + die("couldn't stat file\n"); |
| 87 | + |
| 88 | + result.len = st.st_size; |
| 89 | + result.data = malloc((uz)st.st_size); |
| 90 | + if (!result.data) |
| 91 | + die("couldn't alloc space for reading\n"); |
| 92 | + |
| 93 | + iz rlen = read(fd, result.data, (u32)st.st_size); |
| 94 | + close(fd); |
| 95 | + |
| 96 | + if (rlen != st.st_size) |
| 97 | + die("couldn't read file: %s\n", fname); |
| 98 | + |
| 99 | + return result; |
| 100 | +} |
| 101 | + |
| 102 | +#elif OS_WINDOWS |
| 103 | + |
| 104 | +global w32_context os_context; |
| 105 | + |
| 106 | +function void |
| 107 | +os_init_timer(void) |
| 108 | +{ |
| 109 | + os_context.timer_frequency = os_get_timer_frequency(); |
| 110 | +} |
| 111 | + |
| 112 | +function f64 |
| 113 | +os_get_time(void) |
| 114 | +{ |
| 115 | + f64 result = (f64)os_get_timer_counter() / (f64)os_context.timer_frequency; |
| 116 | + return result; |
| 117 | +} |
| 118 | + |
| 119 | +function s8 |
| 120 | +os_read_file_simp(char *fname) |
| 121 | +{ |
| 122 | + s8 result; |
| 123 | + iptr h = CreateFileA(fname, GENERIC_READ, 0, 0, OPEN_EXISTING, 0, 0); |
| 124 | + if (h == INVALID_FILE) |
| 125 | + die("couldn't open file: %s\n", fname); |
| 126 | + |
| 127 | + w32_file_info fileinfo; |
| 128 | + if (!GetFileInformationByHandle(h, &fileinfo)) |
| 129 | + die("couldn't get file info\n", stderr); |
| 130 | + |
| 131 | + result.len = fileinfo.nFileSizeLow; |
| 132 | + result.data = malloc(fileinfo.nFileSizeLow); |
| 133 | + if (!result.data) |
| 134 | + die("couldn't alloc space for reading\n"); |
| 135 | + |
| 136 | + i32 rlen = 0; |
| 137 | + if (!ReadFile(h, result.data, (i32)fileinfo.nFileSizeLow, &rlen, 0) && rlen != (i32)fileinfo.nFileSizeLow) |
| 138 | + die("couldn't read file: %s\n", fname); |
| 139 | + CloseHandle(h); |
| 140 | + |
| 141 | + return result; |
| 142 | +} |
| 143 | + |
| 144 | +#else |
| 145 | +#error Unsupported Platform |
| 146 | +#endif |
| 147 | + |
| 148 | +function void |
| 149 | +stream_ensure_termination(Stream *s, u8 byte) |
| 150 | +{ |
| 151 | + b32 found = 0; |
| 152 | + if (!s->errors && s->widx > 0) |
| 153 | + found = s->data[s->widx - 1] == byte; |
| 154 | + if (!found) { |
| 155 | + s->errors |= s->cap - 1 < s->widx; |
| 156 | + if (!s->errors) |
| 157 | + s->data[s->widx++] = byte; |
| 158 | + } |
| 159 | +} |
| 160 | + |
| 161 | +function void * |
| 162 | +decompress_zstd_data(s8 raw) |
| 163 | +{ |
| 164 | + uz requested_size = ZSTD_getFrameContentSize(raw.data, (uz)raw.len); |
| 165 | + void *out = malloc(requested_size); |
| 166 | + if (out) { |
| 167 | + uz decompressed = ZSTD_decompress(out, requested_size, raw.data, (uz)raw.len); |
| 168 | + if (decompressed != requested_size) { |
| 169 | + free(out); |
| 170 | + out = 0; |
| 171 | + } |
| 172 | + } |
| 173 | + return out; |
| 174 | +} |
| 175 | + |
| 176 | +function zemp_bp_v1 * |
| 177 | +read_zemp_bp_v1(u8 *path) |
| 178 | +{ |
| 179 | + s8 raw = os_read_file_simp((char *)path); |
| 180 | + zemp_bp_v1 *result = 0; |
| 181 | + if (raw.len == sizeof(zemp_bp_v1) && *(u64 *)raw.data == ZEMP_BP_MAGIC) { |
| 182 | + if (((zemp_bp_v1 *)raw.data)->version == 1) |
| 183 | + result = (zemp_bp_v1 *)raw.data; |
| 184 | + } |
| 185 | + return result; |
| 186 | +} |
| 187 | + |
| 188 | +function void |
| 189 | +beamformer_parameters_from_zemp_bp_v1(BeamformerParameters *out, zemp_bp_v1 *zbp) |
| 190 | +{ |
| 191 | + mem_copy(out->xdc_transform, zbp->xdc_transform, sizeof(out->xdc_transform)); |
| 192 | + mem_copy(out->xdc_element_pitch, zbp->xdc_element_pitch, sizeof(out->xdc_element_pitch)); |
| 193 | + mem_copy(out->raw_data_dimensions, zbp->raw_data_dim, sizeof(out->raw_data_dimensions)); |
| 194 | + |
| 195 | + out->sample_count = zbp->decoded_data_dim[0]; |
| 196 | + out->channel_count = zbp->decoded_data_dim[1]; |
| 197 | + out->acquisition_count = zbp->decoded_data_dim[2]; |
| 198 | + out->transmit_mode = (u8)((zbp->transmit_mode & 2) >> 1); |
| 199 | + out->receive_mode = (u8)((zbp->transmit_mode & 1) >> 0); |
| 200 | + out->decode = (u8)zbp->decode_mode; |
| 201 | + out->das_shader_id = zbp->beamform_mode; |
| 202 | + out->time_offset = zbp->time_offset; |
| 203 | + out->sampling_frequency = zbp->sampling_frequency; |
| 204 | + out->demodulation_frequency = zbp->center_frequency; |
| 205 | + out->speed_of_sound = zbp->speed_of_sound; |
| 206 | +} |
| 207 | + |
| 208 | +#define shift_n(v, c, n) v += n, c -= n |
| 209 | +#define shift(v, c) shift_n(v, c, 1) |
| 210 | + |
| 211 | +function void |
| 212 | +usage(char *argv0) |
| 213 | +{ |
| 214 | + die("%s base_path\n" |
| 215 | + , argv0); |
| 216 | +} |
| 217 | + |
| 218 | +function i16 * |
| 219 | +decompress_data_at_work_index(Stream *path_base, u32 index) |
| 220 | +{ |
| 221 | + stream_append_byte(path_base, '_'); |
| 222 | + stream_append_u64_width(path_base, index, 2); |
| 223 | + stream_append_s8(path_base, s8(".zst")); |
| 224 | + stream_ensure_termination(path_base, 0); |
| 225 | + |
| 226 | + s8 compressed_data = os_read_file_simp((char *)path_base->data); |
| 227 | + i16 *result = decompress_zstd_data(compressed_data); |
| 228 | + if (!result) |
| 229 | + die("failed to decompress data: %s\n", path_base->data); |
| 230 | + free(compressed_data.data); |
| 231 | + |
| 232 | + return result; |
| 233 | +} |
| 234 | + |
| 235 | +function b32 |
| 236 | +send_frame_at(i16 *restrict i16_data, BeamformerParameters *restrict bp, BeamformerViewPlaneTag tag, u8 block) |
| 237 | +{ |
| 238 | + u32 data_size = bp->raw_data_dimensions[0] * bp->raw_data_dimensions[1] * sizeof(i16); |
| 239 | + b32 result = beamformer_push_data_with_compute(i16_data, data_size, tag, block); |
| 240 | + if (!result) printf("lib error: %s\n", beamformer_get_last_error_string()); |
| 241 | + |
| 242 | + return result; |
| 243 | +} |
| 244 | + |
| 245 | +function void |
| 246 | +execute_study_with_block(s8 study, Stream path, f32 chirp_length, BeamformerViewPlaneTag tag, u8 block) |
| 247 | +{ |
| 248 | + fprintf(stderr, "showing: %.*s\n", (i32)study.len, study.data); |
| 249 | + |
| 250 | + stream_append_s8(&path, study); |
| 251 | + stream_ensure_termination(&path, OS_PATH_SEPARATOR_CHAR); |
| 252 | + stream_append_s8(&path, study); |
| 253 | + i32 path_work_index = path.widx; |
| 254 | + |
| 255 | + stream_append_s8(&path, s8(".bp")); |
| 256 | + stream_ensure_termination(&path, 0); |
| 257 | + |
| 258 | + zemp_bp_v1 *zbp = read_zemp_bp_v1(path.data); |
| 259 | + if (!zbp) die("failed to unpack parameters file\n"); |
| 260 | + |
| 261 | + BeamformerParameters bp = {0}; |
| 262 | + beamformer_parameters_from_zemp_bp_v1(&bp, zbp); |
| 263 | + |
| 264 | + mem_copy(bp.output_points, g_output_points, sizeof(bp.output_points)); |
| 265 | + bp.output_points[3] = 1; |
| 266 | + |
| 267 | + bp.output_min_coordinate[0] = g_lateral_extent.x; |
| 268 | + bp.output_min_coordinate[1] = 0; |
| 269 | + bp.output_min_coordinate[2] = g_axial_extent.x; |
| 270 | + |
| 271 | + bp.output_max_coordinate[0] = g_lateral_extent.y; |
| 272 | + bp.output_max_coordinate[1] = 0; |
| 273 | + bp.output_max_coordinate[2] = g_axial_extent.y; |
| 274 | + |
| 275 | + bp.f_number = g_f_number; |
| 276 | + bp.beamform_plane = tag; |
| 277 | + bp.interpolate = 1; |
| 278 | + |
| 279 | + bp.decimation_rate = 1; |
| 280 | + |
| 281 | + bp.demodulation_frequency = bp.sampling_frequency / 4; |
| 282 | + bp.sampling_mode = BeamformerSamplingMode_4X; |
| 283 | + |
| 284 | + BeamformerFilterParameters kaiser = {0}; |
| 285 | + kaiser.Kaiser.beta = 5.65f; |
| 286 | + kaiser.Kaiser.cutoff_frequency = 2.0e6f; |
| 287 | + kaiser.Kaiser.length = 36; |
| 288 | + |
| 289 | + beamformer_create_filter(BeamformerFilterKind_Kaiser, (f32 *)&kaiser, sizeof(kaiser.Kaiser) / sizeof(f32), |
| 290 | + bp.sampling_frequency / 2, 0, 0, block); |
| 291 | + beamformer_set_pipeline_stage_parameters_at(0, 0, block); |
| 292 | + |
| 293 | + #if 0 |
| 294 | + BeamformerFilterParameters matched = {0}; |
| 295 | + typeof(matched.MatchedChirp) *mp = &matched.MatchedChirp; |
| 296 | + mp->duration = chirp_length; |
| 297 | + mp->min_frequency = 2.9e6 - zbp->demodulation_frequency; |
| 298 | + mp->max_frequency = 6.0e6 - zbp->demodulation_frequency; |
| 299 | + |
| 300 | + beamformer_create_filter(BeamformerFilterKind_MatchedChirp, (f32 *)mp, |
| 301 | + sizeof(matched.MatchedChirp) / sizeof(f32), |
| 302 | + bp.sampling_frequency / 2, 1, 0, block); |
| 303 | + beamformer_set_pipeline_stage_parameters_at(0, 0, block); |
| 304 | + #endif |
| 305 | + |
| 306 | + if (zbp->beamform_mode == DASShaderKind_UHERCULES || zbp->beamform_mode == DASShaderKind_UFORCES) |
| 307 | + beamformer_push_sparse_elements_at(zbp->sparse_elements, bp.acquisition_count, block); |
| 308 | + |
| 309 | + { |
| 310 | + alignas(64) v2 focal_vectors[countof(zbp->focal_depths)]; |
| 311 | + for (u32 i = 0; i < countof(zbp->focal_depths); i++) |
| 312 | + focal_vectors[i] = (v2){{zbp->transmit_angles[i], zbp->focal_depths[i]}}; |
| 313 | + beamformer_push_focal_vectors_at((f32 *)focal_vectors, countof(focal_vectors), block); |
| 314 | + } |
| 315 | + |
| 316 | + beamformer_push_channel_mapping_at(zbp->channel_mapping, countof(zbp->channel_mapping), block); |
| 317 | + beamformer_push_parameters_at(&bp, block); |
| 318 | + |
| 319 | + i32 shader_stages[16]; |
| 320 | + u32 shader_stage_count = 0; |
| 321 | + shader_stages[shader_stage_count++] = BeamformerShaderKind_Demodulate; |
| 322 | + shader_stages[shader_stage_count++] = BeamformerShaderKind_Decode; |
| 323 | + shader_stages[shader_stage_count++] = BeamformerShaderKind_DAS; |
| 324 | + |
| 325 | + beamformer_push_pipeline_at(shader_stages, shader_stage_count, BeamformerDataKind_Int16, block); |
| 326 | + |
| 327 | + stream_reset(&path, path_work_index); |
| 328 | + i16 *data = decompress_data_at_work_index(&path, 0); |
| 329 | + |
| 330 | + send_frame_at(data, &bp, tag, block); |
| 331 | + |
| 332 | + free(zbp); |
| 333 | + free(data); |
| 334 | +} |
| 335 | + |
| 336 | +extern i32 |
| 337 | +main(i32 argc, char *argv[]) |
| 338 | +{ |
| 339 | + if (argc != 2) usage(argv[0]); |
| 340 | + |
| 341 | + os_init_timer(); |
| 342 | + |
| 343 | + if (!beamformer_set_global_timeout(1000)) die((char *)beamformer_get_last_error_string()); |
| 344 | + if (!beamformer_reserve_parameter_blocks(2)) die((char *)beamformer_get_last_error_string()); |
| 345 | + |
| 346 | + Arena arena = os_alloc_arena(KB(8)); |
| 347 | + Stream path = arena_stream(arena); |
| 348 | + stream_append_s8(&path, c_str_to_s8(argv[1])); |
| 349 | + stream_ensure_termination(&path, OS_PATH_SEPARATOR_CHAR); |
| 350 | + |
| 351 | + execute_study_with_block(s8("250319_A06_7.80MHz_ATS539_Cyst_FORCES-TxRow"), path, 0, BeamformerViewPlaneTag_YZ, 0); |
| 352 | + execute_study_with_block(s8("250319_A06_7.80MHz_ATS539_Cyst_FORCES-TxColumn"), path, 0, BeamformerViewPlaneTag_XZ, 1); |
| 353 | + fprintf(stderr, "press enter to continue..."); |
| 354 | + if (fgetc(stdin) == EOF) return 0; |
| 355 | + |
| 356 | + execute_study_with_block(s8("250319_A06_7.80MHz_ATS539_Cyst_TPW-128-TxColumn"), path, 0, BeamformerViewPlaneTag_YZ, 0); |
| 357 | + execute_study_with_block(s8("250319_A06_7.80MHz_ATS539_Cyst_TPW-128-TxColumn"), path, 0, BeamformerViewPlaneTag_XZ, 1); |
| 358 | + fprintf(stderr, "press enter to continue..."); |
| 359 | + if (fgetc(stdin) == EOF) return 0; |
| 360 | + |
| 361 | + execute_study_with_block(s8("250319_A06_7.80MHz_ATS539_Cyst_VLS-128-TxColumn"), path, 0, BeamformerViewPlaneTag_YZ, 0); |
| 362 | + execute_study_with_block(s8("250319_A06_7.80MHz_ATS539_Cyst_VLS-128-TxColumn"), path, 0, BeamformerViewPlaneTag_XZ, 1); |
| 363 | + |
| 364 | + return 0; |
| 365 | +} |
0 commit comments