O7/Core/Src/ref_app_tank_level.c

647 lines
24 KiB
C

// Copyright (c) Acconeer AB, 2020-2021
// All rights reserved
// This file is subject to the terms and conditions defined in the file
// 'LICENSES/license_acconeer.txt', (BSD 3-Clause License) which is part
// of this source code package.
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include "sys_app.h"
#include "acc_definitions_common.h"
#include "acc_detector_distance.h"
#include "acc_hal_definitions.h"
#include "acc_hal_integration.h"
#include "acc_rss.h"
#include "acc_version.h"
// Default values for this reference application
// Note that if a range longer than 7 m is used, an update of 'mur' is required.
// See API documentation for more information of respective parameter
#define DEFAULT_SENSOR 1
#define DEFAULT_CLOSE_RANGE_MUR ACC_SERVICE_MUR_6
#define DEFAULT_CLOSE_RANGE_START -0.11f
#define DEFAULT_CLOSE_RANGE_LENGTH 0.23f
#define DEFAULT_CLOSE_RANGE_GAIN 0.3182f
#define DEFAULT_CLOSE_MAXIMIZE_SIGNAL_ATTENUATION true
#define DEFAULT_CLOSE_RANGE_SERVICE_PROFILE ACC_SERVICE_PROFILE_1
#define DEFAULT_CLOSE_RANGE_DOWNSAMPLIG_FACTOR 1
#define DEFAULT_CLOSE_RANGE_SWEEP_AVERAGING 30
#define DEFAULT_CLOSE_RANGE_THRESHOLD_TYPE ACC_DETECTOR_DISTANCE_THRESHOLD_TYPE_RECORDED
#define DEFAULT_CLOSE_RANGE_RECORD_BACKGROUND_SWEEPS 30
#define DEFAULT_CLOSE_RANGE_THRESHOLD_SENSITIVITY 0.2f
#define DEFAULT_MID_RANGE_MUR ACC_SERVICE_MUR_6
#define DEFAULT_MID_RANGE_START 0.1f
#define DEFAULT_MID_RANGE_LENGTH 0.37f
#define DEFAULT_MID_RANGE_GAIN 0.5f
#define DEFAULT_MID_MAXIMIZE_SIGNAL_ATTENUATION false
#define DEFAULT_MID_RANGE_SERVICE_PROFILE ACC_SERVICE_PROFILE_1
#define DEFAULT_MID_RANGE_DOWNSAMPLING_FACTOR 1
#define DEFAULT_MID_RANGE_SWEEP_AVERAGING 30
#define DEFAULT_MID_RANGE_THRESHOLD_TYPE ACC_DETECTOR_DISTANCE_THRESHOLD_TYPE_RECORDED
#define DEFAULT_MID_RANGE_RECORD_BACKGROUND_SWEEPS 30
#define DEFAULT_MID_RANGE_THRESHOLD_SENSITIVITY 0.2f
#define DEFAULT_FAR_RANGE_MUR ACC_SERVICE_MUR_6
#define DEFAULT_FAR_RANGE_START 0.19f
#define DEFAULT_FAR_RANGE_LENGTH 3.3f //1.3f
#define DEFAULT_FAR_RANGE_GAIN 0.8182f //0.8182f
#define DEFAULT_FAR_MAXIMIZE_SIGNAL_ATTENUATION false
#define DEFAULT_FAR_RANGE_SERVICE_PROFILE ACC_SERVICE_PROFILE_2
#define DEFAULT_FAR_RANGE_DOWNSAMPLING_FACTOR 4
#define DEFAULT_FAR_RANGE_SWEEP_AVERAGING 10
#define DEFAULT_FAR_RANGE_THRESHOLD_TYPE ACC_DETECTOR_DISTANCE_THRESHOLD_TYPE_CFAR
#define DEFAULT_FAR_RANGE_THRESHOLD_SENSITIVITY 0.4f
#define DEFAULT_FAR_RANGE_CFAR_THRESHOLD_GUARD 0.12f
#define DEFAULT_FAR_RANGE_CFAR_THRESHOLD_WINDOW 0.03f
// Gain can be configured in 22 steps between 0.0 and 1.0
#define GAIN_STEP (1.0f / 22.0f)
#define MAX_BACKGROUND_LENGTH 1200
static uint16_t close_background[MAX_BACKGROUND_LENGTH];
static uint16_t close_background_length;
static float close_range_gain = DEFAULT_CLOSE_RANGE_GAIN;
static uint16_t mid_background[MAX_BACKGROUND_LENGTH];
static uint16_t mid_background_length;
static float mid_range_gain = DEFAULT_MID_RANGE_GAIN;
/**
* Calibrate the sensor
*
* @return True, if sensor calibration was successful
*/
static bool sensor_calibration(void);
/**
* Configure distance detector to measure in the close range sector
*
* @param distance_handle Distance Detector handle
* @param distance_configuration Distance Detector configuration
* @return True, if configuration was successful
*/
static bool configure_close_range(acc_detector_distance_handle_t *distance_handle,
acc_detector_distance_configuration_t distance_configuration);
/**
* Configure distance detector to measure in the mid range sector
*
* @param distance_handle Distance Detector handle
* @param distance_configuration Distance Detector configuration
* @return True, if configuration was successful
*/
static bool configure_mid_range(acc_detector_distance_handle_t *distance_handle,
acc_detector_distance_configuration_t distance_configuration);
/**
* Configure distance detector to measure in the far range sector
*
* @param distance_handle Distance Detector handle
* @param distance_configuration Distance Detector configuration
* @return True, if configuration was successful
*/
static bool configure_far_range(acc_detector_distance_handle_t *distance_handle,
acc_detector_distance_configuration_t distance_configuration);
/**
* Record the background for specific configuration
*
* This function will set the requested gain, but the gain will be lowered in case of data saturation.
*
* @param distance_handle Distance Detector handle
* @param distance_configuration Distance Detector configuration
* @param range_gain The gain actually used
* @param background Array to store background in
* @param background_length Length of background array
* @return True, if recording was successful
*/
static bool record_background(acc_detector_distance_handle_t *distance_handle,
acc_detector_distance_configuration_t distance_configuration,
float *range_gain, uint16_t *background, uint16_t background_length);
/**
* Record background threshold for close and mid range sector
*
* Make sure no object is within these two sectors, i.e. no objects closer
* to the sensor than DEFAULT_MID_RANGE_START + DEFAULT_MID_RANGE_LENGTH
*
* @param distance_handle Distance Detector handle
* @param distance_configuration Distance Detector configuration
* @return True, if recording was successful
*/
static bool record_backgrounds(acc_detector_distance_handle_t *distance_handle,
acc_detector_distance_configuration_t distance_configuration);
/**
* Perform one measurement
*
* This function will set the requested gain, but the gain will be lowered in case of data saturation.
*
* @param distance_handle Distance Detector handle
* @param distance_configuration Distance Detector configuration
* @param result Distance Detector result
* @param result_info Distance Detector result info
*/
static bool measurement(acc_detector_distance_handle_t *distance_handle,
acc_detector_distance_configuration_t distance_configuration,
acc_detector_distance_result_t *result,
acc_detector_distance_result_info_t *result_info);
/**
* Measure in the close range sector
*
* @param distance_handle Distance Detector handle
* @param distance_configuration Distance Detector configuration
* @param distance_detected True, if a distance was detected
* @param distance Detected distance
* @return True, if measurement was successful
*/
static bool measure_close_range(acc_detector_distance_handle_t *distance_handle,
acc_detector_distance_configuration_t distance_configuration,
bool *distance_detected, float *distance);
/**
* Measure in the mid range sector
*
* @param distance_handle Distance Detector handle
* @param distance_configuration Distance Detector configuration
* @param distance_detected True, if a distance was detected
* @param distance Detected distance
* @return True, if measurement was successful
*/
static bool measure_mid_range(acc_detector_distance_handle_t *distance_handle,
acc_detector_distance_configuration_t distance_configuration,
bool *distance_detected, float *distance);
/**
* Measure in the far range sector
*
* @param distance_handle Distance Detector handle
* @param distance_configuration Distance Detector configuration
* @param distance_detected True, if a distance was detected
* @param distance Detected distance
* @return True, if measurement was successful
*/
static bool measure_far_range(acc_detector_distance_handle_t *distance_handle,
acc_detector_distance_configuration_t distance_configuration,
bool *distance_detected, float *distance);
int acc_ref_app_tank_level(int argc, char *argv[]);
int acc_ref_app_tank_level(int argc, char *argv[])
{
(void)argc;
(void)argv;
APP_LOG(TS_OFF, VLEVEL_M,"Acconeer software version %s\n", acc_version_get());
const acc_hal_t *hal = acc_hal_integration_get_implementation();
if (!acc_rss_activate(hal))
{
APP_LOG(TS_OFF, VLEVEL_M,"Failed to activate RSS\n");
return EXIT_FAILURE;
}
acc_detector_distance_configuration_t distance_configuration = acc_detector_distance_configuration_create();
if (distance_configuration == NULL)
{
APP_LOG(TS_OFF, VLEVEL_M,"Failed to create detector configuration\n");
acc_rss_deactivate();
return EXIT_FAILURE;
}
acc_detector_distance_handle_t distance_handle = acc_detector_distance_create(distance_configuration);
if (distance_handle == NULL)
{
APP_LOG(TS_OFF, VLEVEL_M,"Failed to create detector\n");
acc_detector_distance_configuration_destroy(&distance_configuration);
acc_rss_deactivate();
return EXIT_FAILURE;
}
if (!record_backgrounds(&distance_handle, distance_configuration))
{
APP_LOG(TS_OFF, VLEVEL_M,"Failed to calibrate detector\n");
acc_detector_distance_configuration_destroy(&distance_configuration);
acc_detector_distance_destroy(&distance_handle);
acc_rss_deactivate();
return EXIT_FAILURE;
}
// If there is a long time between measurements it is good to calibrate the sensor
// before each measurement
acc_detector_distance_destroy(&distance_handle);
if (!sensor_calibration())
{
APP_LOG(TS_OFF, VLEVEL_M,"Failed to calibrate sensor\n");
return EXIT_FAILURE;
}
distance_handle = acc_detector_distance_create(distance_configuration);
bool status = true;
while (status)
{
float distance = 0.0f;
bool distance_detected = false;
APP_LOG(TS_OFF, VLEVEL_M,"Measure close range\n");
status = measure_close_range(&distance_handle, distance_configuration, &distance_detected, &distance);
if (status && !distance_detected)
{
APP_LOG(TS_OFF, VLEVEL_M,"Measure mid range\n");
status = measure_mid_range(&distance_handle, distance_configuration, &distance_detected, &distance);
}
if (status && !distance_detected)
{
APP_LOG(TS_OFF, VLEVEL_M,"Measure far range\n");
status = measure_far_range(&distance_handle, distance_configuration, &distance_detected, &distance);
}
if (!status)
{
APP_LOG(TS_OFF, VLEVEL_M,"Measurement failed\n");
break;
}
if (distance_detected)
{
APP_LOG(TS_OFF, VLEVEL_M,"Peak at %u mm\n", (unsigned int)(distance * 1000));
}
else
{
APP_LOG(TS_OFF, VLEVEL_M,"No peak found\n");
}
//Add a call to a sleep function here to limit measurement update rate
}
acc_detector_distance_configuration_destroy(&distance_configuration);
acc_detector_distance_destroy(&distance_handle);
acc_rss_deactivate();
return status ? EXIT_SUCCESS : EXIT_FAILURE;
}
bool sensor_calibration(void)
{
acc_calibration_context_t calibration_context;
if (!acc_rss_calibration_context_get(DEFAULT_SENSOR, &calibration_context))
{
return false;
}
if (!acc_rss_calibration_context_forced_set(DEFAULT_SENSOR, &calibration_context))
{
return false;
}
return true;
}
bool configure_close_range(acc_detector_distance_handle_t *distance_handle,
acc_detector_distance_configuration_t distance_configuration)
{
acc_detector_distance_configuration_sensor_set(distance_configuration, DEFAULT_SENSOR);
acc_detector_distance_configuration_mur_set(distance_configuration, DEFAULT_CLOSE_RANGE_MUR);
acc_detector_distance_configuration_requested_start_set(distance_configuration, DEFAULT_CLOSE_RANGE_START);
acc_detector_distance_configuration_requested_length_set(distance_configuration, DEFAULT_CLOSE_RANGE_LENGTH);
acc_detector_distance_configuration_receiver_gain_set(distance_configuration, close_range_gain);
acc_detector_distance_configuration_maximize_signal_attenuation_set(distance_configuration,
DEFAULT_CLOSE_MAXIMIZE_SIGNAL_ATTENUATION);
acc_detector_distance_configuration_service_profile_set(distance_configuration,
DEFAULT_CLOSE_RANGE_SERVICE_PROFILE);
acc_detector_distance_configuration_downsampling_factor_set(distance_configuration,
DEFAULT_CLOSE_RANGE_DOWNSAMPLIG_FACTOR);
acc_detector_distance_configuration_sweep_averaging_set(distance_configuration,
DEFAULT_CLOSE_RANGE_SWEEP_AVERAGING);
acc_detector_distance_configuration_threshold_type_set(distance_configuration,
DEFAULT_CLOSE_RANGE_THRESHOLD_TYPE);
acc_detector_distance_configuration_record_background_sweeps_set(distance_configuration,
DEFAULT_CLOSE_RANGE_RECORD_BACKGROUND_SWEEPS);
acc_detector_distance_configuration_threshold_sensitivity_set(distance_configuration,
DEFAULT_CLOSE_RANGE_THRESHOLD_SENSITIVITY);
return acc_detector_distance_reconfigure(distance_handle, distance_configuration);
}
bool configure_mid_range(acc_detector_distance_handle_t *distance_handle,
acc_detector_distance_configuration_t distance_configuration)
{
acc_detector_distance_configuration_mur_set(distance_configuration, DEFAULT_MID_RANGE_MUR);
acc_detector_distance_configuration_requested_start_set(distance_configuration, DEFAULT_MID_RANGE_START);
acc_detector_distance_configuration_requested_length_set(distance_configuration, DEFAULT_MID_RANGE_LENGTH);
acc_detector_distance_configuration_receiver_gain_set(distance_configuration, mid_range_gain);
acc_detector_distance_configuration_maximize_signal_attenuation_set(distance_configuration,
DEFAULT_MID_MAXIMIZE_SIGNAL_ATTENUATION);
acc_detector_distance_configuration_service_profile_set(distance_configuration,
DEFAULT_MID_RANGE_SERVICE_PROFILE);
acc_detector_distance_configuration_downsampling_factor_set(distance_configuration,
DEFAULT_MID_RANGE_DOWNSAMPLING_FACTOR);
acc_detector_distance_configuration_sweep_averaging_set(distance_configuration,
DEFAULT_MID_RANGE_SWEEP_AVERAGING);
acc_detector_distance_configuration_threshold_type_set(distance_configuration,
DEFAULT_MID_RANGE_THRESHOLD_TYPE);
acc_detector_distance_configuration_record_background_sweeps_set(distance_configuration,
DEFAULT_MID_RANGE_RECORD_BACKGROUND_SWEEPS);
acc_detector_distance_configuration_threshold_sensitivity_set(distance_configuration,
DEFAULT_MID_RANGE_THRESHOLD_SENSITIVITY);
return acc_detector_distance_reconfigure(distance_handle, distance_configuration);
}
bool configure_far_range(acc_detector_distance_handle_t *distance_handle,
acc_detector_distance_configuration_t distance_configuration)
{
acc_detector_distance_configuration_mur_set(distance_configuration, DEFAULT_FAR_RANGE_MUR);
acc_detector_distance_configuration_requested_start_set(distance_configuration, DEFAULT_FAR_RANGE_START);
acc_detector_distance_configuration_requested_length_set(distance_configuration, DEFAULT_FAR_RANGE_LENGTH);
acc_detector_distance_configuration_receiver_gain_set(distance_configuration, DEFAULT_FAR_RANGE_GAIN);
acc_detector_distance_configuration_maximize_signal_attenuation_set(distance_configuration,
DEFAULT_FAR_MAXIMIZE_SIGNAL_ATTENUATION);
acc_detector_distance_configuration_service_profile_set(distance_configuration,
DEFAULT_FAR_RANGE_SERVICE_PROFILE);
acc_detector_distance_configuration_downsampling_factor_set(distance_configuration,
DEFAULT_FAR_RANGE_DOWNSAMPLING_FACTOR);
acc_detector_distance_configuration_sweep_averaging_set(distance_configuration,
DEFAULT_FAR_RANGE_SWEEP_AVERAGING);
acc_detector_distance_configuration_threshold_type_set(distance_configuration,
DEFAULT_FAR_RANGE_THRESHOLD_TYPE);
acc_detector_distance_configuration_threshold_sensitivity_set(distance_configuration,
DEFAULT_FAR_RANGE_THRESHOLD_SENSITIVITY);
acc_detector_distance_configuration_cfar_threshold_guard_set(distance_configuration,
DEFAULT_FAR_RANGE_CFAR_THRESHOLD_GUARD);
acc_detector_distance_configuration_cfar_threshold_window_set(distance_configuration,
DEFAULT_FAR_RANGE_CFAR_THRESHOLD_WINDOW);
return acc_detector_distance_reconfigure(distance_handle, distance_configuration);
}
bool record_background(acc_detector_distance_handle_t *distance_handle,
acc_detector_distance_configuration_t distance_configuration,
float *range_gain, uint16_t *background, uint16_t background_length)
{
acc_detector_distance_recorded_background_info_t recorded_background_info;
bool status = true;
bool previous_gain_saturated = true;
float gain = acc_detector_distance_configuration_receiver_gain_get(distance_configuration);
recorded_background_info.data_saturated = true;
while (status && gain > 0)
{
acc_detector_distance_configuration_receiver_gain_set(distance_configuration, gain);
status = acc_detector_distance_reconfigure(distance_handle, distance_configuration);
if (status)
{
status = acc_detector_distance_record_background(*distance_handle, background, background_length,
&recorded_background_info);
if (!status || (!previous_gain_saturated && !recorded_background_info.data_saturated))
{
break;
}
previous_gain_saturated = recorded_background_info.data_saturated;
gain = gain - GAIN_STEP;
}
}
if (status && recorded_background_info.data_saturated)
{
APP_LOG(TS_OFF, VLEVEL_M,"Unable to record background without data saturation\n");
status = false;
}
*range_gain = gain;
return status;
}
bool record_backgrounds(acc_detector_distance_handle_t *distance_handle,
acc_detector_distance_configuration_t distance_configuration)
{
acc_detector_distance_metadata_t metadata;
if (!configure_close_range(distance_handle, distance_configuration))
{
return false;
}
if (!acc_detector_distance_metadata_get(*distance_handle, &metadata))
{
return false;
}
close_background_length = metadata.background_length;
APP_LOG(TS_OFF, VLEVEL_M,"Record close range\n");
if (!record_background(distance_handle, distance_configuration, &close_range_gain,
close_background, close_background_length))
{
return false;
}
if (!configure_mid_range(distance_handle, distance_configuration))
{
return false;
}
if (!acc_detector_distance_metadata_get(*distance_handle, &metadata))
{
return false;
}
mid_background_length = metadata.background_length;
APP_LOG(TS_OFF, VLEVEL_M,"Record mid range\n");
if (!record_background(distance_handle, distance_configuration, &mid_range_gain,
mid_background, mid_background_length))
{
return false;
}
return true;
}
bool measurement(acc_detector_distance_handle_t *distance_handle,
acc_detector_distance_configuration_t distance_configuration,
acc_detector_distance_result_t *result,
acc_detector_distance_result_info_t *result_info)
{
float gain = acc_detector_distance_configuration_receiver_gain_get(distance_configuration);
result_info->data_saturated = true;
while (gain > 0)
{
if (!acc_detector_distance_activate(*distance_handle))
{
APP_LOG(TS_OFF, VLEVEL_M,"Failed to activate detector\n");
return false;
}
if (!acc_detector_distance_get_next(*distance_handle, result, 1, result_info))
{
APP_LOG(TS_OFF, VLEVEL_M,"Failed to get next from detector\n");
return false;
}
if (!acc_detector_distance_deactivate(*distance_handle))
{
APP_LOG(TS_OFF, VLEVEL_M,"Failed to deactivate detector\n");
return false;
}
if (!result_info->data_saturated || gain < GAIN_STEP)
{
break;
}
gain = gain - GAIN_STEP;
acc_detector_distance_configuration_receiver_gain_set(distance_configuration, gain);
if (!acc_detector_distance_reconfigure(distance_handle, distance_configuration))
{
return false;
}
}
if (result_info->data_saturated)
{
APP_LOG(TS_OFF, VLEVEL_M,"Unable to measure without data saturation\n");
return false;
}
return true;
}
bool measure_close_range(acc_detector_distance_handle_t *distance_handle,
acc_detector_distance_configuration_t distance_configuration,
bool *distance_detected, float *distance)
{
*distance_detected = false;
acc_detector_distance_result_info_t result_info;
acc_detector_distance_result_t result;
if (!configure_close_range(distance_handle, distance_configuration))
{
return false;
}
if (!acc_detector_distance_set_background(*distance_handle, close_background, close_background_length))
{
return false;
}
if (!measurement(distance_handle, distance_configuration, &result, &result_info))
{
return false;
}
if (result_info.measurement_sample_above_threshold)
{
*distance_detected = true;
*distance = result_info.closest_detection_m;
}
return true;
}
bool measure_mid_range(acc_detector_distance_handle_t *distance_handle,
acc_detector_distance_configuration_t distance_configuration,
bool *distance_detected, float *distance)
{
*distance_detected = false;
acc_detector_distance_result_info_t result_info;
acc_detector_distance_result_t result;
if (!configure_mid_range(distance_handle, distance_configuration))
{
return false;
}
if (!acc_detector_distance_set_background(*distance_handle, mid_background, mid_background_length))
{
return false;
}
if (!measurement(distance_handle, distance_configuration, &result, &result_info))
{
return false;
}
if (result_info.number_of_peaks > 0)
{
*distance_detected = true;
*distance = result.distance_m;
}
return true;
}
bool measure_far_range(acc_detector_distance_handle_t *distance_handle,
acc_detector_distance_configuration_t distance_configuration,
bool *distance_detected, float *distance)
{
*distance_detected = false;
acc_detector_distance_result_info_t result_info;
acc_detector_distance_result_t result;
if (!configure_far_range(distance_handle, distance_configuration))
{
return false;
}
if (!measurement(distance_handle, distance_configuration, &result, &result_info))
{
return false;
}
if (result_info.number_of_peaks > 0)
{
*distance_detected = true;
*distance = result.distance_m;
}
return true;
}