除了显示驱动,其余全部测试通过
This commit is contained in:
@@ -0,0 +1,332 @@
|
||||
#include "system/includes.h"
|
||||
#include "radar_manager.h"
|
||||
#include <string.h>
|
||||
#include "app_log.h"
|
||||
|
||||
static struct radar_sensor_data current_data;
|
||||
static struct radar_sensor_data consumed_data;
|
||||
|
||||
static OS_MUTEX radar_manager_mutex;
|
||||
static u8 radar_manager_mutex_ready;
|
||||
static u8 has_consumed_snapshot;
|
||||
|
||||
|
||||
static u16 abs_diff_u16(u16 a, u16 b)
|
||||
{
|
||||
return (a >= b) ? (u16)(a - b) : (u16)(b - a);
|
||||
}
|
||||
|
||||
|
||||
static u8 abs_diff_u8(u8 a, u8 b)
|
||||
{
|
||||
return (a >= b) ? (u8)(a - b) : (u8)(b - a);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* 保留 ESP32 当前最终业务判定:
|
||||
* heart: > 0 && < 200 bpm
|
||||
* breath: > 0 && <= 60 /min
|
||||
*
|
||||
* AC79 内部使用 x10 整数。
|
||||
*/
|
||||
static u8 heart_rate_valid(u16 value_x10)
|
||||
{
|
||||
return (value_x10 > 0 && value_x10 < 2000) ? 1 : 0;
|
||||
}
|
||||
|
||||
|
||||
static u8 breath_rate_valid(u16 value_x10)
|
||||
{
|
||||
return (value_x10 > 0 && value_x10 <= 600) ? 1 : 0;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* 调用者必须持有 radar_manager_mutex。
|
||||
*/
|
||||
static void build_change_flags_locked(
|
||||
struct radar_change_flags *flags
|
||||
)
|
||||
{
|
||||
memset(flags, 0, sizeof(*flags));
|
||||
|
||||
if (!has_consumed_snapshot) {
|
||||
|
||||
if (current_data.last_update_ms != 0) {
|
||||
flags->any_changed = 1;
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
flags->presence_changed =
|
||||
current_data.presence != consumed_data.presence;
|
||||
|
||||
flags->motion_changed =
|
||||
current_data.motion != consumed_data.motion;
|
||||
|
||||
flags->sleep_state_changed =
|
||||
current_data.sleep_state != consumed_data.sleep_state;
|
||||
|
||||
/*
|
||||
* ESP32 当前 meaningful-change 阈值:
|
||||
* body movement >= 5
|
||||
* distance >= 10 cm
|
||||
* heart >= 3 bpm
|
||||
* breath >= 2 /min
|
||||
*/
|
||||
flags->body_movement_changed =
|
||||
abs_diff_u8(
|
||||
current_data.body_movement,
|
||||
consumed_data.body_movement
|
||||
) >= 5;
|
||||
|
||||
flags->distance_changed =
|
||||
abs_diff_u16(
|
||||
current_data.distance_cm,
|
||||
consumed_data.distance_cm
|
||||
) >= 10;
|
||||
|
||||
flags->heart_rate_changed =
|
||||
abs_diff_u16(
|
||||
current_data.heart_rate_x10,
|
||||
consumed_data.heart_rate_x10
|
||||
) >= 30;
|
||||
|
||||
flags->breath_rate_changed =
|
||||
abs_diff_u16(
|
||||
current_data.breath_rate_x10,
|
||||
consumed_data.breath_rate_x10
|
||||
) >= 20;
|
||||
|
||||
flags->abnormal_state_changed =
|
||||
current_data.abnormal_state !=
|
||||
consumed_data.abnormal_state;
|
||||
|
||||
|
||||
flags->any_changed =
|
||||
flags->presence_changed ||
|
||||
flags->motion_changed ||
|
||||
flags->sleep_state_changed ||
|
||||
flags->body_movement_changed ||
|
||||
flags->distance_changed ||
|
||||
flags->heart_rate_changed ||
|
||||
flags->breath_rate_changed ||
|
||||
flags->abnormal_state_changed;
|
||||
}
|
||||
|
||||
|
||||
int radar_manager_init(void)
|
||||
{
|
||||
memset(¤t_data, 0, sizeof(current_data));
|
||||
memset(&consumed_data, 0, sizeof(consumed_data));
|
||||
|
||||
has_consumed_snapshot = 0;
|
||||
|
||||
if (!radar_manager_mutex_ready) {
|
||||
os_mutex_create(&radar_manager_mutex);
|
||||
radar_manager_mutex_ready = 1;
|
||||
}
|
||||
|
||||
APP_LOG("[RADAR_MGR] init success\n");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int radar_manager_update(
|
||||
const struct radar_manager_input *input
|
||||
)
|
||||
{
|
||||
if (!input) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (!radar_manager_mutex_ready) {
|
||||
return -2;
|
||||
}
|
||||
|
||||
|
||||
os_mutex_pend(&radar_manager_mutex, 0);
|
||||
|
||||
|
||||
current_data.presence = input->presence;
|
||||
current_data.motion = input->motion;
|
||||
current_data.body_movement = input->body_movement;
|
||||
|
||||
current_data.distance_cm = input->distance_cm;
|
||||
|
||||
current_data.heart_rate_x10 = input->heart_rate_x10;
|
||||
current_data.breath_rate_x10 = input->breath_rate_x10;
|
||||
|
||||
current_data.heart_valid =
|
||||
heart_rate_valid(input->heart_rate_x10);
|
||||
|
||||
current_data.breath_valid =
|
||||
breath_rate_valid(input->breath_rate_x10);
|
||||
|
||||
current_data.sleep_state = input->sleep_state;
|
||||
|
||||
current_data.pos_x_mm = input->pos_x_mm;
|
||||
current_data.pos_y_mm = input->pos_y_mm;
|
||||
current_data.pos_z_mm = input->pos_z_mm;
|
||||
|
||||
current_data.abnormal_state = input->abnormal_state;
|
||||
|
||||
current_data.last_update_ms = input->update_ms;
|
||||
|
||||
|
||||
os_mutex_post(&radar_manager_mutex);
|
||||
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int radar_manager_get_sensor_data(
|
||||
struct radar_sensor_data *out
|
||||
)
|
||||
{
|
||||
if (!out) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (!radar_manager_mutex_ready) {
|
||||
return -2;
|
||||
}
|
||||
|
||||
|
||||
os_mutex_pend(&radar_manager_mutex, 0);
|
||||
|
||||
memcpy(out, ¤t_data, sizeof(*out));
|
||||
|
||||
os_mutex_post(&radar_manager_mutex);
|
||||
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int radar_manager_get_change_flags(
|
||||
struct radar_change_flags *out
|
||||
)
|
||||
{
|
||||
if (!out) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (!radar_manager_mutex_ready) {
|
||||
return -2;
|
||||
}
|
||||
|
||||
|
||||
os_mutex_pend(&radar_manager_mutex, 0);
|
||||
|
||||
build_change_flags_locked(out);
|
||||
|
||||
os_mutex_post(&radar_manager_mutex);
|
||||
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int radar_manager_get_report_snapshot(
|
||||
u32 now_ms,
|
||||
u32 max_age_ms,
|
||||
struct radar_report_snapshot *out
|
||||
)
|
||||
{
|
||||
struct radar_change_flags flags;
|
||||
u32 age;
|
||||
|
||||
if (!out) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (!radar_manager_mutex_ready) {
|
||||
return -2;
|
||||
}
|
||||
|
||||
|
||||
memset(out, 0, sizeof(*out));
|
||||
|
||||
|
||||
os_mutex_pend(&radar_manager_mutex, 0);
|
||||
|
||||
|
||||
memcpy(
|
||||
&out->sensor,
|
||||
¤t_data,
|
||||
sizeof(out->sensor)
|
||||
);
|
||||
|
||||
build_change_flags_locked(&flags);
|
||||
|
||||
|
||||
out->has_person =
|
||||
current_data.presence != 0;
|
||||
|
||||
/*
|
||||
* 先保持 ESP32 当前语义:
|
||||
* presence != 0 或 sleep_state > 0
|
||||
*/
|
||||
out->bed_occupied =
|
||||
(current_data.presence != 0) ||
|
||||
(current_data.sleep_state > 0);
|
||||
|
||||
out->has_valid_vitals =
|
||||
current_data.heart_valid ||
|
||||
current_data.breath_valid;
|
||||
|
||||
out->has_meaningful_change =
|
||||
flags.any_changed;
|
||||
|
||||
|
||||
if (current_data.last_update_ms != 0) {
|
||||
|
||||
age =
|
||||
(u32)(
|
||||
now_ms -
|
||||
current_data.last_update_ms
|
||||
);
|
||||
|
||||
out->sample_age_ms = age;
|
||||
|
||||
if (age <= max_age_ms) {
|
||||
out->has_fresh_sample = 1;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
os_mutex_post(&radar_manager_mutex);
|
||||
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int radar_manager_mark_snapshot_consumed(void)
|
||||
{
|
||||
if (!radar_manager_mutex_ready) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
os_mutex_pend(&radar_manager_mutex, 0);
|
||||
|
||||
memcpy(
|
||||
&consumed_data,
|
||||
¤t_data,
|
||||
sizeof(consumed_data)
|
||||
);
|
||||
|
||||
has_consumed_snapshot = 1;
|
||||
|
||||
os_mutex_post(&radar_manager_mutex);
|
||||
|
||||
|
||||
return 0;
|
||||
}
|
||||
Reference in New Issue
Block a user