增加景点播报功能原型 后期需修改

This commit is contained in:
wjw 2025-07-03 15:12:57 +08:00
parent cf05010be5
commit 21790a8b22
2 changed files with 283 additions and 0 deletions

View File

@ -0,0 +1,29 @@
#ifndef ATTR_BROADCAST_H
#define ATTR_BROADCAST_H
#include "cm_os.h"
#include "nmea/nmea.h"
// 景点信息
typedef struct {
double longitude; // 经度
double latitude; // 纬度
char name[50]; // 景点名称
char description[200]; // 景点描述
} Attraction;
// 初始化景点播报系统
void attr_broadcast_init(void);
// 添加景点
void attr_broadcast_add_attraction(double lon, double lat,
const char* name, const char* desc);
// 停止景点播报任务
void attr_broadcast_stop(void);
// 设置播报距离阈值 (米)
void attr_broadcast_set_distance_threshold(double threshold);
#endif /* ATTR_BROADCAST_H */

View File

@ -0,0 +1,254 @@
#include "cm_iomux.h"
#include "cm_gpio.h"
#include "stdio.h"
#include "stdlib.h"
#include "stdarg.h"
#include "stdbool.h"
#include "math.h"
#include "cm_os.h"
#include "cm_mem.h"
#include "cm_sys.h"
#include "cm_uart.h"
#include "app_common.h"
#include "app_uart.h"
#include "attr_broadcast.h"
#include "gps_config.h"
#include "local_tts.h"
#if 0
#include "app_uart.h"
#define DEBUG(fmt, args...) app_printf("[GPS]" fmt, ##args)
#else
#include "app_uart.h"
#define DEBUG(fmt, ...)
#endif
#define MAX_ATTRACTIONS 50 // 最大景点数量
#define PLAY_DISTANCE_THRESHOLD 50.0 // 有效播报距离(米)
#define DISTANCE_CHANGE_THRESHOLD 10.0 // 距离变化阈值(米)
#define TTS_PRIORITY 5 // TTS播报优先级
static osMutexId_t attractions_mutex = NULL;
static AttractionNode* attractions_head = NULL;
static BroadcastState broadcast_state = {0};
static osThreadId_t guide_task_id = NULL;
static int tts_speed = 5;
static int tts_volume = 10;
static nmeaPARSER parser;
typedef enum { false, true } bool; // 手动定义布尔类型
// 全局景点链表
typedef struct AttractionNode {
Attraction attraction;
struct AttractionNode* next;
} AttractionNode;
// 播报状态
typedef struct {
AttractionNode* last_broadcast; // 上次播报的景点
double last_distance; // 上次播报时的距离
uint8_t is_playing; // TTS是否正在播放
} BroadcastState;
//位置信息
typedef struct {
double longitude;
double latitude;
} Location;
//计算到景点的距离
double location_service_calculate_distance(Location loc1, Location loc2) {
// 使用Haversine公式计算真实距离
const double R = 6371000; // 地球半径(米)
double dLat = (loc2.latitude - loc1.latitude) * M_PI / 180.0;
double dLon = (loc2.longitude - loc1.longitude) * M_PI / 180.0;
double a = sin(dLat/2) * sin(dLat/2) +
cos(loc1.latitude * M_PI / 180.0) *
cos(loc2.latitude * M_PI / 180.0) *
sin(dLon/2) * sin(dLon/2);
double c = 2 * atan2(sqrt(a), sqrt(1-a));
return R * c;
}
// 获取当前位置
Location location_service_get_current(void) {
Location current = {0};
// 加锁保护全局数据
if (osMutexAcquire(gps_mutex, 100) == osOK) {
// 检查数据是否有效
if (gps_data.info.sig > 0 && gps_data.info.fix > 0) {
current.longitude = gps_data.dpos.lon;
current.latitude = gps_data.dpos.lat;
}
osMutexRelease(gps_mutex);
}
return current;
}
// 添加景点
void attr_broadcast_add_attraction(double lon, double lat,
const char* name, const char* desc) {
if (attractions_mutex == NULL) return;
osMutexAcquire(attractions_mutex, osWaitForever);
AttractionNode* new_node = malloc(sizeof(AttractionNode));
if (!new_node) {
osMutexRelease(attractions_mutex);
return;
}
// 填充景点信息
new_node->attraction.longitude = lon;
new_node->attraction.latitude = lat;
strncpy(new_node->attraction.name, name, sizeof(new_node->attraction.name)-1);
strncpy(new_node->attraction.description, desc, sizeof(new_node->attraction.description)-1);
// 添加到链表头部
new_node->next = attractions_head;
attractions_head = new_node;
osMutexRelease(attractions_mutex);
}
// 设置播报距离阈值 (米)
void attr_broadcast_set_distance_threshold(double threshold) {
if (threshold > 0) {
broadcast_state.distance_threshold = threshold;
}
}
//设置tts参数
void attr_broadcast_set_tts_params(int speed, int volume) {
tts_speed = speed;
tts_volume = volume;
if (guide_task_id != NULL) {
// 如果任务已启动更新TTS参数
local_tts_set(tts_speed, tts_volume, CM_LOCAL_TTS_DIGIT_AUTO);
}
}
// 根据当前位置查找最近的景区节点
static AttractionNode* find_nearest_attraction(Location current_pos) {
if (attractions_head == NULL) return NULL;
osMutexAcquire(attractions_mutex, osWaitForever);
AttractionNode* current = attractions_head;
AttractionNode* nearest = attractions_head;
double min_distance = location_service_calculate_distance(
(Location){current->attraction.longitude, current->attraction.latitude},
current_pos
);
while (current != NULL) {
double distance = location_service_calculate_distance(
(Location){current->attraction.longitude, current->attraction.latitude},
current_pos
);
if (distance < min_distance) {
min_distance = distance;
nearest = current;
}
current = current->next;
}
osMutexRelease(attractions_mutex);
return nearest;
}
static void play_attraction_info(AttractionNode* attraction, double distance) {
if (!attraction) return;
// 构建播报文本
char buffer[300];
snprintf(buffer, sizeof(buffer), "您已到达%s%s。距离%.1f米。",
attraction->attraction.name,
attraction->attraction.description,
distance);
// 调用TTS接口播放文本
local_tts_text_play(buffer, 0, 1);
// 更新播报状态
broadcast_state.last_broadcast = attraction;
broadcast_state.last_distance = distance;
broadcast_state.is_playing = 1;
}
static void attr_broadcast_task(void* arg) {
(void)arg; // 避免未使用参数警告
while (1) {
// 获取当前位置
Location current_pos = location_service_get_current();
// 查找最近景点
AttractionNode* nearest = find_nearest_attraction(current_pos);
if (nearest != NULL) {
// 计算距离
double distance = location_service_calculate_distance(
(Location){nearest->attraction.longitude, nearest->attraction.latitude},
current_pos
);
// 检查是否在有效范围内
if (distance <= broadcast_state.distance_threshold) {
// 检查是否需要播报
bool should_play = false;
if (broadcast_state.last_broadcast == NULL) {
should_play = true; // 首次播报
}
else if (broadcast_state.last_broadcast != nearest) {
should_play = true; // 新景点
}
else if (fabs(distance - broadcast_state.last_distance) > 10.0) {
should_play = true; // 同一景点但距离变化大
}
// 触发播报
if (should_play && !broadcast_state.is_playing) {
play_attraction_info(nearest, distance);
}
}
}
// 5秒检测一次
osDelay(5000);
}
}
void attr_broadcast_init(void) {
// 初始化互斥锁
if (attractions_mutex == NULL) {
attractions_mutex = osMutexNew(NULL);
}
// 初始化状态
memset(&broadcast_state, 0, sizeof(broadcast_state));
broadcast_state.distance_threshold = 50.0; // 默认阈值50米
}