diff --git a/custom/attr_broadcast/inc/attr_broadcast.h b/custom/attr_broadcast/inc/attr_broadcast.h new file mode 100644 index 0000000..c48e992 --- /dev/null +++ b/custom/attr_broadcast/inc/attr_broadcast.h @@ -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 */ \ No newline at end of file diff --git a/custom/attr_broadcast/src/attr_broadcast.c b/custom/attr_broadcast/src/attr_broadcast.c new file mode 100644 index 0000000..e494e31 --- /dev/null +++ b/custom/attr_broadcast/src/attr_broadcast.c @@ -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米 + + + +} \ No newline at end of file