Feat/acoustics - #42
Conversation
1 FDCAN 1 SPI master 3 SPI slaves 1 Usart
allow cli make clean all build project used to generate compile_json
remove all incompatible *f16.c from CMSIS.DSP remove all incompatible ARM cortex A (neon) from CMSIS_DSP remove all source files that include all source files in the same directory
First iteration, made completely by chatgtp
forrisdahl
left a comment
There was a problem hiding this comment.
I do not know why, but the GitHub browser is insanely slow and difficult to mark where I want to comment, so I will make one larger comment instead.
My general impression of main is that it is big. It has a lot of functions, and it is not immediately clear which are important/used and which are not. I suggested splitting the debug or printf functions into another file. I understand that this might be challenging due to variable scope. The most important thing to see in the main.c file is the main function. I would like the main function to be as early as possible, with other functions defined below it but declared in the header or before main.
I think the code inside the while loop could also be split into functions. It does not have to be many functions, but as it is now, we end up with a lot of indentation. Function names could also make it easier to understand what each step is trying to achieve.
It is a bit random where there are comments and where there are not. For the most important functions, you should have Doxygen-style comments to explain what they do and what they expect.
//NOT TESTED AT TIME OF COMMIT// cleaned and modularized everything all that remains should be to implement dumping functionality and test Features added: CAN interface Proper state machine Stale Data detection
Left to implement:
Dump commands
Making it not error due to stale data every 10 seconds
|
Idk what i'm doing. |
…SPI interface would fail. Fixed it. Acoustics is now megagood. Dump still not implemented
|
Fixed the erroring out thingy now. |
| while(((idxs[0] < dead_space) || (idxs[0] > (WORKSPACE_LEN-(WORKSPACE_OFFSET-1)*BLOCK_LEN))) && max_retries--){ | ||
| float32_t threshold = dsp_min_max_lerp(processing_workspace[0], WORKSPACE_LEN, linear_threshold, n_upper_average, dead_space); | ||
| idxs[0] = dsp_rl_under_threshold_search(processing_workspace[0],WORKSPACE_LEN, threshold , PROCESSING_PATIENCE); | ||
| linear_threshold *= 2; | ||
| } | ||
| times_of_arrival[0] = (float32_t)idxs[0]; | ||
|
|
||
| for(int i = 1; i < N_HYDROPHONES; i++){ | ||
| float32_t linear_threshold = lerp_threshold; | ||
| uint8_t max_retries = 6; | ||
| uint32_t dead_space = n_lower_average; | ||
| idxs[i] = 0; | ||
| while(((utils_abs_int32(idxs[0] - idxs[i])) > max_idx_difference) && max_retries--){ | ||
| float32_t threshold = dsp_min_max_lerp(processing_workspace[i], WORKSPACE_LEN, linear_threshold, n_upper_average, dead_space); | ||
| idxs[i] = dsp_rl_under_threshold_search(processing_workspace[i],WORKSPACE_LEN, threshold , PROCESSING_PATIENCE); | ||
| linear_threshold *= 2; | ||
| } | ||
| times_of_arrival[i] = (float32_t)idxs[i]; | ||
| } | ||
|
|
||
| valid_buffers = 0; |
There was a problem hiding this comment.
Some of these conditions are quite cryptic, not immediately obvious what they are checking. You could have a static inline function to have a human readable name for the conditions.
You probably should split the first part of the function, (the searching?) to its own function. Currently function does a lot of things. Could have one function that finds the TDOA and validates the data and one that solves the position
|
Aight now there are surely no other problems left |
used outside of acoustics.c
|
This looks a lot better, much easier to understand the acoustics processing. What remains now is to clean up your branch. First of all I see that there are telemetry files I accidentally pushed on the branch that needs to be removed. I think the old version of the firmware can be removed to save space. Both the old version and the new version includes CMSIS library which adds a lot more files to your branch than needs to be. In addition since I do not think we have done any changes cmsis library we should look for a way to avoid having it in our repo. |
Most importantly i found and removed the test data that was still in the repo (i think)
forrisdahl
left a comment
There was a problem hiding this comment.
Hope your summer is going well so far! Here are some suggestions for improvement
| float32_t find_idx_of_arrival_in_buffer(uint8_t buffer_idx){ | ||
| float32_t linear_threshold = lerp_threshold; | ||
| uint8_t max_retries = find_idx_of_arrival_max_retries; | ||
| idxs[buffer_idx] = 0; | ||
|
|
||
| while(is_idx_out_of_bounds(buffer_idx) && max_retries--){ | ||
| float32_t threshold = dsp_min_max_lerp(processing_workspace[buffer_idx], WORKSPACE_LEN, linear_threshold, n_upper_average, dead_space); | ||
| idxs[buffer_idx] = dsp_rl_under_threshold_search(processing_workspace[buffer_idx],WORKSPACE_LEN, threshold , PROCESSING_PATIENCE); | ||
| linear_threshold *= 2; | ||
| } | ||
| return idxs[buffer_idx]; | ||
| } | ||
|
|
||
| bool are_idxs_of_arrival_valid(uint8_t valid_buffers_array[N_HYDROPHONES]){ | ||
| uint8_t valid_buffers = 0; | ||
|
|
||
| for(int i = 1; i < N_HYDROPHONES; i++){ | ||
| bool valid = !regular_idx_out_of_bounds(idxs[i]); | ||
| valid_buffers += valid; | ||
| valid_buffers_array[i] &= valid; | ||
| } | ||
|
|
||
| return valid_buffers >= (MINIMUM_VALID_BUFFERS - 1); | ||
| } |
There was a problem hiding this comment.
find_idx_of_arrival_in_buffer only process one index whereas are_idxs_of_arrival_valid processes all indexes. I think both functions should either process all indexes or process one index
| for (uint32_t i = 1; i < signal_len; i++) | ||
| { | ||
| float32_t key = sorted[i]; | ||
| int32_t j = (int32_t)i - 1; | ||
| while (j >= 0 && sorted[j] > key) | ||
| { | ||
| sorted[j + 1] = sorted[j]; | ||
| j--; | ||
| } | ||
| sorted[j + 1] = key; | ||
| } |
There was a problem hiding this comment.
Could make this a static inline function, would express intent clearer and reduce the length of this function
| arm_add_f32( /* low + t*(high-low) */ | ||
| &low_mean, /* not a vector call, so we */ | ||
| &(float32_t){t * /* use scalar arithmetic below */ | ||
| (high_mean - low_mean)}, | ||
| &result, 1); |
There was a problem hiding this comment.
This is quite confusing. Would instead of doing this inside the function call you instead
&(float32_t){t * (high_mean - low_mean)}float32_t temp = t * (high_mean - low_mean)
arm_add_f32(&low_mean, &temp, &result, 1)You should give it a better name than temp, but I was too lazy to come up with something clever
| static bool is_vector_too_long(float32_t vec[3]){ | ||
| float32_t sum = 0.0f; | ||
| for(int i = 0; i < 3; i++) sum += utils_abs_f32(vec[i]); | ||
| return sum > 1.732050807569f; |
There was a problem hiding this comment.
Very specific number, probably should use a define here
| void hydrophone_interface_init(float32_t new_hydrophone_positions[N_HYDROPHONES][3]){ | ||
|
|
||
| for(int i = 0; i < N_HYDROPHONES; i++){ | ||
| hydrophone_valid[i] = false; | ||
| hydrophone_positions[i][0] = new_hydrophone_positions[i][0]; | ||
| hydrophone_positions[i][1] = new_hydrophone_positions[i][1]; | ||
| hydrophone_positions[i][2] = new_hydrophone_positions[i][2]; | ||
| } | ||
|
|
||
| float32_t biggest_distance = 0; | ||
| for(int i = 1; i < N_HYDROPHONES; i++){ | ||
| float32_t dist = utils_distance_3d(hydrophone_positions[0],hydrophone_positions[i]); | ||
| if(dist > biggest_distance){ | ||
| biggest_distance = dist; | ||
| } | ||
| } | ||
| { | ||
| float32_t idx_distance = (biggest_distance/(WAVE_SPEED)*SAMPLING_FREQUENCY) + BLOCK_LEN/4; | ||
| uint16_t n = (uint16_t)ceilf(idx_distance); | ||
| n--; | ||
| n |= n >> 1; | ||
| n |= n >> 2; | ||
| n |= n >> 4; | ||
| n |= n >> 8; | ||
| n |= n >> 16; | ||
| n++; | ||
| max_idx_difference = n; | ||
| } | ||
|
|
||
|
|
||
| struct ad7606_pins pins = { | ||
| .cs = {CS}, | ||
| .busy = {BUSY}, | ||
| .frstdata = {FRSTDATA}, | ||
| .convst = {CONVST}, | ||
| }; | ||
|
|
||
| union ad7606_spi spi = { | ||
| .by_name = { | ||
| .douta = DOUTA, | ||
| .doutb = DOUTB, | ||
| .doutc = DOUTC, | ||
| .doutd = DOUTD, | ||
| .doute = DOUTE, | ||
| .doutf = NULL, | ||
| .doutg = NULL, | ||
| .douth = DOUTH, | ||
| .sdi = MASTER_SPI, | ||
| } | ||
| }; | ||
|
|
||
| struct ad7606_config config = { | ||
| .status_header = false, | ||
| .external_oversampling_clock = false, | ||
| .dout_format = AD7606_DOUT_8, | ||
| .operation_mode = AD7606_OPERATION_NORMAL, | ||
| }; | ||
|
|
||
| struct ad7606_channel channels[8]; | ||
| for(int i = 0; i < 8; i++){ | ||
| // AD7606_MUX_CTRL_TEMP, | ||
| // AD7606_MUX_CTRL_2V5_REF, | ||
| // AD7606_MUX_CTRL_1V8_ALDO, | ||
| // AD7606_MUX_CTRL_1V8_DLDO, | ||
| // AD7606_MUX_CTRL_V_DRIVE, | ||
| // AD7606_MUX_CTRL_A_GND, | ||
| // AD7606_MUX_CTRL_AV_CC; | ||
| AD7606_CHANNEL_MUX_CTRL mux_ctrl = AD7606_MUX_CTRL_A_IN; // = (i != 8) ? (AD7606_MUX_CTRL_A_IN) : (AD7606_MUX_CTRL_TEMP); | ||
| AD7606_CHANNEL_RANGE range = AD7606_RANGE_SE_PM_12_5V; | ||
| switch(i){ | ||
| case(2): | ||
| range = AD7606_RANGE_SE_PM_2_5V; | ||
| break; | ||
| case(5): | ||
| mux_ctrl = AD7606_MUX_CTRL_A_GND; | ||
| range = AD7606_RANGE_SE_PM_2_5V; | ||
| break; | ||
| case(6): | ||
| mux_ctrl = AD7606_MUX_CTRL_AV_CC; | ||
| range = AD7606_RANGE_SE_0_TO_5V; | ||
| break; | ||
| case(7): | ||
| mux_ctrl = AD7606_MUX_CTRL_TEMP; | ||
| range = AD7606_RANGE_SE_PM_2_5V; | ||
| break; | ||
| } | ||
| struct ad7606_channel ch = { | ||
| .open_detect = false, | ||
| .high_bandwidth = true, | ||
| .range = range, | ||
| .gain = 0, | ||
| .phase = 0, | ||
| .offset = 0x80, | ||
| .mux_ctrl = mux_ctrl, | ||
| }; | ||
| channels[i] = ch; | ||
| } | ||
|
|
||
| struct ad7606_oversampling oversampling = { | ||
| .oversampling_ratio = 3, // 2^N oversampling | ||
| .oversampling_padding = 0, | ||
| }; | ||
|
|
||
| struct ad7606_digital_diagnostics digital_diagnostics = { | ||
| .rom_CRC_err_en = true, | ||
| .mm_CRC_err_en = false, | ||
| .int_CRC_err_en = false, | ||
| .spi_write_err_en = false, | ||
| .spi_read_err_en = false, | ||
| .busy_stuck_high_err_en = true, | ||
| .clk_fs_os_en = false, | ||
| .interface_check_en = false, | ||
| }; | ||
|
|
||
| ADC_settings.config = config; | ||
| ADC_settings.digital_diagnostics = digital_diagnostics; | ||
| ADC_settings.oversampling = oversampling; | ||
|
|
||
| for(int i = 0; i < 8; i++){ | ||
| ADC_settings.channels[i] = channels[i]; | ||
| } | ||
| my_ADC.cooked = true; | ||
| ad7606_init(&my_ADC, &ADC_regs, pins, spi, &ADC_settings, &diagnostics_sample); | ||
| // if(verbose){ | ||
| // printf("ADC initialized. Status register:\t"); | ||
| // print_binary(ad7606_check_status(&my_ADC),8); | ||
| // printf("\r\n"); | ||
| // | ||
| // printf("Digital diagnostics error register:\t"); | ||
| // print_binary(ad7606_check_digital_error(&my_ADC),8); | ||
| // printf("\r\n"); | ||
| // } | ||
|
|
||
| uint8_t interface_check_result[8]; | ||
| ad7606_check_interface(&my_ADC, interface_check_result); | ||
| if(!hydrophone_valid[0]){ | ||
| for(int i = 0; i < N_HYDROPHONES; i++){ | ||
| hydrophone_valid[i] = interface_check_result[i]; | ||
| } | ||
| } | ||
| // if(verbose){ | ||
| // printf("Interface check result:\r\n"); | ||
| // for(int i = 0; i < 8; i++){ | ||
| // printf("Channel V%d: ",i+1); | ||
| // switch(interface_check_result[i]){ | ||
| // case 0xFF: | ||
| // printf("Not configured"); | ||
| // break; | ||
| // case 0: | ||
| // printf("Fail"); | ||
| // break; | ||
| // case 1: | ||
| // printf("Pass"); | ||
| // break; | ||
| // default: | ||
| // printf("Unknown result"); | ||
| // break; | ||
| // } | ||
| // printf("\t\t\t"); | ||
| // if(i%4 == 3) printf("\r\n"); | ||
| // } | ||
| // printf("\r\n"); | ||
| // } | ||
|
|
||
| int lengths[8] = { | ||
| BUFFER_LEN, | ||
| BUFFER_LEN, | ||
| BUFFER_LEN, | ||
| BUFFER_LEN, | ||
| BUFFER_LEN, | ||
| 0, | ||
| 0, | ||
| 0, | ||
| }; | ||
| int16_t* buffers[8] = {NULL}; | ||
| for(int i = 0; i < 5; i++) buffers[i] = (int16_t*)&hydrophone_buffers[i][0][0]; | ||
|
|
||
| ad7606_enter_adc_mode(&my_ADC); | ||
|
|
||
| for(int i = 0; i < 5; i++){ | ||
| utils_clear_array_q15(hydrophone_buffers[i][0], BUFFER_LEN); | ||
| } | ||
|
|
||
| ad7606_init_output_buffers_DMA(&my_ADC, buffers, lengths); | ||
| HAL_MDMA_RegisterCallback(&hmdma_mdma_channel0_sw_0, HAL_MDMA_XFER_CPLT_CB_ID, MyMDMA_TransferCompleteCallback); | ||
| HAL_MDMA_RegisterCallback(&hmdma_mdma_channel0_sw_0, HAL_MDMA_XFER_ERROR_CB_ID, MyMDMA_ErrorCallback); | ||
| ad7606_dma_spi_init(&my_ADC, &hdma_spi6_rx, diagnostics_buffer, BLOCK_LEN); | ||
|
|
||
| mdma_half = 0; | ||
| mdma_done_flag = false; | ||
| } |
There was a problem hiding this comment.
Would clean this function up a bit, remove the commented code or have them in the code. Possible to have them conditially using #ifdef if you do not want them in your code all the time.
| float32_t idx_distance = (biggest_distance/(WAVE_SPEED)*SAMPLING_FREQUENCY) + BLOCK_LEN/4; | ||
| uint16_t n = (uint16_t)ceilf(idx_distance); | ||
| n--; | ||
| n |= n >> 1; | ||
| n |= n >> 2; | ||
| n |= n >> 4; | ||
| n |= n >> 8; | ||
| n |= n >> 16; | ||
| n++; |
There was a problem hiding this comment.
Quite interesting trick, not immideately obvious what is going on here, so again I would recommend using static inline function to express intent clearer

It works, but needs a bit of polish