-// pah\r
-#include "pah800x_main.h"\r
-//#include "pah_8009_reg_arr_default.h"\r
-#include "pxialg_spo2.h"\r
-#include "pxialg_hrv.h"\r
-#include "pxivar.h"\r
-#include "pah_8009_reg_arr_default_20220623.h"\r
-#include "pah800x_factory_test.h"\r
-#include "cmsis_os2.h"\r
-\r
-#ifdef SUPPORT_PPG_SERVICE\r
-#include "ppg_service.h"\r
-#include "ppg_hal.h"\r
-#endif\r
-#ifdef SUPPORT_CYWEE_SERVICE\r
-#include "cywee_service.h"\r
-#endif\r
-#include "custom_runtime.h"\r
-#ifdef SUPPORT_PPG_BLE_SERVICE\r
-#include "ppgBleService.h"\r
-#endif\r
-\r
-#define HRS_WINDOW_SIZE 5\r
-//============================================================================\r
-// Static Global Variable\r
-//============================================================================\r
-static main_state _main;\r
-static ppg_mems_data_t ppg_mems_data_buf[FIFO_SIZE] ;\r
-static int _read_index = 0 ;\r
-static int _write_index = 0 ;\r
- \r
-// hr algotirhm\r
-static float hr = 0.0f;\r
-static float hr_grader =0.0f;\r
-static ppg_mems_data_t ppg_mems_data_alg;\r
-\r
-// spo2 algorithm\r
-void *pBuffer = NULL;\r
-void *hr_pBuffer = NULL; \r
-static float myHR = 0.0f;\r
-static float mySpO2 = 0.0f;\r
-static float correlation = 0.0f;\r
-static float trust_flag = 0.0f;\r
-static float MEMS_RMS = 0.0f;\r
-uint8_t mySPO2_ReadyFlag = 0 ;\r
-static uint8_t no_value_reason = 0 ;\r
-static float SignalPeriodicity=0.0f ;\r
-\r
-static int time_accu = 0 ;\r
-static int count =0;\r
-static float spo2_array[31]={0.0f}; \r
-\r
-static uint32_t data_buf_count_spo2_alg=0;\r
-\r
-static SpO2_info_t _spo2_detect_item;\r
-pah8series_data_t pxialg_data_spo2;\r
-static SpO2_mems_t mems_data_raw[150]; \r
-#ifdef PPG_MEMS_Synchronize \r
-//int16_t mems_int_data_in[90] ={0};\r
-int16_t mems_int_data_out[90]={0};\r
-int time_temp=0; \r
-static uint8_t ppg_data_num=0;\r
-uint8_t mems_data_polling_ready = 0 ; \r
-#endif\r
-\r
-// Stress Algorithm \r
-static hrv_state_s _hrv_state;\r
-void *stress_pBuffer = NULL; \r
-#define HRV_ENABLE_LHR 1\r
-#define HRV_SAMPLING_RATE 20\r
-#define HRV_COLLECTION_SECONDS 180 \r
-static uint32_t duration_total = 0;\r
-uint8_t SDNN_ReadyFlag = 0;\r
-uint8_t Get_Stress = 0;\r
-static float Stress = 0.0f;\r
-static uint8_t HRAvg = 0;\r
-static float SQ_Stress = 0.0f;\r
-static uint8_t AnomalyFlag = 0;\r
-uint8_t Stress_ReadyFlag = 0;\r
-uint8_t Weak_Flag = 0;\r
-\r
-static int32_t factory_data = 0;\r
-static uint8_t work_mode = 0;\r
-static uint8_t wear_status = 0;\r
-\r
-static osTimerId_t g_pah8009_timer = NULL;\r
-\r
-static uint8_t time_cnt = 0;\r
-static uint8_t wear_cnt = 0;\r
-\r
-static int history_wear_status = -1;\r
-\r
-static int time_out = 15;\r
-\r
-\r
-static uint8_t hrs_num = 0;\r
-static int32_t sum_hrs = 0;\r
-static int8_t hrs_data[HRS_WINDOW_SIZE] = {0};\r
-static uint8_t left = 0;\r
-static uint8_t right = 0;\r
-static uint8_t avg_hrs = 0;\r
-\r
-static uint8_t timeout = 0;\r
-\r
-//============================================================================\r
-// Private function\r
-//============================================================================\r
-static bool sensor_basic_verification(void); \r
-static void process(uint64_t timestamp);\r
-static void ppg_sensor_enable(void);\r
-static void ppg_sensor_disable(void);\r
-static void touch_sensor_enable(void);\r
-static void touch_sensor_disable(void);\r
-\r
-static void report_touch_data(uint64_t timestamp, const int8_t *touch_flag);\r
-static void report_ppg_data(const ppg_mems_data_t *ppg_mems_data_alg);\r
-static void report_hr_data(const ppg_mems_data_t *ppg_mems_data_alg, int32_t ret, float hr, float hr_grader);\r
-static void report_spo2_data(const ppg_mems_data_t *ppg_mems_data_alg, int32_t ret, float myHR, float mySpO2, float correlation, float trust_flag, float MEMS_RMS, int data_buf_count_spo2_alg, uint8_t no_value_reason, float SignalPeriodicity, int time_accu );\r
-static void report_stress_data(const ppg_mems_data_t *ppg_mems_data_alg, uint8_t Stress_ReadyFlag, uint32_t measuretime, float collect_time);\r
-\r
-static void error_handle(void);\r
-\r
-\r
-static void pah8009_unwearstatus_update(void)\r
-{\r
- uint8_t data = 0;\r
- printf("uapi_dev_runtime_get_train_status() : %d\n",uapi_dev_runtime_get_train_status()); \r
- if(uapi_dev_runtime_get_train_status())\r
- {\r
- }\r
- else\r
- {\r
-// #ifdef SUPPORT_PPG_SERVICE\r
-// ppg_msg_callback_t func = ppg_get_send_msg_callback_func();\r
-// if(func != NULL)\r
-// {\r
-// //为了主界面脱腕灵敏;需要增加未佩戴\r
-// func(PPG_MSG_TYPE_HRS_UPDATE, &data, 1);\r
-// func(PPG_MSG_TYPE_WEARSTAT_UPDATE, &data,1);\r
-// func(PPG_MSG_TYPE_WEARSTAT_UPDATE,&data,1);\r
-// func(PPG_MSG_TYPE_WEARSTAT_UPDATE,&data,1);\r
-// }\r
-// #endif\r
- }\r
-\r
-} \r
-\r
-static void pah8009_wearstatus_update(int wear_status)\r
-{\r
- if(history_wear_status != wear_status)\r
- {\r
- if(uapi_dev_runtime_get_train_status())\r
- {\r
- // printf("wear_cnt : %d\n",wear_cnt);\r
- wear_cnt++;\r
- if(wear_cnt < 50)\r
- return;\r
- }\r
- wear_cnt = 0;\r
- history_wear_status = wear_status;\r
-#ifdef SUPPORT_CYWEE_SERVICE\r
- cywee_wear_status_update((uint8_t *)&history_wear_status);\r
-#endif\r
- if(history_wear_status != 1)\r
- {\r
- // pah8009_unwearstatus_update();\r
- avg_hrs = 0;\r
- }\r
- }\r
- else\r
- wear_cnt = 0;\r
-\r
-}\r
-\r
-static void bubblesort(float *input,int count) \r
-{\r
- int i,j;\r
- float sort_temp;\r
- for(i=0;i<count - 1;i++)\r
- {\r
- for(j=0;j<count-i - 1;j++) \r
- {\r
- if(input[j+1]<input[j])\r
- {\r
- sort_temp=input[j+1];\r
- input[j+1]=input[j];\r
- input[j]=sort_temp;\r
- }\r
- }\r
- }\r
-}\r
-\r
-void spo2_log_printf(char s, float f)\r
-{\r
- LOG_PRINT("%s= "LOG_FLOAT_MARKER" \n",s, LOG_FLOAT(f));\r
-\r
-}\r
-\r
-/*****************************\r
-FW FIFO Function\r
-*****************************/\r
-bool isFIFOEmpty(void)\r
-{\r
- return (_write_index == _read_index) ;\r
-}\r
-\r
-bool pah_push(ppg_mems_data_t *data) //Push return data must be true. If return false, please increase FIFO size. \r
-{\r
- int tmp = _write_index ;\r
- tmp++;\r
- if(tmp >= FIFO_SIZE)\r
- tmp = 0 ;\r
- if(tmp == _read_index)\r
- return false;\r
- ppg_mems_data_buf[tmp] = *data ;\r
- _write_index = tmp ;\r
- return true;\r
-}\r
-\r
-bool pah_pop(ppg_mems_data_t *data)\r
-{\r
- int tmp ;\r
- if(isFIFOEmpty())\r
- return false;\r
- tmp = _read_index + 1;\r
- if(tmp >= FIFO_SIZE)\r
- tmp = 0 ; \r
- _read_index = tmp ;\r
- *data = ppg_mems_data_buf[_read_index] ;\r
- \r
- return true;\r
-}\r
-\r
-\r
-//============================================================================\r
-// Public function\r
-//============================================================================\r
-void pah_sensor_init(reg_config_type type)\r
-{\r
- memset(&_main, 0, sizeof(_main));\r
-\r
- pah_800x_reg_setting reg_setting;\r
- memset(®_setting, 0, sizeof(reg_setting));\r
- \r
- if (!sensor_basic_verification())\r
- {\r
- // error_handle();\r
- LOG_PRINT("sensor_basic_verification failed\n");\r
- return;\r
- }\r
-\r
-\r
- // pah_800x\r
- pah_800x_config pah_800x_config;\r
- pah_800x_get_default_config(&pah_800x_config);\r
- LOG_PRINT("PPG_SETTING_VESION = %d \n" ,PPG_SETTING_VERSION);\r
- \r
-#if (ENABLE_AE==0)\r
- pah_800x_config.enable_ppg_convergence[0] = false;\r
- pah_800x_config.enable_ppg_convergence[1] = false;\r
- pah_800x_config.enable_ppg_convergence[2] = false;\r
-#endif\r
-\r
-#if (ENABLE_AE_RECONVERGENCE==0)\r
- pah_800x_config.enable_ppg_reconvergence_if_out_of_bounds[0] = false;\r
- pah_800x_config.enable_ppg_reconvergence_if_out_of_bounds[1] = false;\r
- pah_800x_config.enable_ppg_reconvergence_if_out_of_bounds[2] = false;\r
-#endif\r
-\r
- if (type == pc_4mm_2g_ir_hrd)\r
- {\r
-#if (PC_4MM_2G_IR_HRD==1)\r
- PAH_REG_ARR_SETUP(reg_setting.startup_init, PAH8009_REG_DEVICE_INIT);\r
- PAH_REG_ARR_SETUP(reg_setting.ppg_enable, PAH8009_REG_PC_4MM_2G_IR_PPG);\r
- PAH_REG_ARR_SETUP(reg_setting.touch_enable, PAH8009_REG_PC_4MM_2G_IR_TOUCH);\r
-#endif\r
- }\r
- else if (type == diw_4mm_g_ir_hrd)\r
- {\r
-#if (DIW_4MM_G_IR_HRD==1)\r
- PAH_REG_ARR_SETUP(reg_setting.startup_init, PAH8009_REG_DEVICE_INIT);\r
- PAH_REG_ARR_SETUP(reg_setting.ppg_enable, PAH8009_REG_DIW_4MM_G_IR_PPG);\r
- PAH_REG_ARR_SETUP(reg_setting.touch_enable, PAH8009_REG_DIW_4MM_G_IR_TOUCH);\r
-#endif\r
- }\r
- else if (type == diw_4mm_ir_r_ir_spo2)\r
- {\r
-#if (DIW_4MM_IR_R_IR_SPO2==1)\r
- for(int ch=0;ch<PAH_800x_MAX_CH_NUM;ch++)\r
- {\r
- pah_800x_config.ppg_convergence[ch].t_weight_normal_mode = 4;\r
- pah_800x_config.ppg_convergence[ch].t_weight_fast_mode = 8;\r
- pah_800x_config.ppg_convergence[ch].t_max = 150;\r
- }\r
- PAH_REG_ARR_SETUP(reg_setting.startup_init, PAH8009_REG_DEVICE_INIT);\r
- PAH_REG_ARR_SETUP(reg_setting.ppg_enable, PAH8009_REG_DIW_4MM_IR_R_IR_PPG);\r
- PAH_REG_ARR_SETUP(reg_setting.touch_enable, PAH8009_REG_DIW_4MM_IR_R_IR_TOUCH);\r
-#endif\r
- }\r
- else if (type == pc_22mm_2g_ir_hrd)\r
- {\r
-#if (PC_22MM_2G_IR_HRD==1)\r
- for(int ch=0;ch<PAH_800x_MAX_CH_NUM;ch++)\r
- {\r
- pah_800x_config.ppg_convergence[ch].t_max = 1280;\r
- pah_800x_config.ppg_convergence[ch].raw_outer_high_bound = 14848;\r
- pah_800x_config.ppg_convergence[ch].raw_inner_high_target = 13824;\r
- pah_800x_config.ppg_convergence[ch].raw_inner_low_target = 10752;\r
- pah_800x_config.ppg_convergence[ch].raw_outer_low_bound = 9728;\r
-\r
- }\r
- PAH_REG_ARR_SETUP(reg_setting.startup_init, PAH800x_REG_DEVICE_INIT);\r
- PAH_REG_ARR_SETUP(reg_setting.ppg_enable, PAH800x_REG_PC_22MM_2G_IR_PPG);\r
- PAH_REG_ARR_SETUP(reg_setting.touch_enable, PAH800x_REG_PC_22MM_2G_IR_TOUCH);\r
-#endif\r
- }\r
- else if (type == di_18mm_g_g_hrd)\r
- {\r
-#if (DI_18MM_G_G_HRD==1)\r
- for(int ch=0;ch<PAH_800x_MAX_CH_NUM;ch++)\r
- {\r
- pah_800x_config.ppg_convergence[ch].t_weight_normal_mode = 15;\r
- pah_800x_config.ppg_convergence[ch].t_weight_fast_mode = 25;\r
- pah_800x_config.ppg_convergence[ch].t_max = 2048;\r
- }\r
- PAH_REG_ARR_SETUP(reg_setting.startup_init, PAH8001_REG_DEVICE_INIT);\r
- PAH_REG_ARR_SETUP(reg_setting.ppg_enable, PAH8001_REG_DI_18MM_G_G_PPG);\r
- PAH_REG_ARR_SETUP(reg_setting.touch_enable, PAH8001_REG_DI_18MM_G_G_TOUCH);\r
-#endif\r
- }\r
-\r
- _main.pah_800x.is_powered= false ;\r
- pah_800x_init(&_main.pah_800x, &pah_800x_config);\r
- pah_800x_device_init(&_main.pah_800x, ®_setting);\r
-\r
-}\r
-\r
-void touch_only_sensor_start(void)\r
-{\r
- pah_800x_power_toggle(&_main.pah_800x, true);\r
- _main.is_Auto_mode = 0;\r
- touch_sensor_enable();\r
- _main.debug_print_header = true;\r
-\r
-}\r
-\r
-void ppg_only_sensor_start(void)\r
-{\r
- pah_800x_power_toggle(&_main.pah_800x, true);\r
-\r
- touch_sensor_enable();\r
- ppg_sensor_enable();\r
- _main.alg_is_enabled = MSG_ALG_ENABLE;\r
- \r
- \r
- //_main.ppg_only_is_enabled = 1 ;\r
- _main.is_Auto_mode = 0;\r
- _main.debug_print_header = true;\r
- \r
-}\r
-\r
-void auto_mode_sensor_start(void)\r
-{\r
- pah_800x_power_toggle(&_main.pah_800x, true);\r
- \r
- touch_sensor_enable();\r
- _main.is_Auto_mode = 1;\r
- _main.debug_print_header = true;\r
- \r
-}\r
-\r
-void pah_sensor_stop(void)\r
-{\r
- if (_main.ppg_is_enabled)\r
- {\r
- ppg_sensor_disable();\r
- }\r
- if (_main.touch_is_enabled)\r
- {\r
- touch_sensor_disable();\r
- }\r
-\r
- if(work_mode == 1)\r
- hr_algorithm_disable();\r
- else if(work_mode == 2)\r
- spo2_algorithm_disable();\r
-\r
- pah_800x_deinit(&_main.pah_800x); \r
-\r
- hr = 0; \r
- mySpO2 = 0;\r
-\r
- hrs_num = 0;\r
- sum_hrs = 0;\r
- memset(hrs_data,0,HRS_WINDOW_SIZE);\r
-\r
- left = 0;\r
- right = 0;\r
- avg_hrs = 0;\r
- wear_cnt = 0;\r
-}\r
-\r
-void ppg_interrupt_handle(void)\r
-{\r
- _main.has_interrupt = true;\r
- _main.interrupt_timestamp = pah_get_tick_count();\r
-}\r
-\r
-void ppg_sensor_task_interrupt(void)\r
-{\r
- if (_main.has_interrupt)\r
- {\r
- //LOG_PRINT("ppg_sensor_task_interrupt111111\n");\r
- _main.has_interrupt = false;\r
- _main.interrupt_timestamp = pah_get_tick_count();\r
- process(_main.interrupt_timestamp);\r
- }\r
-\r
-} \r
-\r
-void ppg_sensor_task_polling(void)\r
-{\r
- _main.interrupt_timestamp = pah_get_tick_count();\r
- process(_main.interrupt_timestamp);\r
-} \r
-\r
-uint8_t Check_alg_enable(void)\r
-{\r
- return _main.alg_is_enabled;\r
-}\r
-\r
-void Clear_alg_enable(void)\r
-{\r
- _main.alg_is_enabled = MSG_ALG_NOCHANGE;\r
-}\r
-\r
-\r
-// HR\r
-void hr_algorithm_enable(void)\r
-{\r
- if (_main.hr_is_enabled)\r
- return;\r
-\r
- // static Hr-V301009\r
- \r
- PxiAlg_Close();\r
- int version = PxiAlg_Version();\r
- DEBUG_PRINT("HR_Version = %d \r\n", version);\r
- \r
-\r
- uint32_t Query_Mem_Size = PxiAlg_Query_Mem_Size();\r
- DEBUG_PRINT("HR_Mem_Size = %d \r\n", Query_Mem_Size);\r
- \r
- hr_pBuffer = malloc(Query_Mem_Size);\r
- \r
- \r
- if(!PxiAlg_Open_Mem(hr_pBuffer))\r
- DEBUG_PRINT("PxiAlg_Open_Mem fail \n");\r
- \r
- \r
- PxiAlg_SetQualityThreshold(0.35f); \r
- PxiAlg_SetHRSmoothWeight(0.9f); \r
- PxiAlg_SetProcWindow(5.0f, 5.0f); \r
- PxiAlg_SetTimeoutLength(30.0f);\r
- PxiAlg_SetMemsScale(3); //MEMS 2/4/8/16G : 0/1/2/3\r
- PxiAlg_SetSkipSampleNum(10); \r
- PxiAlg_SetMemsScaleBase(3, 0); //MEMS 2/4/8/16G : 0/1/2/3\r
- PxiAlg_SetForceOutputLink(10, 150, 40, 1, 0.9f); \r
- PxiAlg_SetNormalOutput(200, 40, 0); \r
- PxiAlg_SetForceRipple(3, 1); \r
- //PxiAlg_SetForceOutput(30, 120, 45);\r
- //PxiAlg_SetForceOutputTime(10);\r
- PxiAlg_SetPackage(0, 1);\r
-\r
- _main.hr_is_enabled = true;\r
-}\r
-\r
-void hr_algorithm_disable(void)\r
-{\r
- if (!_main.hr_is_enabled)\r
- return;\r
-\r
- PxiAlg_Close();\r
- free(hr_pBuffer);\r
- hr = 0.0f;\r
- hr_grader =0.0f;\r
- _main.hr_is_enabled = false;\r
-}\r
-\r
-static void get_avg_hrs(uint8_t hrs)\r
-{\r
-\r
-\r
- if (hrs_num >= HRS_WINDOW_SIZE) {\r
- sum_hrs -= hrs_data[left];\r
- left = (left + 1) % HRS_WINDOW_SIZE;\r
- hrs_num--;\r
- }\r
-\r
- printf("PAH:hrs:%d\n",hrs);\r
-\r
- hrs_data[right] = hrs;\r
- sum_hrs += hrs;\r
- right = (right + 1) % HRS_WINDOW_SIZE;\r
- hrs_num++;\r
-\r
-\r
- if (hrs_num > 2) \r
- {\r
-\r
- uint8_t first_idx = left;\r
- int32_t min_hrs = hrs_data[first_idx];\r
- int32_t max_hrs = hrs_data[first_idx];\r
-\r
-\r
- for (uint8_t i = 0; i < hrs_num; i++) {\r
- uint8_t current_idx = (left + i) % HRS_WINDOW_SIZE;\r
- uint8_t current_hrs = hrs_data[current_idx];\r
-\r
- printf("current_hrs: %d\n", current_hrs);\r
-\r
- if (current_hrs < 40 || current_hrs > 180) {\r
- printf("PAH:无效数据过滤: %d\n", current_hrs);\r
- continue;\r
- }\r
-\r
- if (current_hrs < min_hrs) {\r
- min_hrs = current_hrs;\r
- }\r
- if (current_hrs > max_hrs) {\r
- max_hrs = current_hrs;\r
- }\r
- }\r
-\r
- uint8_t valid_count = hrs_num - 2;\r
- avg_hrs = (sum_hrs - max_hrs - min_hrs) / valid_count;\r
-\r
- printf("sum_hrs[%d],max_hrs[%d],min_hrs[%d],avg_hrs[%d]\n",sum_hrs,max_hrs,min_hrs,avg_hrs);\r
- }\r
-}\r
-\r
-\r
-void hr_alg_task(void)\r
-{\r
- if(Check_alg_enable())\r
- {\r
- if(Check_alg_enable()==MSG_ALG_ENABLE)\r
- {\r
- hr_algorithm_enable();\r
- }else if(Check_alg_enable()==MSG_ALG_DISABLE)\r
- {\r
- hr_algorithm_disable();\r
- }\r
- Clear_alg_enable();\r
- }\r
-\r
- if(!isFIFOEmpty())\r
- {\r
- if(pah_pop(&ppg_mems_data_alg)) //Get data from FIFO\r
- {\r
- if(_main.hr_is_enabled)\r
- { \r
- ppg_mems_data_alg.ppg_data.has_overflow = 0;\r
- ppg_mems_data_alg.ppg_data.overflow_num = 0;\r
-\r
- \r
-\r
- // 13Bytes format to hr lib;\r
- sRaw_Data_13Bytes hr_data;\r
-\r
- memset(&hr_data, 0, sizeof(hr_data));\r
- hr_data.loss = (uint8_t)ppg_mems_data_alg.ppg_data.overflow_num + 1;\r
- hr_data.rawData = ppg_mems_data_alg.ppg_data.data[1];\r
- hr_data.non_zero1 = 1;\r
- hr_data.non_zero2 = 2;\r
- hr_data.non_zero3 = 3;\r
- hr_data.frame_count = ppg_mems_data_alg.ppg_frame_count;\r
- hr_data.time = ppg_mems_data_alg.duration;\r
- hr_data.rsv = 0;\r
- hr_data.touch = (ppg_mems_data_alg.touch_flag > 0 ? 0x80 : 0x00);\r
- hr_data.rsv2 = 0;\r
-\r
- int32_t ret = PxiAlg_Process_Expo((uint8_t*)&hr_data, ppg_mems_data_alg.MEMS_Data, ppg_mems_data_alg.ppg_data.exposure_time[1]);\r
- \r
-\r
- if(PxiAlg_GetHrUpdateFlag())\r
- {\r
- PxiAlg_HrGet(&hr);\r
- PxiAlg_GetSigGrade(&hr_grader);\r
- }\r
- if(ppg_mems_data_alg.touch_flag==0)\r
- {\r
- hr = 0 ;\r
- hr_grader = 0;\r
- }\r
-\r
- // if(hr > 0)\r
- // printf("hr:%f\n",hr);\r
-#ifdef SUPPORT_PPG_SERVICE\r
- time_cnt++;\r
-\r
-// if(time_cnt > 15)\r
-// {\r
-// #ifdef SUPPORT_CYWEE_SERVICE\r
-// do_cywee_service_check_msg(CYWEE_SERVICE_CMD_CHECK_ACC, NULL); // 检查gsensor是否停止\r
-// #endif\r
-// }\r
-\r
-\r
- if(time_cnt >= 20) \r
- {\r
- if(ppg_get_factory_log_switch())\r
- {\r
- printf("green data : %d\n",factory_data);\r
- }\r
-\r
- time_cnt = 0;\r
- // ppg_msg_callback_t func = ppg_get_send_msg_callback_func();\r
-// #ifdef SUPPORT_CYWEE_SERVICE\r
-// // if(uapi_dev_runtime_get_train_status())\r
-// {\r
-// do_cywee_service_check_msg(CYWEE_SERVICE_CMD_CHECK_ACC, NULL); // 检查gsensor是否停止\r
-// }\r
-// #endif\r
- if(hr > 0)\r
- {\r
- int hr_int = round(hr);\r
- if (hr_int >= 30 && hr_int <= 210) \r
- {\r
- avg_hrs = (uint8_t)hr_int; // 安全转换\r
- }\r
- }\r
-\r
-\r
- // if(data > 30 && data < 220)\r
- // get_avg_hrs(data);\r
-\r
-\r
- LOG_PRINT("hr : %f avg_hrs : %d\n",hr,avg_hrs);\r
- // printf("func(PPG_MSG_TYPE_HRS_UPDATE, &avg_hrs, 1); :%d\n",avg_hrs);\r
- // func(PPG_MSG_TYPE_HRS_UPDATE, &avg_hrs, 1);\r
-\r
- // data = cywee_wear_status;\r
- // func(PPG_MSG_TYPE_WEARSTAT_UPDATE, &data, 1);\r
-\r
-#ifdef SUPPORT_PPG_BLE_SERVICE\r
- if(uapi_dev_runtime_get_hrs_ble_status())\r
- {\r
- uint8_t ble_data = (uint8_t)avg_hrs;\r
- ble_ppgtest_server_send_input_report(&ble_data,1);\r
- }\r
-#endif\r
-\r
- }\r
-\r
- \r
-\r
- \r
-#endif\r
- report_hr_data(&ppg_mems_data_alg, ret, hr, hr_grader);\r
-\r
- \r
- }\r
- else\r
- {\r
- report_ppg_data(&ppg_mems_data_alg);\r
- }\r
- }\r
- }\r
-}\r
-\r
-// HRD- ALG- Task2\r
-void hr_alg_task2(uint8_t ppg_num)\r
-{\r
-#ifdef PPG_MEMS_Synchronize\r
- if(Check_alg_enable())\r
- {\r
- if(Check_alg_enable()==MSG_ALG_ENABLE)\r
- {\r
- hr_algorithm_enable();\r
- }else if(Check_alg_enable()==MSG_ALG_DISABLE)\r
- {\r
- hr_algorithm_disable();\r
- }\r
- Clear_alg_enable();\r
- }\r
- \r
- if(!isFIFOEmpty())\r
- {\r
- for (int i=0; i<ppg_num; i++)\r
- {\r
- \r
- if(pah_pop(&ppg_mems_data_alg)) //Get data from FIFO\r
- {\r
- if(_main.hr_is_enabled)\r
- {\r
- ppg_mems_data_alg.MEMS_Data[0]=mems_int_data_out[3*i];\r
- ppg_mems_data_alg.MEMS_Data[1]=mems_int_data_out[3*i+1];\r
- ppg_mems_data_alg.MEMS_Data[2]=mems_int_data_out[3*i+2];\r
- \r
- // 13Bytes format to hr lib;\r
- sRaw_Data_13Bytes hr_data;\r
- \r
- memset(&hr_data, 0, sizeof(hr_data));\r
- hr_data.loss = (uint8_t)ppg_mems_data_alg.ppg_data.overflow_num + 1;\r
- hr_data.rawData = ppg_mems_data_alg.ppg_data.data[1];\r
- hr_data.non_zero1 = 1;\r
- hr_data.non_zero2 = 2;\r
- hr_data.non_zero3 = 3;\r
- hr_data.frame_count = ppg_mems_data_alg.ppg_frame_count;\r
- hr_data.time = ppg_mems_data_alg.duration;\r
- hr_data.rsv = 0;\r
- hr_data.touch = (ppg_mems_data_alg.touch_flag > 0 ? 0x80 : 0x00);\r
- hr_data.rsv2 = 0;\r
- \r
- int32_t ret = PxiAlg_Process_Expo((uint8_t*)&hr_data, ppg_mems_data_alg.MEMS_Data, ppg_mems_data_alg.ppg_data.exposure_time[1]);\r
- \r
-\r
- if(PxiAlg_GetHrUpdateFlag())\r
- {\r
- PxiAlg_HrGet(&hr);\r
- PxiAlg_GetSigGrade(&hr_grader);\r
- }\r
- \r
- report_hr_data(&ppg_mems_data_alg, ret, hr, hr_grader);\r
- \r
- }\r
- else\r
- {\r
- report_ppg_data(&ppg_mems_data_alg);\r
- }\r
- }\r
- }\r
- }\r
-#endif\r
-}\r
-\r
-\r
-\r
-// SPO2\r
-void spo2_algorithm_enable(void)\r
-{\r
- if (_main.spo2_is_enabled)\r
- {\r
- return;\r
- }\r
-\r
-\r
- int version = SpO2_Version();\r
- DEBUG_PRINT("PxiAlg_Version() = %d\n", version);\r
- \r
- uint32_t buffer_size = SpO2_OpenSize();\r
- DEBUG_PRINT("SpO2_Open buffer_size = %d \n", buffer_size);\r
-\r
- pBuffer = malloc(buffer_size);\r
- \r
- if (SpO2_Open(pBuffer) != SPO2_MSG_SUCCESS)\r
- {\r
- DEBUG_PRINT("SpO2_Open() failed. \n");\r
- free(pBuffer);\r
- return;\r
- }\r
- int8_t alg_ch[2] = {0, 1}; \r
-\r
- SpO2_SetChMapping(alg_ch, 2);\r
- SpO2_SetInputRatioHighBound(94.0f);\r
- SpO2_SetInputRatioLowBound(87.0f);\r
- SpO2_SetInputModelScale(0.50f);\r
-\r
- SpO2_SetInputCoefHigh0(-36877.85546875f);\r
- SpO2_SetInputCoefHigh1(1135.91394042969f);\r
- SpO2_SetInputCoefHigh2(-11.6429319381714f);\r
- SpO2_SetInputCoefHigh3(0.0398150011897087f);\r
-\r
- SpO2_SetInputCoefMid0(-1430.70520019531f);\r
- SpO2_SetInputCoefMid1(50.1599006652832f);\r
- SpO2_SetInputCoefMid2(-0.567422986030579f);\r
- SpO2_SetInputCoefMid3(0.00219199992716312f);\r
-\r
- SpO2_SetInputCoefLow0(-8.19796657562256f);\r
- SpO2_SetInputCoefLow1(1.74200701713562f);\r
- SpO2_SetInputCoefLow2(-0.0173850003629923f);\r
- SpO2_SetInputCoefLow3(0.000105999999505002f);\r
-\r
- SpO2_SetIIRFilterRatio(0.5f);\r
- SpO2_SetInputCorrelationTh(0.850000078678131f);\r
- SpO2_SetInputRatioLow(0.05f); \r
-\r
- SpO2_SetInputFlagCheckHr(1);\r
- SpO2_SetMotionDetectionLevel(2.0f);\r
- SpO2_Set_Update_Sec(1);\r
-\r
- SpO2_Set_Init_Low_SpO2_Constrain(0.0); \r
- SpO2_SetOutputHighBound(100.0f);\r
- SpO2_SetOutputLowBound(60.0f);\r
- SpO2_Set_Keep_Init_SpO2_Bound(95.0f); \r
- SpO2_Set_Keep_Init_SpO2_Count_Th(18.0f);\r
- SpO2_Set_SpO2_Bias_Value(0.5f); \r
- SpO2_Set_SpO2_Outlier_Smooth_Coef(5.5f); \r
- SpO2_Set_SpO2_Decrease_Coef(0.5f); \r
- SpO2_SetSkipDataLen(25);\r
-\r
- SpO2_Set_Flag_Hold_Breathe_Mode(0);\r
- SpO2_Set_Input_Correlation_Th2(0.85000f);\r
- SpO2_Set_Correlation_Th2_Count(30.0f);\r
- SpO2_Set_Init_Low_SpO2_Th(94.5f);\r
- SpO2_Set_Motion_Rms_Th(3000.0f);\r
- SpO2_Set_Motion_Rms_Th2(1500.0f);\r
- SpO2_Set_Motion_MEMS_Th(0.0f); \r
- SpO2_Set_T1(0.85f);\r
- SpO2_Set_Spo2_Force_Output_Second(15.0f);\r
-\r
- Spo2_Set_Force_Output_Value(97.50f); // Good Signal Force SpO2\r
- Spo2_Set_Force_Output_Value2(95.00f); // Bad Signal Force SpO2\r
-\r
- SpO2_Set_Init_Low_SpO2_Check_Sec(20);\r
- Spo2_Set_SpO2_First_Smooth_Th(95.0f);\r
- Spo2_Set_SpO2_First_Smooth_Sec(15);\r
- Spo2_Set_SpO2_Correlation_Smooth_Th(0.85f);\r
- SpO2_Set_Signal_Periodicity_Th(0.50f);\r
- \r
- // static Hr-V301009\r
- PxiAlg_Close();\r
- version = PxiAlg_Version();\r
- DEBUG_PRINT("HR_Version = %d \r\n", version);\r
- \r
- \r
- uint32_t Query_Mem_Size = PxiAlg_Query_Mem_Size();\r
- DEBUG_PRINT("HR_Mem_Size = %d \r\n", Query_Mem_Size);\r
- \r
- hr_pBuffer = malloc(Query_Mem_Size);\r
- \r
- \r
- if(!PxiAlg_Open_Mem(hr_pBuffer))\r
- DEBUG_PRINT("PxiAlg_Open_Mem fail \n");\r
- \r
- \r
- PxiAlg_SetQualityThreshold(0.35f); \r
- PxiAlg_SetHRSmoothWeight(0.9f); \r
- PxiAlg_SetProcWindow(5.0f, 5.0f); \r
- PxiAlg_SetTimeoutLength(30.0f);\r
- PxiAlg_SetMemsScale(1); //MEMS 2/4/8/16G : 0/1/2/3\r
- PxiAlg_SetSkipSampleNum(10); \r
- PxiAlg_SetMemsScaleBase(1, 0); //MEMS 2/4/8/16G : 0/1/2/3\r
- PxiAlg_SetForceOutputLink(15, 90, 60, 1, 0.9f); \r
- PxiAlg_SetNormalOutput(200, 40, 1); \r
- PxiAlg_SetForceRipple(3, 1); \r
- PxiAlg_SetPackage(0, 1);\r
- \r
- myHR = 0.0f;\r
- mySpO2 = 0.0f;\r
- correlation = 0.0f;\r
- trust_flag = 0.0f;\r
- MEMS_RMS = 0.0f;\r
- mySPO2_ReadyFlag = 0;\r
- hr_grader = 0.0f;\r
- data_buf_count_spo2_alg=0;\r
- no_value_reason = 0 ;\r
- SignalPeriodicity=0.0f ;\r
- time_accu = 0 ;\r
- count = 0 ;\r
- memset(spo2_array, 0, sizeof(*spo2_array));\r
- _main.spo2_is_enabled = true;\r
-}\r
-\r
-void spo2_algorithm_disable(void)\r
-{\r
- if (!_main.spo2_is_enabled)\r
- return;\r
- PxiAlg_Close();\r
- SpO2_Close();\r
- free(pBuffer);\r
- free(hr_pBuffer);\r
- myHR = 0.0f;\r
- mySpO2 = 0.0f;\r
- correlation = 0.0f;\r
- trust_flag = 0.0f;\r
- MEMS_RMS = 0.0f;\r
- mySPO2_ReadyFlag = 0;\r
- hr_grader = 0.0f;\r
- data_buf_count_spo2_alg=0;\r
- \r
- no_value_reason = 0 ;\r
- SignalPeriodicity=0.0f ;\r
- time_accu = 0 ;\r
- \r
- \r
- _main.spo2_is_enabled = false;\r
-}\r
-\r
-void spo2_alg_task(void)\r
-{\r
- if(Check_alg_enable())\r
- {\r
- if(Check_alg_enable()==MSG_ALG_ENABLE)\r
- {\r
- spo2_algorithm_enable();\r
- }else if(Check_alg_enable()==MSG_ALG_DISABLE)\r
- {\r
- spo2_algorithm_disable();\r
- }\r
- Clear_alg_enable();\r
- }\r
-\r
- if(!isFIFOEmpty())\r
- {\r
- if(pah_pop(&ppg_mems_data_alg)) //Get data from FIFO\r
- {\r
- if(_main.spo2_is_enabled)\r
- {\r
- // 13Bytes format to hr lib;\r
- \r
- int32_t RAW_DATA[2] ;\r
- sRaw_Data_13Bytes hr_data;\r
- \r
- memset(&hr_data, 0, sizeof(hr_data));\r
- hr_data.loss = (uint8_t)ppg_mems_data_alg.ppg_data.overflow_num + 1;\r
- hr_data.rawData = ppg_mems_data_alg.ppg_data.data[0];\r
- hr_data.non_zero1 = 1;\r
- hr_data.non_zero2 = 2;\r
- hr_data.non_zero3 = 3;\r
- hr_data.frame_count = ppg_mems_data_alg.ppg_frame_count;\r
- hr_data.time = ppg_mems_data_alg.duration;\r
- hr_data.rsv = 0;\r
- hr_data.touch = (ppg_mems_data_alg.touch_flag > 0 ? 0x80 : 0x00);\r
- hr_data.rsv2 = 0;\r
- \r
- \r
- int32_t ret = PxiAlg_Process_Expo((uint8_t*)&hr_data, ppg_mems_data_alg.MEMS_Data, ppg_mems_data_alg.ppg_data.exposure_time[0]);\r
- \r
-\r
- \r
-\r
- if(PxiAlg_GetHrUpdateFlag())\r
- {\r
- PxiAlg_HrGet(&myHR);\r
- PxiAlg_GetSigGrade(&hr_grader);\r
- }\r
-\r
-\r
- int32_t spo2_raw_1=ppg_mems_data_alg.ppg_data.data[0];\r
- int32_t spo2_raw_2=ppg_mems_data_alg.ppg_data.data[1];\r
- \r
- RAW_DATA[0]=spo2_raw_1;\r
- RAW_DATA[1]=spo2_raw_2;\r
-\r
- mems_data_raw[0].data[0]=ppg_mems_data_alg.MEMS_Data[0];\r
- mems_data_raw[0].data[1]=ppg_mems_data_alg.MEMS_Data[1] ;\r
- mems_data_raw[0].data[2]=ppg_mems_data_alg.MEMS_Data[2]; \r
-\r
- // SPO2 fill data on pah8series structure\r
- pxialg_data_spo2.touch_flag = ppg_mems_data_alg.touch_flag ;\r
- pxialg_data_spo2.time = ppg_mems_data_alg.duration ;;\r
- pxialg_data_spo2.ppg_data = (int32_t*)RAW_DATA;\r
- pxialg_data_spo2.nf_ppg_channel = 2;\r
- pxialg_data_spo2.nf_ppg_per_channel = 1;\r
- pxialg_data_spo2.frame_count = ppg_mems_data_alg.ppg_frame_count ;\r
- pxialg_data_spo2.nf_mems = 1;\r
- pxialg_data_spo2.mems_data = (int16_t*)mems_data_raw;\r
-\r
-\r
-\r
- // DEBUG_PRINT("data_buf_count_spo2_alg = %d \r\n", data_buf_count_spo2_alg);\r
-\r
-\r
- SpO2_SetHrValue(myHR);\r
- SpO2_SetMems(mems_data_raw, 1);\r
- //int ret_value = SpO2_Process(ppg_mems_data_alg.touch_flag , 2, pxialg_data_spo2.ppg_data, 1);\r
- int ret_value = SpO2_Process(ppg_mems_data_alg.touch_flag , 2, pxialg_data_spo2.ppg_data, 1,pxialg_data_spo2.time );\r
- \r
- SpO2_GetSpO2(&mySpO2);\r
- \r
- // if(mySpO2 > 0)\r
- // printf("SpO2 : %f\n",mySpO2);\r
-#ifdef SUPPORT_PPG_SERVICE\r
- time_cnt++;\r
-\r
- if(time_cnt >= 20) \r
- {\r
- if(ppg_get_factory_log_switch())\r
- {\r
- printf("red data : %d\n",factory_data);\r
- }\r
-\r
- time_cnt = 0;\r
- // ppg_msg_callback_t func = ppg_get_send_msg_callback_func();\r
-// #ifdef SUPPORT_CYWEE_SERVICE\r
-// // if(uapi_dev_runtime_get_train_status())\r
-// {\r
-// do_cywee_service_check_msg(CYWEE_SERVICE_CMD_CHECK_ACC, NULL); // 检查gsensor是否停止\r
-// }\r
-// #endif\r
- }\r
- // ppg_msg_callback_t func = ppg_get_send_msg_callback_func();\r
-\r
- // if(func != NULL && time_cnt >= 25)\r
- // {\r
- // uint8_t data = (uint8_t)mySpO2;\r
- // func(PPG_MSG_TYPE_SPO2_UPDATE, &data, 1);\r
- // time_cnt = 0;\r
- // }\r
-#endif\r
-\r
- SpO2_GetReadyFlag(&mySPO2_ReadyFlag);\r
- SpO2_GetInfo(&_spo2_detect_item);\r
- correlation =_spo2_detect_item.Noise;\r
- trust_flag =_spo2_detect_item.Output[3];\r
- no_value_reason=_spo2_detect_item.Output[4];\r
- SpO2_GetPeriodicity(&SignalPeriodicity);\r
- MEMS_RMS =_spo2_detect_item.RMS;\r
- \r
-\r
- data_buf_count_spo2_alg++;\r
- \r
- #if defined(Optimize_SPO2_flow) \r
- time_accu=time_accu+pxialg_data_spo2.time;\r
- #endif\r
- \r
- if( (ret_value == 0 ) && (data_buf_count_spo2_alg>=25) )\r
- { \r
- \r
- #if defined(Optimize_SPO2_flow)\r
- //time_accu=time_accu+pxialg_data->time;\r
- DEBUG_PRINT("SPO2 Test Time = %d ,count =%d\n",time_accu,count);\r
- if(time_accu>=60000)\r
- {\r
- spo2_array[count]=mySpO2;\r
- count=count+1;\r
- \r
- LOG_PRINT("Over_30_sec_Stop_measurement\n");\r
- LOG_PRINT("This_meausrement_suggest_spo2_value is");\r
- \r
- bubblesort(spo2_array,count);\r
- \r
- if(count % 2 == 0)\r
- {\r
- //log_printf(" "LOG_FLOAT_MARKER" \n", LOG_FLOAT(spo2_array[count/2]));\r
- LOG_PRINT(" "LOG_FLOAT_MARKER" \n",LOG_FLOAT((spo2_array[(int)(count/2)] + spo2_array[(int)(count/2) - 1]) / 2.0f)); \r
- }\r
- else\r
- {\r
- LOG_PRINT(" "LOG_FLOAT_MARKER" \n", LOG_FLOAT(spo2_array[(int)(count/2)]*1.0f));\r
- }\r
- \r
- //log_pah8series_no_touch();\r
- spo2_algorithm_disable();\r
- pah_sensor_stop();\r
- count=0;\r
- //add sensor stop here \r
- }\r
- else\r
- {\r
- if(mySpO2>0)\r
- {\r
- spo2_array[count]=mySpO2;\r
- count=count+1; \r
- } \r
- }\r
- #endif\r
- //report_spo2_data(&ppg_mems_data_alg, ret, mySpO2, myHR, correlation, trust_flag, MEMS_RMS,data_buf_count_spo2_alg, Opt_SpO2, drop_flag, ascend_flag, mems_fla);\r
- report_spo2_data(&ppg_mems_data_alg, ret, mySpO2, myHR, correlation, trust_flag, MEMS_RMS,data_buf_count_spo2_alg, no_value_reason, SignalPeriodicity, time_accu);\r
- data_buf_count_spo2_alg = data_buf_count_spo2_alg -25;\r
- }\r
- else\r
- {\r
- //report_spo2_data(&ppg_mems_data_alg, ret, mySpO2, myHR, correlation, trust_flag, MEMS_RMS,data_buf_count_spo2_alg, Opt_SpO2, drop_flag, ascend_flag, mems_flag);\r
- report_spo2_data(&ppg_mems_data_alg, ret, mySpO2, myHR, correlation, trust_flag, MEMS_RMS,data_buf_count_spo2_alg, no_value_reason, SignalPeriodicity, time_accu);\r
-\r
- switch (ret_value)\r
- {\r
- case SPO2_MSG_ALG_NOT_OPEN:\r
- DEBUG_PRINT("Algorithm is not initialized. \n");\r
- break;\r
- \r
- case SPO2_MSG_ALG_REOPEN:\r
- DEBUG_PRINT("Algorithm is re initialized. \n");\r
- break;\r
- \r
- case SPO2_MSG_ALG_NOT_SUPPORT:\r
- DEBUG_PRINT("Algorithm is not support. \n");\r
- break;\r
- \r
- default:\r
- // DEBUG_PRINT("Algorithm unhandle error = %d \n", ret_value);\r
- break;\r
- }\r
- //return 1 ;\r
- }\r
- }\r
- else\r
- {\r
- report_ppg_data(&ppg_mems_data_alg);\r
- }\r
- }\r
- }\r
-}\r
-\r
-\r
-\r
-// SPO2- ALG- Task2\r
-void spo2_alg_task2(uint8_t ppg_num)\r
-{\r
-#ifdef PPG_MEMS_Synchronize\r
- if(Check_alg_enable())\r
- {\r
- if(Check_alg_enable()==MSG_ALG_ENABLE)\r
- {\r
- spo2_algorithm_enable();\r
- }else if(Check_alg_enable()==MSG_ALG_DISABLE)\r
- {\r
- spo2_algorithm_disable();\r
- }\r
- Clear_alg_enable();\r
- }\r
- \r
- if(!isFIFOEmpty())\r
- {\r
- for (int k=0; k<ppg_num; k++)\r
- {\r
- if(pah_pop(&ppg_mems_data_alg)) //Get data from FIFO\r
- {\r
- if(_main.spo2_is_enabled)\r
- {\r
- // 13Bytes format to hr lib;\r
- \r
-\r
- ppg_mems_data_alg.MEMS_Data[0]=mems_int_data_out[3*k];\r
- ppg_mems_data_alg.MEMS_Data[1]=mems_int_data_out[3*k+1];\r
- ppg_mems_data_alg.MEMS_Data[2]=mems_int_data_out[3*k+2];\r
- \r
- int32_t RAW_DATA[2]={0} ;\r
- sRaw_Data_13Bytes hr_data;\r
- \r
- memset(&hr_data, 0, sizeof(hr_data));\r
- hr_data.loss = (uint8_t)ppg_mems_data_alg.ppg_data.overflow_num + 1;\r
- hr_data.rawData = ppg_mems_data_alg.ppg_data.data[0];\r
- hr_data.non_zero1 = 1;\r
- hr_data.non_zero2 = 2;\r
- hr_data.non_zero3 = 3;\r
- hr_data.frame_count = ppg_mems_data_alg.ppg_frame_count;\r
- hr_data.time = ppg_mems_data_alg.duration;\r
- hr_data.rsv = 0;\r
- hr_data.touch = (ppg_mems_data_alg.touch_flag > 0 ? 0x80 : 0x00);\r
- hr_data.rsv2 = 0;\r
- \r
- \r
- int32_t ret = PxiAlg_Process_Expo((uint8_t*)&hr_data, ppg_mems_data_alg.MEMS_Data, ppg_mems_data_alg.ppg_data.exposure_time[0]);\r
- \r
-\r
- \r
-\r
- if(PxiAlg_GetHrUpdateFlag())\r
- {\r
- PxiAlg_HrGet(&myHR);\r
- PxiAlg_GetSigGrade(&hr_grader);\r
- }\r
- \r
-\r
- int32_t spo2_raw_1=ppg_mems_data_alg.ppg_data.data[0];\r
- int32_t spo2_raw_2=ppg_mems_data_alg.ppg_data.data[1];\r
- \r
- RAW_DATA[0]=spo2_raw_1;\r
- RAW_DATA[1]=spo2_raw_2;\r
- \r
- mems_data_raw[0].data[0]=ppg_mems_data_alg.MEMS_Data[0];\r
- mems_data_raw[0].data[1]=ppg_mems_data_alg.MEMS_Data[1] ;\r
- mems_data_raw[0].data[2]=ppg_mems_data_alg.MEMS_Data[2]; \r
- \r
- // SPO2 fill data on pah8series structure\r
- pxialg_data_spo2.touch_flag = ppg_mems_data_alg.touch_flag ;\r
- pxialg_data_spo2.time = ppg_mems_data_alg.duration ;;\r
- pxialg_data_spo2.ppg_data = (int32_t*)RAW_DATA;\r
- pxialg_data_spo2.nf_ppg_channel = 2;\r
- pxialg_data_spo2.nf_ppg_per_channel = 1;\r
- pxialg_data_spo2.frame_count = ppg_mems_data_alg.ppg_frame_count ;\r
- pxialg_data_spo2.nf_mems = 1;\r
- pxialg_data_spo2.mems_data = (int16_t*)mems_data_raw;\r
- \r
-\r
-\r
- //DEBUG_PRINT("data_buf_count_spo2_alg = %d \r\n", data_buf_count_spo2_alg);\r
- \r
-\r
- SpO2_SetHrValue(myHR);\r
- SpO2_SetMems(mems_data_raw, 1);\r
- //int ret_value = SpO2_Process(ppg_mems_data_alg.touch_flag , 2, pxialg_data_spo2.ppg_data, 1);\r
- int ret_value = SpO2_Process(ppg_mems_data_alg.touch_flag , 2, pxialg_data_spo2.ppg_data, 1,pxialg_data_spo2.time );\r
- \r
- SpO2_GetSpO2(&mySpO2);\r
- SpO2_GetReadyFlag(&mySPO2_ReadyFlag);\r
- SpO2_GetInfo(&_spo2_detect_item);\r
- correlation =_spo2_detect_item.Noise;\r
- trust_flag =_spo2_detect_item.Output[3];\r
- no_value_reason=_spo2_detect_item.Output[4];\r
- SpO2_GetPeriodicity(&SignalPeriodicity);\r
- MEMS_RMS =_spo2_detect_item.RMS;\r
- \r
-\r
- data_buf_count_spo2_alg++;\r
- \r
- #if defined(Optimize_SPO2_flow) \r
- time_accu=time_accu+pxialg_data_spo2.time;\r
- #endif\r
- \r
- if( (ret_value == 0 ) && (data_buf_count_spo2_alg>=25) )\r
- { \r
- \r
- #if defined(Optimize_SPO2_flow)\r
- //time_accu=time_accu+pxialg_data->time;\r
- DEBUG_PRINT("SPO2 Test Time = %d ,count =%d\n",time_accu,count);\r
- if(time_accu>=31000)\r
- {\r
- spo2_array[count]=mySpO2;\r
- count=count+1;\r
- \r
- LOG_PRINT("Over_30_sec_Stop_measurement\n");\r
- LOG_PRINT("This_meausrement_suggest_spo2_value is");\r
- \r
- bubblesort(spo2_array,count);\r
- \r
- if(count % 2 == 0)\r
- {\r
- //log_printf(" "LOG_FLOAT_MARKER" \n", LOG_FLOAT(spo2_array[count/2]));\r
- LOG_PRINT(" "LOG_FLOAT_MARKER" \n",LOG_FLOAT((spo2_array[(int)(count/2)] + spo2_array[(int)(count/2) - 1]) / 2.0f)); \r
- }\r
- else\r
- {\r
- LOG_PRINT(" "LOG_FLOAT_MARKER" \n", LOG_FLOAT(spo2_array[(int)(count/2)]*1.0f));\r
- }\r
- \r
- //log_pah8series_no_touch();\r
- spo2_algorithm_disable();\r
- pah_sensor_stop();\r
- count=0;\r
- //add sensor stop here \r
- }\r
- else\r
- { \r
- if(mySpO2>0)\r
- {\r
- spo2_array[count]=mySpO2;\r
- count=count+1; \r
- }\r
- }\r
- #endif\r
- //report_spo2_data(&ppg_mems_data_alg, ret, mySpO2, myHR, correlation, trust_flag, MEMS_RMS,data_buf_count_spo2_alg, Opt_SpO2, drop_flag, ascend_flag, mems_fla);\r
- report_spo2_data(&ppg_mems_data_alg, ret, mySpO2, myHR, correlation, trust_flag, MEMS_RMS,data_buf_count_spo2_alg, no_value_reason, SignalPeriodicity, time_accu);\r
- data_buf_count_spo2_alg = data_buf_count_spo2_alg -25;\r
- }\r
- else\r
- {\r
- //report_spo2_data(&ppg_mems_data_alg, ret, mySpO2, myHR, correlation, trust_flag, MEMS_RMS,data_buf_count_spo2_alg, Opt_SpO2, drop_flag, ascend_flag, mems_flag);\r
- report_spo2_data(&ppg_mems_data_alg, ret, mySpO2, myHR, correlation, trust_flag, MEMS_RMS,data_buf_count_spo2_alg, no_value_reason, SignalPeriodicity, time_accu);\r
- \r
- switch (ret_value)\r
- {\r
- case SPO2_MSG_ALG_NOT_OPEN:\r
- DEBUG_PRINT("Algorithm is not initialized. \n");\r
- break;\r
- \r
- case SPO2_MSG_ALG_REOPEN:\r
- DEBUG_PRINT("Algorithm is re initialized. \n");\r
- break;\r
- \r
- case SPO2_MSG_ALG_NOT_SUPPORT:\r
- DEBUG_PRINT("Algorithm is not support. \n");\r
- break;\r
- \r
- default:\r
- DEBUG_PRINT("Algorithm unhandle error = %d \n", ret_value);\r
- break;\r
- }\r
- //return 1 ;\r
- }\r
- }\r
- else\r
- {\r
- report_ppg_data(&ppg_mems_data_alg);\r
- }\r
- }\r
- }\r
- }\r
-#endif\r
-}\r
-\r
-\r
-\r
-// Stress\r
-void stress_algorithm_enable(void)\r
-{\r
- int buffer_size = 0;\r
- if (_main.stress_is_enable)\r
- {\r
- return;\r
- }\r
-\r
- LOG_PRINT("HRVGetVersion() = %d \n", HRVGetVersion());\r
- buffer_size = HRVGetMemorySize(HRV_ENABLE_LHR, HRV_COLLECTION_SECONDS, HRV_SAMPLING_RATE);\r
- LOG_PRINT("HRVGetMemorySize(), open_size = %d \n", buffer_size);\r
-\r
- if (!buffer_size)\r
- {\r
- LOG_PRINT("HRVGetMemorySize(), failed to get memory size \n");\r
- return ;\r
- }\r
-\r
- stress_pBuffer = (uint8_t*)malloc(buffer_size);\r
- if (!stress_pBuffer)\r
- {\r
- LOG_PRINT("Out of memory \n");\r
- return ;\r
- }\r
-\r
- if (!HRVOpen(stress_pBuffer))\r
- {\r
- LOG_PRINT("HRVOpen() failed. \n");\r
- return ;\r
- }\r
- duration_total=0;\r
- _hrv_state.collect_duration = 0;\r
- \r
- _main.stress_is_enable = true;\r
-}\r
-\r
-void stress_algorithm_disable(void)\r
-{\r
- if (!_main.stress_is_enable)\r
- return;\r
- HRVClose();\r
- free(stress_pBuffer);\r
- \r
- duration_total = 0;\r
- SDNN_ReadyFlag = 0;\r
- Get_Stress = 0;\r
- Stress = 0.0f;\r
- HRAvg = 0;\r
- SQ_Stress = 0.0f;\r
- AnomalyFlag = 0;\r
- Stress_ReadyFlag = 0;\r
- Weak_Flag = 0; \r
- _main.stress_is_enable = false;\r
-}\r
-\r
-void stress_alg_task(int grade)\r
-{\r
- if(Check_alg_enable())\r
- {\r
- if(Check_alg_enable()==MSG_ALG_ENABLE)\r
- {\r
- stress_algorithm_enable();\r
- }else if(Check_alg_enable()==MSG_ALG_DISABLE)\r
- {\r
- stress_algorithm_disable();\r
- }\r
- Clear_alg_enable();\r
- }\r
-\r
- if(!isFIFOEmpty())\r
- {\r
- if(pah_pop(&ppg_mems_data_alg)) //Get data from FIFO\r
- {\r
- if(_main.stress_is_enable)\r
- {\r
- uint32_t raw_data = ppg_mems_data_alg.ppg_data.data[1];\r
- uint8_t frame_count = ppg_mems_data_alg.ppg_frame_count;\r
- uint8_t timeInterval = ppg_mems_data_alg.duration;\r
- uint8_t touch_flag = ppg_mems_data_alg.touch_flag;\r
-\r
- // If using Interrupt mode call this API\r
- HRVSetDiffTime(timeInterval,1);\r
- \r
- HRVProcess(touch_flag, raw_data);\r
-\r
- duration_total+=timeInterval;\r
- \r
- \r
- if(Stress_ReadyFlag==0)\r
- {\r
- Stress_ReadyFlag=HRVGetReadyFlag();\r
- }\r
- \r
- if((duration_total>25000)&& Stress_ReadyFlag==0)\r
- {\r
- LOG_PRINT("PPG Raw data is too Weak\r\n");\r
- LOG_PRINT("TEST Stop\r\n");\r
- stress_algorithm_disable();\r
- Weak_Flag=1;\r
- return ;\r
- } \r
- \r
- if(Stress_ReadyFlag == 1 || HRVGetReadyFlag())\r
- {\r
- Stress_ReadyFlag=1;\r
- _hrv_state.collect_duration += timeInterval;\r
- \r
- if(_hrv_state.collect_duration > (HRV_COLLECTION_SECONDS * 1000))\r
- {\r
- //int grade=100;\r
- HRAvg = HRVGetHRAvg(); \r
- Stress= HRVGetStress(grade); \r
- SQ_Stress=HRVGetRRIQuality(); \r
- AnomalyFlag= HRVGetAnomalyFlag();\r
- LOG_PRINT("HRAvg = %d Stress = %f HRV_Quality = %f AnomalyFlag= %d \n", HRAvg ,Stress,SQ_Stress,AnomalyFlag);\r
-\r
- if(SQ_Stress>0.4f)\r
- {\r
- LOG_PRINT("HRV_Quality Good\n");\r
- }\r
- else\r
- {\r
- LOG_PRINT("HRV_Quality Weak\n");\r
- }\r
- \r
- \r
- if(Stress >=80 && Stress<=100)\r
- {\r
- LOG_PRINT("Stress_Level High\n");\r
- }\r
- else if(Stress >=60 && Stress<80)\r
- {\r
- LOG_PRINT("Stress_Level Medium\n");\r
- }\r
- else if(Stress >=30 && Stress<60)\r
- {\r
- LOG_PRINT("Stress_Level Normal\n");\r
- }\r
- else\r
- {\r
- LOG_PRINT("Stress_Level Relaxed\n");\r
- } \r
- \r
- stress_algorithm_disable();\r
- _hrv_state.has_got_sdnn = 1;\r
- Get_Stress=1;\r
- return ;\r
- }\r
- } \r
- report_stress_data(&ppg_mems_data_alg,Stress_ReadyFlag, duration_total, _hrv_state.collect_duration );\r
- }\r
- else\r
- {\r
- report_ppg_data(&ppg_mems_data_alg);\r
- LOG_PRINT("Stress_Level Normal\n");\r
- }\r
- }\r
- }\r
-}\r
-\r
-\r
-\r
-\r
-//============================================================================\r
-// Private function\r
-//============================================================================\r
-static bool sensor_basic_verification(void)\r
-{\r
- uint16_t product_id = 0;\r
- pah_800x_get_product_id(&product_id);\r
- if (product_id != PAH_800x_PRODUCT_ID)\r
- {\r
- pah_800x_power_toggle(&_main.pah_800x, true);\r
- pah_800x_get_product_id( &product_id);\r
- }\r
-\r
- DEBUG_PRINT("product_id = 0x%04X\n", product_id);\r
- if (product_id != PAH_800x_PRODUCT_ID)\r
- {\r
- DEBUG_PRINT("Failed to verify product_id\n");\r
- return false;\r
- }\r
- \r
- return true;\r
-}\r
-\r
-static void process(uint64_t timestamp)\r
-{\r
- float mems_data[3] = { 0.0f, 0.0f, 0.0f };\r
-\r
- // Task\r
- pah_ret ret = pah_800x_task(&_main.pah_800x);\r
- if (PAH_FAILED(ret))\r
- {\r
- DEBUG_PRINT("pah_800x_task() failed, ret = %d\n", ret);\r
- return;\r
- }\r
-\r
- int8_t touch_flag = -1;\r
- pah_800x_get_touch_flag(&_main.pah_800x, &touch_flag);\r
-\r
- // PRINT("touch_flag : %d\n",touch_flag);\r
-\r
- pah8009_wearstatus_update(touch_flag);\r
-\r
- if (_main.ppg_is_enabled)\r
- {\r
- pah_800x_ppg_data ppg_data;\r
- pah_800x_ppg_get_data(&_main.pah_800x, &ppg_data);\r
-\r
- if (ppg_data.has_data)\r
- {\r
- // LOG_PRINT("ppg data %d %d %d %d %d %d %d %d\n",ppg_data.has_data,ppg_data.overflow_num,ppg_data.data[0],ppg_data.data[1],ppg_data.data[2],\r
- // ppg_data.exposure_time[0],ppg_data.exposure_time[1],ppg_data.exposure_time[2]);\r
- // LOG_PRINT("ppg data %d %d %d %d %d %d\n",ppg_data.frame_period[0],ppg_data.frame_period[1],ppg_data.frame_period[2]\r
- // ,ppg_data.adc[0],ppg_data.adc[1],ppg_data.adc[2]);\r
-\r
- // PRINT("ppg data : %d %d %d\n",ppg_data.data[0],ppg_data.data[1],ppg_data.data[2]);\r
-\r
- if(work_mode == 1)\r
- factory_data = ppg_data.data[1];\r
- else if(work_mode == 2)\r
- factory_data = ppg_data.data[0];\r
-\r
- uint64_t duration = timestamp - _main.ppg_prev_report_timestamp;\r
-\r
- if (!_main.ppg_has_prev_report_timestamp)\r
- {\r
- _main.ppg_has_prev_report_timestamp = true;\r
- duration = 50;\r
- }\r
- _main.ppg_prev_report_timestamp = timestamp;\r
-\r
- if (duration > UINT8_MAX)\r
- {\r
- DEBUG_PRINT("duration > UINT8_MAX !!!\n");\r
- return;\r
- }\r
- \r
- ppg_mems_data_t ppg_mems_data;\r
-\r
- /***********************G sensor explanation start***********************************/\r
- //If no G sensor, please set ppg_mems_data.MEMS_Data[3] = {0};\r
- //Please set G sensor default range = ±2G.\r
- //If G sensor output data format is not 16bit, please change to 16bit format.\r
- //For example: G sensor is 12bit\r
- //ReadGSensor(&mems_data[0],&mems_data[1],&mems_data[2])\r
- //MEMS_SensorX() absolute data must be largest when let hand down at static state.\r
- //MEMS_SensorZ() absolute data must be largest when let hand horizontal on the table at static state.\r
- /***********************G sensor explanation end***********************************/\r
-\r
- ReadGSensor(&mems_data[0],&mems_data[1],&mems_data[2]);\r
-\r
- ppg_mems_data.MEMS_Data[0] = mems_data[0] * 2048 / 10;//ReadGSensorX();\r
- ppg_mems_data.MEMS_Data[1] = mems_data[1] * 2048 / 10;//ReadGSensorY();\r
- ppg_mems_data.MEMS_Data[2] = mems_data[2] * 2048 / 10;//ReadGSensorZ();\r
-\r
- // ppg_mems_data.MEMS_Data[0] = mems_data[0] * 4096 / 10;//ReadGSensorX();\r
- // ppg_mems_data.MEMS_Data[1] = mems_data[1] * 4096 / 10;//ReadGSensorY();\r
- // ppg_mems_data.MEMS_Data[2] = mems_data[2] * 4096 / 10;//ReadGSensorZ();\r
-\r
- // printf("x:%f y:%f z:%f\n",ppg_mems_data.MEMS_Data[0],ppg_mems_data.MEMS_Data[1],ppg_mems_data.MEMS_Data[2]);\r
- ppg_mems_data.duration = duration;\r
- \r
- ppg_mems_data.ppg_data = ppg_data ;\r
-\r
- ppg_mems_data.touch_flag = history_wear_status ;\r
- ppg_mems_data.ppg_frame_count = _main.ppg_frame_count; //SD-revise-20200320\r
- \r
- pah_push(&ppg_mems_data);\r
-\r
-#ifdef PPG_MEMS_Synchronize \r
- time_temp+=duration;\r
- mems_data_polling_ready=0;\r
- if(time_temp>=Time_Interval)\r
- {\r
- mems_data_polling_ready=1;\r
- time_temp =0;\r
- }\r
- ppg_data_num++;\r
-#endif \r
- ++_main.ppg_frame_count;\r
- \r
- //report_ppg_data(&ppg_mems_data);\r
-\r
- }\r
- }\r
- \r
- if (_main.is_Auto_mode || _main.touch_is_enabled)//SD-reivse-20191205\r
- {\r
- if ( _main.touch_last_is_touched != history_wear_status)\r
- {\r
- _main.touch_last_is_touched = history_wear_status; \r
- \r
- report_touch_data(timestamp, &history_wear_status);\r
- \r
- bool is_touched = (history_wear_status >= 1);\r
-\r
- if (_main.is_Auto_mode && is_touched)//SD-revise-20191205\r
- { \r
- //touch_sensor_disable(); \r
- ppg_sensor_enable();\r
- _main.alg_is_enabled = MSG_ALG_ENABLE;\r
- }\r
- else if (_main.is_Auto_mode && !is_touched)\r
- {\r
- // pah8009_unwearstatus_update();\r
- ppg_sensor_disable(); \r
- pah_800x_update_touch_setting(&_main.pah_800x);\r
- //touch_sensor_enable();\r
- _main.alg_is_enabled = MSG_ALG_DISABLE; \r
- }\r
- }\r
- }\r
-}\r
-\r
-static void ppg_sensor_enable(void)\r
-{\r
- if (_main.ppg_is_enabled)\r
- return;\r
- \r
- pah_800x_ppg_toggle(&_main.pah_800x, true);\r
-\r
- _main.debug_print_header = true;\r
- _main.ppg_has_prev_report_timestamp = false;\r
- _main.ppg_prev_report_timestamp = pah_get_tick_count();\r
- _main.ppg_frame_count = 0;\r
- \r
- _main.ppg_is_enabled = true;\r
- \r
- _read_index = 0 ;\r
- _write_index = 0 ;\r
-#ifdef PPG_MEMS_Synchronize\r
- ppg_data_num = 0 ;\r
-#endif \r
- //LOG_PRINT("ppg_sensor_enable\n");\r
-}\r
-\r
-static void ppg_sensor_disable(void)\r
-{\r
- if (!_main.ppg_is_enabled)\r
- return;\r
- \r
- pah_800x_ppg_toggle(&_main.pah_800x, false);\r
-\r
- _main.ppg_is_enabled = false;\r
-}\r
-\r
-static void touch_sensor_enable(void)\r
-{\r
- if (_main.touch_is_enabled)\r
- return;\r
- \r
- pah_800x_touch_toggle(&_main.pah_800x, true);\r
-\r
- _main.touch_last_is_touched = 0;//default no touch => touch mode\r
- \r
- _main.touch_is_enabled = true;\r
-}\r
-\r
-static void touch_sensor_disable(void)\r
-{\r
- if (!_main.touch_is_enabled)\r
- return;\r
- \r
- pah_800x_touch_toggle(&_main.pah_800x, false);\r
-\r
- _main.touch_is_enabled = false;\r
-}\r
-\r
-static void report_touch_data(uint64_t timestamp, const int8_t *touch_flag)\r
-{\r
- LOG_PRINT("TOUCH: time = { %llu }, touch_flag = { %d } \n", timestamp, *touch_flag );\r
-}\r
-\r
-static void report_ppg_data(const ppg_mems_data_t *ppg_mems_data_alg)\r
-{\r
- if (_main.debug_print_header == true)\r
- {\r
- LOG_PRINT("Frame, Time, Touch_Flag, Data_Ready, Overflow_Flag, Overflow_Num, ppg0, ppg1, ppg2, Expo_A, Expo_B, Expo_C, MEMS_X, MEMS_Y, MEMS_Z\n");\r
- _main.debug_print_header = false;\r
- }\r
-\r
- LOG_PRINT("%d, %d, %d, %d, %d, %d, %d, %d, %d, %d, %d, %d, %d, %d, %d\n",\r
- ppg_mems_data_alg->ppg_frame_count, ppg_mems_data_alg->duration, ppg_mems_data_alg->touch_flag,\r
- ppg_mems_data_alg->ppg_data.has_data, ppg_mems_data_alg->ppg_data.has_overflow, ppg_mems_data_alg->ppg_data.overflow_num,\r
- ppg_mems_data_alg->ppg_data.data[0], ppg_mems_data_alg->ppg_data.data[1], ppg_mems_data_alg->ppg_data.data[2],\r
- ppg_mems_data_alg->ppg_data.exposure_time[0], ppg_mems_data_alg->ppg_data.exposure_time[1], ppg_mems_data_alg->ppg_data.exposure_time[2],\r
- (int)(ppg_mems_data_alg->MEMS_Data[0]), (int)(ppg_mems_data_alg->MEMS_Data[1]), (int)(ppg_mems_data_alg->MEMS_Data[2]));\r
-}\r
-\r
-static void report_hr_data(const ppg_mems_data_t *ppg_mems_data_alg, int32_t ret, float hr, float hr_grader)\r
-{\r
- if (_main.debug_print_header == true)\r
- {\r
- LOG_PRINT("Frame, Time, Touch_Flag, Data_Ready, Overflow_Flag, Overflow_Num, PPG0, PPG1, PPG2, Expo_A, Expo_B, Expo_C, MEMS_X, MEMS_Y, MEMS_Z, ret, hr, hr_grade\n");\r
-\r
- _main.debug_print_header = false;\r
- }\r
- \r
- LOG_PRINT("%d, %d, %d, %d, %d, %d, %d, %d, %d, %d, %d, %d, %d, %d, %d, %d, %f, %f\n",\r
- ppg_mems_data_alg->ppg_frame_count, ppg_mems_data_alg->duration, ppg_mems_data_alg->touch_flag,\r
- ppg_mems_data_alg->ppg_data.has_data, ppg_mems_data_alg->ppg_data.has_overflow, ppg_mems_data_alg->ppg_data.overflow_num,\r
- ppg_mems_data_alg->ppg_data.data[0], ppg_mems_data_alg->ppg_data.data[1], ppg_mems_data_alg->ppg_data.data[2],\r
- ppg_mems_data_alg->ppg_data.exposure_time[0], ppg_mems_data_alg->ppg_data.exposure_time[1], ppg_mems_data_alg->ppg_data.exposure_time[2],\r
- (int)(ppg_mems_data_alg->MEMS_Data[0]), (int)(ppg_mems_data_alg->MEMS_Data[1]), (int)(ppg_mems_data_alg->MEMS_Data[2]), ret, hr, hr_grader);\r
-\r
-}\r
-\r
-//static void report_spo2_data(const ppg_mems_data_t *ppg_mems_data_alg, int32_t ret, float mySpO2, float myHR, float correlation, float trust_flag, float MEMS_RMS, int data_buf_count_spo2_alg, , float Opt_SpO2, int drop_flag, int ascend_flag, int mems_flag)\r
-static void report_spo2_data(const ppg_mems_data_t *ppg_mems_data_alg, int32_t ret, float myHR, float mySpO2, float correlation, float trust_flag, float MEMS_RMS, int data_buf_count_spo2_alg, uint8_t no_value_reason, float SignalPeriodicity, int time_accu )\r
-{\r
- if (_main.debug_print_header == true)\r
- {\r
- \r
- LOG_PRINT("Frame, Time, Touch_Flag, Data_Ready, Overflow_Flag, Overflow_Num, PPG0, PPG1, PPG2, Expo_A, Expo_B, Expo_C, MEMS_X, MEMS_Y, MEMS_Z, ret, spo2, myHR, hr_grader, correlation, MEMS_RMS, trust, no_value_reason, SignalPeriodicity, time_accu \n"); \r
- _main.debug_print_header = false;\r
- }\r
-\r
- if(data_buf_count_spo2_alg>=25)\r
- {\r
- LOG_PRINT("%d, %d, %d, %d, %d, %d, %d, %d, %d, %d, %d, %d, %d, %d, %d, %d, %f, %f, %f, %f, %f, %f, %d, %f, %d\n",\r
- ppg_mems_data_alg->ppg_frame_count, ppg_mems_data_alg->duration, ppg_mems_data_alg->touch_flag,\r
- ppg_mems_data_alg->ppg_data.has_data, ppg_mems_data_alg->ppg_data.has_overflow, ppg_mems_data_alg->ppg_data.overflow_num,\r
- ppg_mems_data_alg->ppg_data.data[0], ppg_mems_data_alg->ppg_data.data[1], ppg_mems_data_alg->ppg_data.data[2],\r
- ppg_mems_data_alg->ppg_data.exposure_time[0], ppg_mems_data_alg->ppg_data.exposure_time[1], ppg_mems_data_alg->ppg_data.exposure_time[2],\r
- (int)(ppg_mems_data_alg->MEMS_Data[0]), (int)(ppg_mems_data_alg->MEMS_Data[1]), (int)(ppg_mems_data_alg->MEMS_Data[2]), ret, mySpO2,myHR,hr_grader,correlation, MEMS_RMS,trust_flag,no_value_reason,SignalPeriodicity, time_accu);\r
- }\r
- else\r
- {\r
- LOG_PRINT("%d, %d, %d, %d, %d, %d, %d, %d, %d, %d, %d, %d, %d, %d, %d, %d\n",\r
- ppg_mems_data_alg->ppg_frame_count, ppg_mems_data_alg->duration, ppg_mems_data_alg->touch_flag,\r
- ppg_mems_data_alg->ppg_data.has_data, ppg_mems_data_alg->ppg_data.has_overflow, ppg_mems_data_alg->ppg_data.overflow_num,\r
- ppg_mems_data_alg->ppg_data.data[0], ppg_mems_data_alg->ppg_data.data[1], ppg_mems_data_alg->ppg_data.data[2],\r
- ppg_mems_data_alg->ppg_data.exposure_time[0], ppg_mems_data_alg->ppg_data.exposure_time[1], ppg_mems_data_alg->ppg_data.exposure_time[2],\r
- (int)(ppg_mems_data_alg->MEMS_Data[0]), (int)(ppg_mems_data_alg->MEMS_Data[1]), (int)(ppg_mems_data_alg->MEMS_Data[2]), ret);\r
-\r
- \r
- }\r
-}\r
-\r
-\r
-static void report_stress_data(const ppg_mems_data_t *ppg_mems_data_alg, uint8_t Stress_ReadyFlag, uint32_t measuretime, float collect_time)\r
-{\r
- if (_main.debug_print_header == true)\r
- {\r
- LOG_PRINT("Frame, Time, Touch_Flag, Data_Ready, Overflow_Flag, Overflow_Num, PPG0, PPG1, PPG2, Expo_A, Expo_B, Expo_C, MEMS_X, MEMS_Y, MEMS_Z, Stress_Ready, measure_time, collect_time\n");\r
-\r
- _main.debug_print_header = false;\r
- }\r
- \r
- LOG_PRINT("%d, %d, %d, %d, %d, %d, %d, %d, %d, %d, %d, %d, %d, %d, %d, %d, %d, %f\n",\r
- ppg_mems_data_alg->ppg_frame_count, ppg_mems_data_alg->duration, ppg_mems_data_alg->touch_flag,\r
- ppg_mems_data_alg->ppg_data.has_data, ppg_mems_data_alg->ppg_data.has_overflow, ppg_mems_data_alg->ppg_data.overflow_num,\r
- ppg_mems_data_alg->ppg_data.data[0], ppg_mems_data_alg->ppg_data.data[1], ppg_mems_data_alg->ppg_data.data[2],\r
- ppg_mems_data_alg->ppg_data.exposure_time[0], ppg_mems_data_alg->ppg_data.exposure_time[1], ppg_mems_data_alg->ppg_data.exposure_time[2],\r
- (int)(ppg_mems_data_alg->MEMS_Data[0]), (int)(ppg_mems_data_alg->MEMS_Data[1]), (int)(ppg_mems_data_alg->MEMS_Data[2]), Stress_ReadyFlag,duration_total, _hrv_state.collect_duration );\r
-\r
-}\r
-#ifdef PPG_MEMS_Synchronize \r
-uint8_t get_ppg_data_num(void)\r
-{\r
- return ppg_data_num;\r
-}\r
-void clear_ppg_data_num(void)\r
-{\r
- ppg_data_num = 0;\r
-}\r
-\r
-void mems_interpolation(int16_t *mems_int_data_in,uint8_t mems_num,uint8_t ppg_num)\r
-{\r
- \r
- // Customer Define MEMS-input X, Y, Z\r
- // Put data in mems_int_data_in ,and mems_count\r
- // Then call the PXI_MEMS_interp to do interpolation \r
- \r
- PXI_MEMS_interp(mems_int_data_in, mems_num, mems_int_data_out, ppg_num);\r
- \r
-}\r
-#endif\r
-\r
-static void error_handle(void)\r
-{\r
- while (true)\r
- {\r
- }\r
-}\r
-\r
-\r
-\r
-uint16_t pah8009_get_chip_id(void)\r
-{\r
- uint16_t product_id = 0;\r
- pah_800x_get_product_id(&product_id);\r
- return product_id;\r
-}\r
-\r
-int32_t pah8009_get_factory_data(void)\r
-{\r
- int32_t data = factory_data;\r
- factory_data = 0;\r
- return data;\r
-}\r
-\r
-void pah8009_int_handler(void)\r
-{\r
- timeout = 1;\r
- ppg_interrupt_handle();\r
- ppg_sensor_task_interrupt();\r
- \r
- if(work_mode == 1)\r
- {\r
- // PRINT("hr_alg_task \n");\r
- hr_alg_task();\r
- } \r
- else if(work_mode == 2)\r
- {\r
- // PRINT("spo2_alg_task \n");\r
- spo2_alg_task();\r
- }\r
-\r
-}\r
-\r
-void pah8009_check_int(void)\r
-{\r
- if(work_mode > 3 || work_mode < 1)\r
- {\r
- return;\r
- }\r
-\r
- if(!timeout)\r
- {\r
- // printf("pah8009_check_int \n");\r
-#ifdef SUPPORT_PPG_SERVICE\r
- send_ppg_cmd_msg(PPG_SERVICE_CMD_TYPE_INT_HANDLER);\r
-#endif\r
-\r
- }\r
- else\r
- {\r
- timeout = 0;\r
- }\r
-}\r
-\r
-void pah8009_timer_handler(void)\r
-{\r
- // printf("pah8009_timer_handler \n");\r
- ppg_sensor_task_polling();\r
- \r
- if(work_mode == 1)\r
- {\r
- // PRINT("hr_alg_task \n");\r
- hr_alg_task();\r
- } \r
- else if(work_mode == 2)\r
- {\r
- // PRINT("spo2_alg_task \n");\r
- spo2_alg_task();\r
- }\r
-\r
-}\r
-\r
-// static uint32_t last_exec_time = 0;\r
-\r
-static void pah8009_timer_callback(void)\r
-{\r
- // printf("pah8009_timer_callback\n");\r
-#ifdef SUPPORT_PPG_SERVICE\r
- send_ppg_cmd_msg(PPG_SERVICE_CMD_TYPE_DO_PPG_HRS_TIMER_CALLBACK);\r
-#endif\r
- // pah8009_check_int();\r
-}\r
-\r
-static void pah8009_timer_init(void)\r
-{\r
- if(g_pah8009_timer == NULL)\r
- g_pah8009_timer = osTimerNew((osTimerFunc_t)pah8009_timer_callback, osTimerPeriodic, NULL, NULL);\r
-}\r
-\r
-\r
-void pah8009_reset(void)\r
-{\r
- pah_sensor_stop();\r
- pah_sensor_init(diw_4mm_g_ir_hrd);\r
- ppg_only_sensor_start();\r
-}\r
-\r
-void pah8009_control(char *mode,uint8_t enable)\r
-{\r
- // printf("mode : %s ,enable : %d\n",mode,enable);\r
- if(!enable)\r
- {\r
-\r
- if(g_pah8009_timer != NULL && work_mode < 4 && work_mode > 0)\r
- osTimerStop(g_pah8009_timer);\r
-\r
- pah_sensor_stop();\r
- work_mode = 0;\r
- if(uapi_dev_runtime_get_sleep_monitoring_status() && uapi_dev_runtime_get_hrs_passivity_flag())\r
- {\r
- pah8009_wearstatus_update(history_wear_status); \r
- uapi_dev_runtime_set_hrs_passivity(0); \r
- }\r
- }\r
- else\r
- {\r
- if (strcmp(mode, "hrs") == 0) {\r
- pah_sensor_init(diw_4mm_g_ir_hrd);\r
- // auto_mode_sensor_start();\r
- work_mode = 1;\r
- // touch_only_sensor_start();\r
- ppg_only_sensor_start();\r
- time_out = 15;\r
- } else if (strcmp(mode, "hrv") == 0) {\r
- // md = HRV_MODE;\r
- } else if (strcmp(mode, "spo2") == 0) {\r
- // md = SPO2_MODE;\r
- pah_sensor_init(diw_4mm_ir_r_ir_spo2);\r
- ppg_only_sensor_start();\r
- work_mode = 2;\r
- time_out = 15;\r
- } else if (strcmp(mode, "leaklight") == 0) {\r
- // md = FT_LEAK_LIGHT_MODE;\r
- factory_test_mode(factory_test_leak_light_target_sample, 0, 0, 0, 0);\r
- // extern void test_library_HRD(void);\r
- // test_library_HRD();\r
- work_mode = 4;\r
- } else if (strcmp(mode, "wear") == 0) {\r
- pah_sensor_init(diw_4mm_g_ir_hrd);\r
- touch_only_sensor_start();\r
- work_mode = 3;\r
- time_out = 5000;\r
- }\r
-\r
- if(work_mode < 4 && work_mode > 0)\r
- pah8009_timer_init();\r
-\r
- time_cnt = 0;\r
-\r
- if(g_pah8009_timer != NULL && work_mode < 4 && work_mode > 0)\r
- {\r
- // printf("time_out : %d\n",time_out);\r
- osTimerStart(g_pah8009_timer,time_out);\r
- }\r
- \r
-\r
- }\r
-}\r
-\r
-uint8_t pah8009_get_hrs(void)\r
-{\r
- return avg_hrs;\r
-}\r
-\r
-uint8_t pah8009_get_spo2(void)\r
-{\r
- return (uint8_t)mySpO2;\r
-}\r
-\r
-uint8_t pah8009_get_wear_status(void)\r
-{\r
- return history_wear_status;\r
+// pah
+#include "pah800x_main.h"
+//#include "pah_8009_reg_arr_default.h"
+#include "pxialg_spo2.h"
+#include "pxialg_hrv.h"
+#include "pxivar.h"
+#include "pah_8009_reg_arr_default_20220623.h"
+#include "pah800x_factory_test.h"
+#include "cmsis_os2.h"
+#include <math.h>
+
+#ifdef SUPPORT_PPG_SERVICE
+#include "ppg_service.h"
+#include "ppg_hal.h"
+#endif
+#ifdef SUPPORT_CYWEE_SERVICE
+#include "cywee_service.h"
+#endif
+#include "custom_runtime.h"
+#ifdef SUPPORT_PPG_BLE_SERVICE
+#include "ppgBleService.h"
+#endif
+
+#define HRS_WINDOW_SIZE 5
+//============================================================================
+// Static Global Variable
+//============================================================================
+static main_state _main;
+static ppg_mems_data_t ppg_mems_data_buf[FIFO_SIZE] ;
+static int _read_index = 0 ;
+static int _write_index = 0 ;
+
+// hr algotirhm
+static float hr = 0.0f;
+static float hr_grader =0.0f;
+static ppg_mems_data_t ppg_mems_data_alg;
+
+// spo2 algorithm
+void *pBuffer = NULL;
+void *hr_pBuffer = NULL;
+static float myHR = 0.0f;
+static float mySpO2 = 0.0f;
+static float correlation = 0.0f;
+static float trust_flag = 0.0f;
+static float MEMS_RMS = 0.0f;
+uint8_t mySPO2_ReadyFlag = 0 ;
+static uint8_t no_value_reason = 0 ;
+static float SignalPeriodicity=0.0f ;
+
+static int time_accu = 0 ;
+static int count =0;
+static float spo2_array[31]={0.0f};
+
+static uint32_t data_buf_count_spo2_alg=0;
+
+static SpO2_info_t _spo2_detect_item;
+pah8series_data_t pxialg_data_spo2;
+static SpO2_mems_t mems_data_raw[150];
+#ifdef PPG_MEMS_Synchronize
+//int16_t mems_int_data_in[90] ={0};
+int16_t mems_int_data_out[90]={0};
+int time_temp=0;
+static uint8_t ppg_data_num=0;
+uint8_t mems_data_polling_ready = 0 ;
+#endif
+
+// Stress Algorithm
+static hrv_state_s _hrv_state;
+void *stress_pBuffer = NULL;
+#define HRV_ENABLE_LHR 1
+#define HRV_SAMPLING_RATE 20
+#define HRV_COLLECTION_SECONDS 180
+static uint32_t duration_total = 0;
+uint8_t SDNN_ReadyFlag = 0;
+uint8_t Get_Stress = 0;
+static float Stress = 0.0f;
+static uint8_t HRAvg = 0;
+static float SQ_Stress = 0.0f;
+static uint8_t AnomalyFlag = 0;
+uint8_t Stress_ReadyFlag = 0;
+uint8_t Weak_Flag = 0;
+
+static int32_t factory_data = 0;
+static uint8_t work_mode = 0;
+static uint8_t wear_status = 0;
+
+static osTimerId_t g_pah8009_timer = NULL;
+
+static uint8_t time_cnt = 0;
+static uint8_t wear_cnt = 0;
+
+static int history_wear_status = -1;
+
+static int time_out = 15;
+
+
+static uint8_t hrs_num = 0;
+static int32_t sum_hrs = 0;
+static int8_t hrs_data[HRS_WINDOW_SIZE] = {0};
+static uint8_t left = 0;
+static uint8_t right = 0;
+static uint8_t avg_hrs = 0;
+
+static uint8_t timeout = 0;
+
+//============================================================================
+// Private function
+//============================================================================
+static bool sensor_basic_verification(void);
+static void process(uint64_t timestamp);
+static void ppg_sensor_enable(void);
+static void ppg_sensor_disable(void);
+static void touch_sensor_enable(void);
+static void touch_sensor_disable(void);
+
+static void report_touch_data(uint64_t timestamp, const int8_t *touch_flag);
+static void report_ppg_data(const ppg_mems_data_t *ppg_mems_data_alg);
+static void report_hr_data(const ppg_mems_data_t *ppg_mems_data_alg, int32_t ret, float hr, float hr_grader);
+static void report_spo2_data(const ppg_mems_data_t *ppg_mems_data_alg, int32_t ret, float myHR, float mySpO2, float correlation, float trust_flag, float MEMS_RMS, int data_buf_count_spo2_alg, uint8_t no_value_reason, float SignalPeriodicity, int time_accu );
+static void report_stress_data(const ppg_mems_data_t *ppg_mems_data_alg, uint8_t Stress_ReadyFlag, uint32_t measuretime, float collect_time);
+
+static void error_handle(void);
+
+
+static void pah8009_unwearstatus_update(void)
+{
+ uint8_t data = 0;
+ printf("uapi_dev_runtime_get_train_status() : %d\n",uapi_dev_runtime_get_train_status());
+ if(uapi_dev_runtime_get_train_status())
+ {
+ }
+ else
+ {
+// #ifdef SUPPORT_PPG_SERVICE
+// ppg_msg_callback_t func = ppg_get_send_msg_callback_func();
+// if(func != NULL)
+// {
+// //为了主界面脱腕灵敏;需要增加未佩戴
+// func(PPG_MSG_TYPE_HRS_UPDATE, &data, 1);
+// func(PPG_MSG_TYPE_WEARSTAT_UPDATE, &data,1);
+// func(PPG_MSG_TYPE_WEARSTAT_UPDATE,&data,1);
+// func(PPG_MSG_TYPE_WEARSTAT_UPDATE,&data,1);
+// }
+// #endif
+ }
+
+}
+
+static void pah8009_wearstatus_update(int wear_status)
+{
+ if(history_wear_status != wear_status)
+ {
+ if(uapi_dev_runtime_get_train_status())
+ {
+ // printf("wear_cnt : %d\n",wear_cnt);
+ wear_cnt++;
+ if(wear_cnt < 50)
+ return;
+ }
+ wear_cnt = 0;
+ history_wear_status = wear_status;
+#ifdef SUPPORT_CYWEE_SERVICE
+ cywee_wear_status_update((uint8_t *)&history_wear_status);
+#endif
+ if(history_wear_status != 1)
+ {
+ // pah8009_unwearstatus_update();
+ avg_hrs = 0;
+ }
+ }
+ else
+ wear_cnt = 0;
+
+}
+
+static void bubblesort(float *input,int count)
+{
+ int i,j;
+ float sort_temp;
+ for(i=0;i<count - 1;i++)
+ {
+ for(j=0;j<count-i - 1;j++)
+ {
+ if(input[j+1]<input[j])
+ {
+ sort_temp=input[j+1];
+ input[j+1]=input[j];
+ input[j]=sort_temp;
+ }
+ }
+ }
+}
+
+void spo2_log_printf(char s, float f)
+{
+ LOG_PRINT("%s= "LOG_FLOAT_MARKER" \n",s, LOG_FLOAT(f));
+
+}
+
+/*****************************
+FW FIFO Function
+*****************************/
+bool isFIFOEmpty(void)
+{
+ return (_write_index == _read_index) ;
+}
+
+bool pah_push(ppg_mems_data_t *data) //Push return data must be true. If return false, please increase FIFO size.
+{
+ int tmp = _write_index ;
+ tmp++;
+ if(tmp >= FIFO_SIZE)
+ tmp = 0 ;
+ if(tmp == _read_index)
+ return false;
+ ppg_mems_data_buf[tmp] = *data ;
+ _write_index = tmp ;
+ return true;
+}
+
+bool pah_pop(ppg_mems_data_t *data)
+{
+ int tmp ;
+ if(isFIFOEmpty())
+ return false;
+ tmp = _read_index + 1;
+ if(tmp >= FIFO_SIZE)
+ tmp = 0 ;
+ _read_index = tmp ;
+ *data = ppg_mems_data_buf[_read_index] ;
+
+ return true;
+}
+
+
+//============================================================================
+// Public function
+//============================================================================
+void pah_sensor_init(reg_config_type type)
+{
+ memset(&_main, 0, sizeof(_main));
+
+ pah_800x_reg_setting reg_setting;
+ memset(®_setting, 0, sizeof(reg_setting));
+
+ if (!sensor_basic_verification())
+ {
+ // error_handle();
+ LOG_PRINT("sensor_basic_verification failed\n");
+ return;
+ }
+
+
+ // pah_800x
+ pah_800x_config pah_800x_config;
+ pah_800x_get_default_config(&pah_800x_config);
+ LOG_PRINT("PPG_SETTING_VESION = %d \n" ,PPG_SETTING_VERSION);
+
+#if (ENABLE_AE==0)
+ pah_800x_config.enable_ppg_convergence[0] = false;
+ pah_800x_config.enable_ppg_convergence[1] = false;
+ pah_800x_config.enable_ppg_convergence[2] = false;
+#endif
+
+#if (ENABLE_AE_RECONVERGENCE==0)
+ pah_800x_config.enable_ppg_reconvergence_if_out_of_bounds[0] = false;
+ pah_800x_config.enable_ppg_reconvergence_if_out_of_bounds[1] = false;
+ pah_800x_config.enable_ppg_reconvergence_if_out_of_bounds[2] = false;
+#endif
+
+ if (type == pc_4mm_2g_ir_hrd)
+ {
+#if (PC_4MM_2G_IR_HRD==1)
+ PAH_REG_ARR_SETUP(reg_setting.startup_init, PAH8009_REG_DEVICE_INIT);
+ PAH_REG_ARR_SETUP(reg_setting.ppg_enable, PAH8009_REG_PC_4MM_2G_IR_PPG);
+ PAH_REG_ARR_SETUP(reg_setting.touch_enable, PAH8009_REG_PC_4MM_2G_IR_TOUCH);
+#endif
+ }
+ else if (type == diw_4mm_g_ir_hrd)
+ {
+#if (DIW_4MM_G_IR_HRD==1)
+ PAH_REG_ARR_SETUP(reg_setting.startup_init, PAH8009_REG_DEVICE_INIT);
+ PAH_REG_ARR_SETUP(reg_setting.ppg_enable, PAH8009_REG_DIW_4MM_G_IR_PPG);
+ PAH_REG_ARR_SETUP(reg_setting.touch_enable, PAH8009_REG_DIW_4MM_G_IR_TOUCH);
+#endif
+ }
+ else if (type == diw_4mm_ir_r_ir_spo2)
+ {
+#if (DIW_4MM_IR_R_IR_SPO2==1)
+ for(int ch=0;ch<PAH_800x_MAX_CH_NUM;ch++)
+ {
+ pah_800x_config.ppg_convergence[ch].t_weight_normal_mode = 4;
+ pah_800x_config.ppg_convergence[ch].t_weight_fast_mode = 8;
+ pah_800x_config.ppg_convergence[ch].t_max = 150;
+ }
+ PAH_REG_ARR_SETUP(reg_setting.startup_init, PAH8009_REG_DEVICE_INIT);
+ PAH_REG_ARR_SETUP(reg_setting.ppg_enable, PAH8009_REG_DIW_4MM_IR_R_IR_PPG);
+ PAH_REG_ARR_SETUP(reg_setting.touch_enable, PAH8009_REG_DIW_4MM_IR_R_IR_TOUCH);
+#endif
+ }
+ else if (type == pc_22mm_2g_ir_hrd)
+ {
+#if (PC_22MM_2G_IR_HRD==1)
+ for(int ch=0;ch<PAH_800x_MAX_CH_NUM;ch++)
+ {
+ pah_800x_config.ppg_convergence[ch].t_max = 1280;
+ pah_800x_config.ppg_convergence[ch].raw_outer_high_bound = 14848;
+ pah_800x_config.ppg_convergence[ch].raw_inner_high_target = 13824;
+ pah_800x_config.ppg_convergence[ch].raw_inner_low_target = 10752;
+ pah_800x_config.ppg_convergence[ch].raw_outer_low_bound = 9728;
+
+ }
+ PAH_REG_ARR_SETUP(reg_setting.startup_init, PAH800x_REG_DEVICE_INIT);
+ PAH_REG_ARR_SETUP(reg_setting.ppg_enable, PAH800x_REG_PC_22MM_2G_IR_PPG);
+ PAH_REG_ARR_SETUP(reg_setting.touch_enable, PAH800x_REG_PC_22MM_2G_IR_TOUCH);
+#endif
+ }
+ else if (type == di_18mm_g_g_hrd)
+ {
+#if (DI_18MM_G_G_HRD==1)
+ for(int ch=0;ch<PAH_800x_MAX_CH_NUM;ch++)
+ {
+ pah_800x_config.ppg_convergence[ch].t_weight_normal_mode = 15;
+ pah_800x_config.ppg_convergence[ch].t_weight_fast_mode = 25;
+ pah_800x_config.ppg_convergence[ch].t_max = 2048;
+ }
+ PAH_REG_ARR_SETUP(reg_setting.startup_init, PAH8001_REG_DEVICE_INIT);
+ PAH_REG_ARR_SETUP(reg_setting.ppg_enable, PAH8001_REG_DI_18MM_G_G_PPG);
+ PAH_REG_ARR_SETUP(reg_setting.touch_enable, PAH8001_REG_DI_18MM_G_G_TOUCH);
+#endif
+ }
+
+ _main.pah_800x.is_powered= false ;
+ pah_800x_init(&_main.pah_800x, &pah_800x_config);
+ pah_800x_device_init(&_main.pah_800x, ®_setting);
+
+}
+
+void touch_only_sensor_start(void)
+{
+ pah_800x_power_toggle(&_main.pah_800x, true);
+ _main.is_Auto_mode = 0;
+ touch_sensor_enable();
+ _main.debug_print_header = true;
+
+}
+
+void ppg_only_sensor_start(void)
+{
+ pah_800x_power_toggle(&_main.pah_800x, true);
+
+ touch_sensor_enable();
+ ppg_sensor_enable();
+ _main.alg_is_enabled = MSG_ALG_ENABLE;
+
+
+ //_main.ppg_only_is_enabled = 1 ;
+ _main.is_Auto_mode = 0;
+ _main.debug_print_header = true;
+
+}
+
+void auto_mode_sensor_start(void)
+{
+ pah_800x_power_toggle(&_main.pah_800x, true);
+
+ touch_sensor_enable();
+ _main.is_Auto_mode = 1;
+ _main.debug_print_header = true;
+
+}
+
+void pah_sensor_stop(void)
+{
+ if (_main.ppg_is_enabled)
+ {
+ ppg_sensor_disable();
+ }
+ if (_main.touch_is_enabled)
+ {
+ touch_sensor_disable();
+ }
+
+ if(work_mode == 1)
+ hr_algorithm_disable();
+ else if(work_mode == 2)
+ spo2_algorithm_disable();
+
+ pah_800x_deinit(&_main.pah_800x);
+
+ hr = 0;
+ mySpO2 = 0;
+
+ hrs_num = 0;
+ sum_hrs = 0;
+ memset(hrs_data,0,HRS_WINDOW_SIZE);
+
+ left = 0;
+ right = 0;
+ avg_hrs = 0;
+ wear_cnt = 0;
+}
+
+void ppg_interrupt_handle(void)
+{
+ _main.has_interrupt = true;
+ _main.interrupt_timestamp = pah_get_tick_count();
+}
+
+void ppg_sensor_task_interrupt(void)
+{
+ if (_main.has_interrupt)
+ {
+ //LOG_PRINT("ppg_sensor_task_interrupt111111\n");
+ _main.has_interrupt = false;
+ _main.interrupt_timestamp = pah_get_tick_count();
+ process(_main.interrupt_timestamp);
+ }
+
+}
+
+void ppg_sensor_task_polling(void)
+{
+ _main.interrupt_timestamp = pah_get_tick_count();
+ process(_main.interrupt_timestamp);
+}
+
+uint8_t Check_alg_enable(void)
+{
+ return _main.alg_is_enabled;
+}
+
+void Clear_alg_enable(void)
+{
+ _main.alg_is_enabled = MSG_ALG_NOCHANGE;
+}
+
+
+// HR
+void hr_algorithm_enable(void)
+{
+ if (_main.hr_is_enabled)
+ return;
+
+ // static Hr-V301009
+
+ PxiAlg_Close();
+ int version = PxiAlg_Version();
+ DEBUG_PRINT("HR_Version = %d \r\n", version);
+
+
+ uint32_t Query_Mem_Size = PxiAlg_Query_Mem_Size();
+ DEBUG_PRINT("HR_Mem_Size = %d \r\n", Query_Mem_Size);
+
+ hr_pBuffer = malloc(Query_Mem_Size);
+
+
+ if(!PxiAlg_Open_Mem(hr_pBuffer))
+ DEBUG_PRINT("PxiAlg_Open_Mem fail \n");
+
+
+ PxiAlg_SetQualityThreshold(0.35f);
+ PxiAlg_SetHRSmoothWeight(0.9f);
+ PxiAlg_SetProcWindow(5.0f, 5.0f);
+ PxiAlg_SetTimeoutLength(30.0f);
+ PxiAlg_SetMemsScale(3); //MEMS 2/4/8/16G : 0/1/2/3
+ PxiAlg_SetSkipSampleNum(10);
+ PxiAlg_SetMemsScaleBase(3, 0); //MEMS 2/4/8/16G : 0/1/2/3
+ PxiAlg_SetForceOutputLink(10, 150, 40, 1, 0.9f);
+ PxiAlg_SetNormalOutput(200, 40, 0);
+ PxiAlg_SetForceRipple(3, 1);
+ //PxiAlg_SetForceOutput(30, 120, 45);
+ //PxiAlg_SetForceOutputTime(10);
+ PxiAlg_SetPackage(0, 1);
+
+ _main.hr_is_enabled = true;
+}
+
+void hr_algorithm_disable(void)
+{
+ if (!_main.hr_is_enabled)
+ return;
+
+ PxiAlg_Close();
+ free(hr_pBuffer);
+ hr = 0.0f;
+ hr_grader =0.0f;
+ _main.hr_is_enabled = false;
+}
+
+static void get_avg_hrs(uint8_t hrs)
+{
+
+
+ if (hrs_num >= HRS_WINDOW_SIZE) {
+ sum_hrs -= hrs_data[left];
+ left = (left + 1) % HRS_WINDOW_SIZE;
+ hrs_num--;
+ }
+
+ printf("PAH:hrs:%d\n",hrs);
+
+ hrs_data[right] = hrs;
+ sum_hrs += hrs;
+ right = (right + 1) % HRS_WINDOW_SIZE;
+ hrs_num++;
+
+
+ if (hrs_num > 2)
+ {
+
+ uint8_t first_idx = left;
+ int32_t min_hrs = hrs_data[first_idx];
+ int32_t max_hrs = hrs_data[first_idx];
+
+
+ for (uint8_t i = 0; i < hrs_num; i++) {
+ uint8_t current_idx = (left + i) % HRS_WINDOW_SIZE;
+ uint8_t current_hrs = hrs_data[current_idx];
+
+ printf("current_hrs: %d\n", current_hrs);
+
+ if (current_hrs < 40 || current_hrs > 180) {
+ printf("PAH:无效数据过滤: %d\n", current_hrs);
+ continue;
+ }
+
+ if (current_hrs < min_hrs) {
+ min_hrs = current_hrs;
+ }
+ if (current_hrs > max_hrs) {
+ max_hrs = current_hrs;
+ }
+ }
+
+ uint8_t valid_count = hrs_num - 2;
+ avg_hrs = (sum_hrs - max_hrs - min_hrs) / valid_count;
+
+ printf("sum_hrs[%d],max_hrs[%d],min_hrs[%d],avg_hrs[%d]\n",sum_hrs,max_hrs,min_hrs,avg_hrs);
+ }
+}
+
+
+void hr_alg_task(void)
+{
+ if(Check_alg_enable())
+ {
+ if(Check_alg_enable()==MSG_ALG_ENABLE)
+ {
+ hr_algorithm_enable();
+ }else if(Check_alg_enable()==MSG_ALG_DISABLE)
+ {
+ hr_algorithm_disable();
+ }
+ Clear_alg_enable();
+ }
+
+ if(!isFIFOEmpty())
+ {
+ if(pah_pop(&ppg_mems_data_alg)) //Get data from FIFO
+ {
+ if(_main.hr_is_enabled)
+ {
+ ppg_mems_data_alg.ppg_data.has_overflow = 0;
+ ppg_mems_data_alg.ppg_data.overflow_num = 0;
+
+
+
+ // 13Bytes format to hr lib;
+ sRaw_Data_13Bytes hr_data;
+
+ memset(&hr_data, 0, sizeof(hr_data));
+ hr_data.loss = (uint8_t)ppg_mems_data_alg.ppg_data.overflow_num + 1;
+ hr_data.rawData = ppg_mems_data_alg.ppg_data.data[1];
+ hr_data.non_zero1 = 1;
+ hr_data.non_zero2 = 2;
+ hr_data.non_zero3 = 3;
+ hr_data.frame_count = ppg_mems_data_alg.ppg_frame_count;
+ hr_data.time = ppg_mems_data_alg.duration;
+ hr_data.rsv = 0;
+ hr_data.touch = (ppg_mems_data_alg.touch_flag > 0 ? 0x80 : 0x00);
+ hr_data.rsv2 = 0;
+
+ int32_t ret = PxiAlg_Process_Expo((uint8_t*)&hr_data, ppg_mems_data_alg.MEMS_Data, ppg_mems_data_alg.ppg_data.exposure_time[1]);
+
+
+ if(PxiAlg_GetHrUpdateFlag())
+ {
+ PxiAlg_HrGet(&hr);
+ PxiAlg_GetSigGrade(&hr_grader);
+ }
+ if(ppg_mems_data_alg.touch_flag==0)
+ {
+ hr = 0 ;
+ hr_grader = 0;
+ }
+
+ // if(hr > 0)
+ // printf("hr:%f\n",hr);
+#ifdef SUPPORT_PPG_SERVICE
+ time_cnt++;
+
+// if(time_cnt > 15)
+// {
+// #ifdef SUPPORT_CYWEE_SERVICE
+// do_cywee_service_check_msg(CYWEE_SERVICE_CMD_CHECK_ACC, NULL); // 检查gsensor是否停止
+// #endif
+// }
+
+
+ if(time_cnt >= 20)
+ {
+ if(ppg_get_factory_log_switch())
+ {
+ printf("green data : %d\n",factory_data);
+ }
+
+ time_cnt = 0;
+ // ppg_msg_callback_t func = ppg_get_send_msg_callback_func();
+// #ifdef SUPPORT_CYWEE_SERVICE
+// // if(uapi_dev_runtime_get_train_status())
+// {
+// do_cywee_service_check_msg(CYWEE_SERVICE_CMD_CHECK_ACC, NULL); // 检查gsensor是否停止
+// }
+// #endif
+
+ if(hr > 0 && hr < 210.0f && !isnan(hr) && !isinf(hr))
+ {
+ int hr_int = round(hr);
+
+ if (hr_int >= 30 && hr_int <= 210)
+ {
+ avg_hrs = (uint8_t)hr_int;
+ }
+ // else
+ // {
+ // // 舍弃异常值,保持之前的有效值
+ // printf("PAH: HR异常值过滤: %d (超出30-210范围)\n", hr_int);
+ // }
+ }
+ // else if(hr > 0)
+ // {
+ // // hr值异常(过大、NaN、Inf等)
+ // // printf("PAH: HR浮点数异常: %.2f\n", hr);
+ // }
+
+
+ // if(data > 30 && data < 220)
+ // get_avg_hrs(data);
+
+
+ LOG_PRINT("hr : %f avg_hrs : %d\n",hr,avg_hrs);
+ // printf("func(PPG_MSG_TYPE_HRS_UPDATE, &avg_hrs, 1); :%d\n",avg_hrs);
+ // func(PPG_MSG_TYPE_HRS_UPDATE, &avg_hrs, 1);
+
+ // data = cywee_wear_status;
+ // func(PPG_MSG_TYPE_WEARSTAT_UPDATE, &data, 1);
+
+#ifdef SUPPORT_PPG_BLE_SERVICE
+ if(uapi_dev_runtime_get_hrs_ble_status())
+ {
+ uint8_t ble_data = (uint8_t)avg_hrs;
+ ble_ppgtest_server_send_input_report(&ble_data,1);
+ }
+#endif
+
+ }
+
+
+
+
+#endif
+ report_hr_data(&ppg_mems_data_alg, ret, hr, hr_grader);
+
+
+ }
+ else
+ {
+ report_ppg_data(&ppg_mems_data_alg);
+ }
+ }
+ }
+}
+
+// HRD- ALG- Task2
+void hr_alg_task2(uint8_t ppg_num)
+{
+#ifdef PPG_MEMS_Synchronize
+ if(Check_alg_enable())
+ {
+ if(Check_alg_enable()==MSG_ALG_ENABLE)
+ {
+ hr_algorithm_enable();
+ }else if(Check_alg_enable()==MSG_ALG_DISABLE)
+ {
+ hr_algorithm_disable();
+ }
+ Clear_alg_enable();
+ }
+
+ if(!isFIFOEmpty())
+ {
+ for (int i=0; i<ppg_num; i++)
+ {
+
+ if(pah_pop(&ppg_mems_data_alg)) //Get data from FIFO
+ {
+ if(_main.hr_is_enabled)
+ {
+ ppg_mems_data_alg.MEMS_Data[0]=mems_int_data_out[3*i];
+ ppg_mems_data_alg.MEMS_Data[1]=mems_int_data_out[3*i+1];
+ ppg_mems_data_alg.MEMS_Data[2]=mems_int_data_out[3*i+2];
+
+ // 13Bytes format to hr lib;
+ sRaw_Data_13Bytes hr_data;
+
+ memset(&hr_data, 0, sizeof(hr_data));
+ hr_data.loss = (uint8_t)ppg_mems_data_alg.ppg_data.overflow_num + 1;
+ hr_data.rawData = ppg_mems_data_alg.ppg_data.data[1];
+ hr_data.non_zero1 = 1;
+ hr_data.non_zero2 = 2;
+ hr_data.non_zero3 = 3;
+ hr_data.frame_count = ppg_mems_data_alg.ppg_frame_count;
+ hr_data.time = ppg_mems_data_alg.duration;
+ hr_data.rsv = 0;
+ hr_data.touch = (ppg_mems_data_alg.touch_flag > 0 ? 0x80 : 0x00);
+ hr_data.rsv2 = 0;
+
+ int32_t ret = PxiAlg_Process_Expo((uint8_t*)&hr_data, ppg_mems_data_alg.MEMS_Data, ppg_mems_data_alg.ppg_data.exposure_time[1]);
+
+
+ if(PxiAlg_GetHrUpdateFlag())
+ {
+ PxiAlg_HrGet(&hr);
+ PxiAlg_GetSigGrade(&hr_grader);
+ }
+
+ report_hr_data(&ppg_mems_data_alg, ret, hr, hr_grader);
+
+ }
+ else
+ {
+ report_ppg_data(&ppg_mems_data_alg);
+ }
+ }
+ }
+ }
+#endif
+}
+
+
+
+// SPO2
+void spo2_algorithm_enable(void)
+{
+ if (_main.spo2_is_enabled)
+ {
+ return;
+ }
+
+
+ int version = SpO2_Version();
+ DEBUG_PRINT("PxiAlg_Version() = %d\n", version);
+
+ uint32_t buffer_size = SpO2_OpenSize();
+ DEBUG_PRINT("SpO2_Open buffer_size = %d \n", buffer_size);
+
+ pBuffer = malloc(buffer_size);
+
+ if (SpO2_Open(pBuffer) != SPO2_MSG_SUCCESS)
+ {
+ DEBUG_PRINT("SpO2_Open() failed. \n");
+ free(pBuffer);
+ return;
+ }
+ int8_t alg_ch[2] = {0, 1};
+
+ SpO2_SetChMapping(alg_ch, 2);
+ SpO2_SetInputRatioHighBound(94.0f);
+ SpO2_SetInputRatioLowBound(87.0f);
+ SpO2_SetInputModelScale(0.50f);
+
+ SpO2_SetInputCoefHigh0(-36877.85546875f);
+ SpO2_SetInputCoefHigh1(1135.91394042969f);
+ SpO2_SetInputCoefHigh2(-11.6429319381714f);
+ SpO2_SetInputCoefHigh3(0.0398150011897087f);
+
+ SpO2_SetInputCoefMid0(-1430.70520019531f);
+ SpO2_SetInputCoefMid1(50.1599006652832f);
+ SpO2_SetInputCoefMid2(-0.567422986030579f);
+ SpO2_SetInputCoefMid3(0.00219199992716312f);
+
+ SpO2_SetInputCoefLow0(-8.19796657562256f);
+ SpO2_SetInputCoefLow1(1.74200701713562f);
+ SpO2_SetInputCoefLow2(-0.0173850003629923f);
+ SpO2_SetInputCoefLow3(0.000105999999505002f);
+
+ SpO2_SetIIRFilterRatio(0.5f);
+ SpO2_SetInputCorrelationTh(0.850000078678131f);
+ SpO2_SetInputRatioLow(0.05f);
+
+ SpO2_SetInputFlagCheckHr(1);
+ SpO2_SetMotionDetectionLevel(2.0f);
+ SpO2_Set_Update_Sec(1);
+
+ SpO2_Set_Init_Low_SpO2_Constrain(0.0);
+ SpO2_SetOutputHighBound(100.0f);
+ SpO2_SetOutputLowBound(60.0f);
+ SpO2_Set_Keep_Init_SpO2_Bound(95.0f);
+ SpO2_Set_Keep_Init_SpO2_Count_Th(18.0f);
+ SpO2_Set_SpO2_Bias_Value(0.5f);
+ SpO2_Set_SpO2_Outlier_Smooth_Coef(5.5f);
+ SpO2_Set_SpO2_Decrease_Coef(0.5f);
+ SpO2_SetSkipDataLen(25);
+
+ SpO2_Set_Flag_Hold_Breathe_Mode(0);
+ SpO2_Set_Input_Correlation_Th2(0.85000f);
+ SpO2_Set_Correlation_Th2_Count(30.0f);
+ SpO2_Set_Init_Low_SpO2_Th(94.5f);
+ SpO2_Set_Motion_Rms_Th(3000.0f);
+ SpO2_Set_Motion_Rms_Th2(1500.0f);
+ SpO2_Set_Motion_MEMS_Th(0.0f);
+ SpO2_Set_T1(0.85f);
+ SpO2_Set_Spo2_Force_Output_Second(15.0f);
+
+ Spo2_Set_Force_Output_Value(97.50f); // Good Signal Force SpO2
+ Spo2_Set_Force_Output_Value2(95.00f); // Bad Signal Force SpO2
+
+ SpO2_Set_Init_Low_SpO2_Check_Sec(20);
+ Spo2_Set_SpO2_First_Smooth_Th(95.0f);
+ Spo2_Set_SpO2_First_Smooth_Sec(15);
+ Spo2_Set_SpO2_Correlation_Smooth_Th(0.85f);
+ SpO2_Set_Signal_Periodicity_Th(0.50f);
+
+ // static Hr-V301009
+ PxiAlg_Close();
+ version = PxiAlg_Version();
+ DEBUG_PRINT("HR_Version = %d \r\n", version);
+
+
+ uint32_t Query_Mem_Size = PxiAlg_Query_Mem_Size();
+ DEBUG_PRINT("HR_Mem_Size = %d \r\n", Query_Mem_Size);
+
+ hr_pBuffer = malloc(Query_Mem_Size);
+
+
+ if(!PxiAlg_Open_Mem(hr_pBuffer))
+ DEBUG_PRINT("PxiAlg_Open_Mem fail \n");
+
+
+ PxiAlg_SetQualityThreshold(0.35f);
+ PxiAlg_SetHRSmoothWeight(0.9f);
+ PxiAlg_SetProcWindow(5.0f, 5.0f);
+ PxiAlg_SetTimeoutLength(30.0f);
+ PxiAlg_SetMemsScale(1); //MEMS 2/4/8/16G : 0/1/2/3
+ PxiAlg_SetSkipSampleNum(10);
+ PxiAlg_SetMemsScaleBase(1, 0); //MEMS 2/4/8/16G : 0/1/2/3
+ PxiAlg_SetForceOutputLink(15, 90, 60, 1, 0.9f);
+ PxiAlg_SetNormalOutput(200, 40, 1);
+ PxiAlg_SetForceRipple(3, 1);
+ PxiAlg_SetPackage(0, 1);
+
+ myHR = 0.0f;
+ mySpO2 = 0.0f;
+ correlation = 0.0f;
+ trust_flag = 0.0f;
+ MEMS_RMS = 0.0f;
+ mySPO2_ReadyFlag = 0;
+ hr_grader = 0.0f;
+ data_buf_count_spo2_alg=0;
+ no_value_reason = 0 ;
+ SignalPeriodicity=0.0f ;
+ time_accu = 0 ;
+ count = 0 ;
+ memset(spo2_array, 0, sizeof(*spo2_array));
+ _main.spo2_is_enabled = true;
+}
+
+void spo2_algorithm_disable(void)
+{
+ if (!_main.spo2_is_enabled)
+ return;
+ PxiAlg_Close();
+ SpO2_Close();
+ free(pBuffer);
+ free(hr_pBuffer);
+ myHR = 0.0f;
+ mySpO2 = 0.0f;
+ correlation = 0.0f;
+ trust_flag = 0.0f;
+ MEMS_RMS = 0.0f;
+ mySPO2_ReadyFlag = 0;
+ hr_grader = 0.0f;
+ data_buf_count_spo2_alg=0;
+
+ no_value_reason = 0 ;
+ SignalPeriodicity=0.0f ;
+ time_accu = 0 ;
+
+
+ _main.spo2_is_enabled = false;
+}
+
+void spo2_alg_task(void)
+{
+ if(Check_alg_enable())
+ {
+ if(Check_alg_enable()==MSG_ALG_ENABLE)
+ {
+ spo2_algorithm_enable();
+ }else if(Check_alg_enable()==MSG_ALG_DISABLE)
+ {
+ spo2_algorithm_disable();
+ }
+ Clear_alg_enable();
+ }
+
+ if(!isFIFOEmpty())
+ {
+ if(pah_pop(&ppg_mems_data_alg)) //Get data from FIFO
+ {
+ if(_main.spo2_is_enabled)
+ {
+ // 13Bytes format to hr lib;
+
+ int32_t RAW_DATA[2] ;
+ sRaw_Data_13Bytes hr_data;
+
+ memset(&hr_data, 0, sizeof(hr_data));
+ hr_data.loss = (uint8_t)ppg_mems_data_alg.ppg_data.overflow_num + 1;
+ hr_data.rawData = ppg_mems_data_alg.ppg_data.data[0];
+ hr_data.non_zero1 = 1;
+ hr_data.non_zero2 = 2;
+ hr_data.non_zero3 = 3;
+ hr_data.frame_count = ppg_mems_data_alg.ppg_frame_count;
+ hr_data.time = ppg_mems_data_alg.duration;
+ hr_data.rsv = 0;
+ hr_data.touch = (ppg_mems_data_alg.touch_flag > 0 ? 0x80 : 0x00);
+ hr_data.rsv2 = 0;
+
+
+ int32_t ret = PxiAlg_Process_Expo((uint8_t*)&hr_data, ppg_mems_data_alg.MEMS_Data, ppg_mems_data_alg.ppg_data.exposure_time[0]);
+
+
+
+
+ if(PxiAlg_GetHrUpdateFlag())
+ {
+ PxiAlg_HrGet(&myHR);
+ PxiAlg_GetSigGrade(&hr_grader);
+ }
+
+
+ int32_t spo2_raw_1=ppg_mems_data_alg.ppg_data.data[0];
+ int32_t spo2_raw_2=ppg_mems_data_alg.ppg_data.data[1];
+
+ RAW_DATA[0]=spo2_raw_1;
+ RAW_DATA[1]=spo2_raw_2;
+
+ mems_data_raw[0].data[0]=ppg_mems_data_alg.MEMS_Data[0];
+ mems_data_raw[0].data[1]=ppg_mems_data_alg.MEMS_Data[1] ;
+ mems_data_raw[0].data[2]=ppg_mems_data_alg.MEMS_Data[2];
+
+ // SPO2 fill data on pah8series structure
+ pxialg_data_spo2.touch_flag = ppg_mems_data_alg.touch_flag ;
+ pxialg_data_spo2.time = ppg_mems_data_alg.duration ;;
+ pxialg_data_spo2.ppg_data = (int32_t*)RAW_DATA;
+ pxialg_data_spo2.nf_ppg_channel = 2;
+ pxialg_data_spo2.nf_ppg_per_channel = 1;
+ pxialg_data_spo2.frame_count = ppg_mems_data_alg.ppg_frame_count ;
+ pxialg_data_spo2.nf_mems = 1;
+ pxialg_data_spo2.mems_data = (int16_t*)mems_data_raw;
+
+
+
+ // DEBUG_PRINT("data_buf_count_spo2_alg = %d \r\n", data_buf_count_spo2_alg);
+
+
+ SpO2_SetHrValue(myHR);
+ SpO2_SetMems(mems_data_raw, 1);
+ //int ret_value = SpO2_Process(ppg_mems_data_alg.touch_flag , 2, pxialg_data_spo2.ppg_data, 1);
+ int ret_value = SpO2_Process(ppg_mems_data_alg.touch_flag , 2, pxialg_data_spo2.ppg_data, 1,pxialg_data_spo2.time );
+
+ SpO2_GetSpO2(&mySpO2);
+
+ // if(mySpO2 > 0)
+ // printf("SpO2 : %f\n",mySpO2);
+#ifdef SUPPORT_PPG_SERVICE
+ time_cnt++;
+
+ if(time_cnt >= 20)
+ {
+ if(ppg_get_factory_log_switch())
+ {
+ printf("red data : %d\n",factory_data);
+ }
+
+ time_cnt = 0;
+ // ppg_msg_callback_t func = ppg_get_send_msg_callback_func();
+// #ifdef SUPPORT_CYWEE_SERVICE
+// // if(uapi_dev_runtime_get_train_status())
+// {
+// do_cywee_service_check_msg(CYWEE_SERVICE_CMD_CHECK_ACC, NULL); // 检查gsensor是否停止
+// }
+// #endif
+ }
+ // ppg_msg_callback_t func = ppg_get_send_msg_callback_func();
+
+ // if(func != NULL && time_cnt >= 25)
+ // {
+ // uint8_t data = (uint8_t)mySpO2;
+ // func(PPG_MSG_TYPE_SPO2_UPDATE, &data, 1);
+ // time_cnt = 0;
+ // }
+#endif
+
+ SpO2_GetReadyFlag(&mySPO2_ReadyFlag);
+ SpO2_GetInfo(&_spo2_detect_item);
+ correlation =_spo2_detect_item.Noise;
+ trust_flag =_spo2_detect_item.Output[3];
+ no_value_reason=_spo2_detect_item.Output[4];
+ SpO2_GetPeriodicity(&SignalPeriodicity);
+ MEMS_RMS =_spo2_detect_item.RMS;
+
+
+ data_buf_count_spo2_alg++;
+
+ #if defined(Optimize_SPO2_flow)
+ time_accu=time_accu+pxialg_data_spo2.time;
+ #endif
+
+ if( (ret_value == 0 ) && (data_buf_count_spo2_alg>=25) )
+ {
+
+ #if defined(Optimize_SPO2_flow)
+ //time_accu=time_accu+pxialg_data->time;
+ DEBUG_PRINT("SPO2 Test Time = %d ,count =%d\n",time_accu,count);
+ if(time_accu>=60000)
+ {
+ spo2_array[count]=mySpO2;
+ count=count+1;
+
+ LOG_PRINT("Over_30_sec_Stop_measurement\n");
+ LOG_PRINT("This_meausrement_suggest_spo2_value is");
+
+ bubblesort(spo2_array,count);
+
+ if(count % 2 == 0)
+ {
+ //log_printf(" "LOG_FLOAT_MARKER" \n", LOG_FLOAT(spo2_array[count/2]));
+ LOG_PRINT(" "LOG_FLOAT_MARKER" \n",LOG_FLOAT((spo2_array[(int)(count/2)] + spo2_array[(int)(count/2) - 1]) / 2.0f));
+ }
+ else
+ {
+ LOG_PRINT(" "LOG_FLOAT_MARKER" \n", LOG_FLOAT(spo2_array[(int)(count/2)]*1.0f));
+ }
+
+ //log_pah8series_no_touch();
+ spo2_algorithm_disable();
+ pah_sensor_stop();
+ count=0;
+ //add sensor stop here
+ }
+ else
+ {
+ if(mySpO2>0)
+ {
+ spo2_array[count]=mySpO2;
+ count=count+1;
+ }
+ }
+ #endif
+ //report_spo2_data(&ppg_mems_data_alg, ret, mySpO2, myHR, correlation, trust_flag, MEMS_RMS,data_buf_count_spo2_alg, Opt_SpO2, drop_flag, ascend_flag, mems_fla);
+ report_spo2_data(&ppg_mems_data_alg, ret, mySpO2, myHR, correlation, trust_flag, MEMS_RMS,data_buf_count_spo2_alg, no_value_reason, SignalPeriodicity, time_accu);
+ data_buf_count_spo2_alg = data_buf_count_spo2_alg -25;
+ }
+ else
+ {
+ //report_spo2_data(&ppg_mems_data_alg, ret, mySpO2, myHR, correlation, trust_flag, MEMS_RMS,data_buf_count_spo2_alg, Opt_SpO2, drop_flag, ascend_flag, mems_flag);
+ report_spo2_data(&ppg_mems_data_alg, ret, mySpO2, myHR, correlation, trust_flag, MEMS_RMS,data_buf_count_spo2_alg, no_value_reason, SignalPeriodicity, time_accu);
+
+ switch (ret_value)
+ {
+ case SPO2_MSG_ALG_NOT_OPEN:
+ DEBUG_PRINT("Algorithm is not initialized. \n");
+ break;
+
+ case SPO2_MSG_ALG_REOPEN:
+ DEBUG_PRINT("Algorithm is re initialized. \n");
+ break;
+
+ case SPO2_MSG_ALG_NOT_SUPPORT:
+ DEBUG_PRINT("Algorithm is not support. \n");
+ break;
+
+ default:
+ // DEBUG_PRINT("Algorithm unhandle error = %d \n", ret_value);
+ break;
+ }
+ //return 1 ;
+ }
+ }
+ else
+ {
+ report_ppg_data(&ppg_mems_data_alg);
+ }
+ }
+ }
+}
+
+
+
+// SPO2- ALG- Task2
+void spo2_alg_task2(uint8_t ppg_num)
+{
+#ifdef PPG_MEMS_Synchronize
+ if(Check_alg_enable())
+ {
+ if(Check_alg_enable()==MSG_ALG_ENABLE)
+ {
+ spo2_algorithm_enable();
+ }else if(Check_alg_enable()==MSG_ALG_DISABLE)
+ {
+ spo2_algorithm_disable();
+ }
+ Clear_alg_enable();
+ }
+
+ if(!isFIFOEmpty())
+ {
+ for (int k=0; k<ppg_num; k++)
+ {
+ if(pah_pop(&ppg_mems_data_alg)) //Get data from FIFO
+ {
+ if(_main.spo2_is_enabled)
+ {
+ // 13Bytes format to hr lib;
+
+
+ ppg_mems_data_alg.MEMS_Data[0]=mems_int_data_out[3*k];
+ ppg_mems_data_alg.MEMS_Data[1]=mems_int_data_out[3*k+1];
+ ppg_mems_data_alg.MEMS_Data[2]=mems_int_data_out[3*k+2];
+
+ int32_t RAW_DATA[2]={0} ;
+ sRaw_Data_13Bytes hr_data;
+
+ memset(&hr_data, 0, sizeof(hr_data));
+ hr_data.loss = (uint8_t)ppg_mems_data_alg.ppg_data.overflow_num + 1;
+ hr_data.rawData = ppg_mems_data_alg.ppg_data.data[0];
+ hr_data.non_zero1 = 1;
+ hr_data.non_zero2 = 2;
+ hr_data.non_zero3 = 3;
+ hr_data.frame_count = ppg_mems_data_alg.ppg_frame_count;
+ hr_data.time = ppg_mems_data_alg.duration;
+ hr_data.rsv = 0;
+ hr_data.touch = (ppg_mems_data_alg.touch_flag > 0 ? 0x80 : 0x00);
+ hr_data.rsv2 = 0;
+
+
+ int32_t ret = PxiAlg_Process_Expo((uint8_t*)&hr_data, ppg_mems_data_alg.MEMS_Data, ppg_mems_data_alg.ppg_data.exposure_time[0]);
+
+
+
+
+ if(PxiAlg_GetHrUpdateFlag())
+ {
+ PxiAlg_HrGet(&myHR);
+ PxiAlg_GetSigGrade(&hr_grader);
+ }
+
+
+ int32_t spo2_raw_1=ppg_mems_data_alg.ppg_data.data[0];
+ int32_t spo2_raw_2=ppg_mems_data_alg.ppg_data.data[1];
+
+ RAW_DATA[0]=spo2_raw_1;
+ RAW_DATA[1]=spo2_raw_2;
+
+ mems_data_raw[0].data[0]=ppg_mems_data_alg.MEMS_Data[0];
+ mems_data_raw[0].data[1]=ppg_mems_data_alg.MEMS_Data[1] ;
+ mems_data_raw[0].data[2]=ppg_mems_data_alg.MEMS_Data[2];
+
+ // SPO2 fill data on pah8series structure
+ pxialg_data_spo2.touch_flag = ppg_mems_data_alg.touch_flag ;
+ pxialg_data_spo2.time = ppg_mems_data_alg.duration ;;
+ pxialg_data_spo2.ppg_data = (int32_t*)RAW_DATA;
+ pxialg_data_spo2.nf_ppg_channel = 2;
+ pxialg_data_spo2.nf_ppg_per_channel = 1;
+ pxialg_data_spo2.frame_count = ppg_mems_data_alg.ppg_frame_count ;
+ pxialg_data_spo2.nf_mems = 1;
+ pxialg_data_spo2.mems_data = (int16_t*)mems_data_raw;
+
+
+
+ //DEBUG_PRINT("data_buf_count_spo2_alg = %d \r\n", data_buf_count_spo2_alg);
+
+
+ SpO2_SetHrValue(myHR);
+ SpO2_SetMems(mems_data_raw, 1);
+ //int ret_value = SpO2_Process(ppg_mems_data_alg.touch_flag , 2, pxialg_data_spo2.ppg_data, 1);
+ int ret_value = SpO2_Process(ppg_mems_data_alg.touch_flag , 2, pxialg_data_spo2.ppg_data, 1,pxialg_data_spo2.time );
+
+ SpO2_GetSpO2(&mySpO2);
+ SpO2_GetReadyFlag(&mySPO2_ReadyFlag);
+ SpO2_GetInfo(&_spo2_detect_item);
+ correlation =_spo2_detect_item.Noise;
+ trust_flag =_spo2_detect_item.Output[3];
+ no_value_reason=_spo2_detect_item.Output[4];
+ SpO2_GetPeriodicity(&SignalPeriodicity);
+ MEMS_RMS =_spo2_detect_item.RMS;
+
+
+ data_buf_count_spo2_alg++;
+
+ #if defined(Optimize_SPO2_flow)
+ time_accu=time_accu+pxialg_data_spo2.time;
+ #endif
+
+ if( (ret_value == 0 ) && (data_buf_count_spo2_alg>=25) )
+ {
+
+ #if defined(Optimize_SPO2_flow)
+ //time_accu=time_accu+pxialg_data->time;
+ DEBUG_PRINT("SPO2 Test Time = %d ,count =%d\n",time_accu,count);
+ if(time_accu>=31000)
+ {
+ spo2_array[count]=mySpO2;
+ count=count+1;
+
+ LOG_PRINT("Over_30_sec_Stop_measurement\n");
+ LOG_PRINT("This_meausrement_suggest_spo2_value is");
+
+ bubblesort(spo2_array,count);
+
+ if(count % 2 == 0)
+ {
+ //log_printf(" "LOG_FLOAT_MARKER" \n", LOG_FLOAT(spo2_array[count/2]));
+ LOG_PRINT(" "LOG_FLOAT_MARKER" \n",LOG_FLOAT((spo2_array[(int)(count/2)] + spo2_array[(int)(count/2) - 1]) / 2.0f));
+ }
+ else
+ {
+ LOG_PRINT(" "LOG_FLOAT_MARKER" \n", LOG_FLOAT(spo2_array[(int)(count/2)]*1.0f));
+ }
+
+ //log_pah8series_no_touch();
+ spo2_algorithm_disable();
+ pah_sensor_stop();
+ count=0;
+ //add sensor stop here
+ }
+ else
+ {
+ if(mySpO2>0)
+ {
+ spo2_array[count]=mySpO2;
+ count=count+1;
+ }
+ }
+ #endif
+ //report_spo2_data(&ppg_mems_data_alg, ret, mySpO2, myHR, correlation, trust_flag, MEMS_RMS,data_buf_count_spo2_alg, Opt_SpO2, drop_flag, ascend_flag, mems_fla);
+ report_spo2_data(&ppg_mems_data_alg, ret, mySpO2, myHR, correlation, trust_flag, MEMS_RMS,data_buf_count_spo2_alg, no_value_reason, SignalPeriodicity, time_accu);
+ data_buf_count_spo2_alg = data_buf_count_spo2_alg -25;
+ }
+ else
+ {
+ //report_spo2_data(&ppg_mems_data_alg, ret, mySpO2, myHR, correlation, trust_flag, MEMS_RMS,data_buf_count_spo2_alg, Opt_SpO2, drop_flag, ascend_flag, mems_flag);
+ report_spo2_data(&ppg_mems_data_alg, ret, mySpO2, myHR, correlation, trust_flag, MEMS_RMS,data_buf_count_spo2_alg, no_value_reason, SignalPeriodicity, time_accu);
+
+ switch (ret_value)
+ {
+ case SPO2_MSG_ALG_NOT_OPEN:
+ DEBUG_PRINT("Algorithm is not initialized. \n");
+ break;
+
+ case SPO2_MSG_ALG_REOPEN:
+ DEBUG_PRINT("Algorithm is re initialized. \n");
+ break;
+
+ case SPO2_MSG_ALG_NOT_SUPPORT:
+ DEBUG_PRINT("Algorithm is not support. \n");
+ break;
+
+ default:
+ DEBUG_PRINT("Algorithm unhandle error = %d \n", ret_value);
+ break;
+ }
+ //return 1 ;
+ }
+ }
+ else
+ {
+ report_ppg_data(&ppg_mems_data_alg);
+ }
+ }
+ }
+ }
+#endif
+}
+
+
+
+// Stress
+void stress_algorithm_enable(void)
+{
+ int buffer_size = 0;
+ if (_main.stress_is_enable)
+ {
+ return;
+ }
+
+ LOG_PRINT("HRVGetVersion() = %d \n", HRVGetVersion());
+ buffer_size = HRVGetMemorySize(HRV_ENABLE_LHR, HRV_COLLECTION_SECONDS, HRV_SAMPLING_RATE);
+ LOG_PRINT("HRVGetMemorySize(), open_size = %d \n", buffer_size);
+
+ if (!buffer_size)
+ {
+ LOG_PRINT("HRVGetMemorySize(), failed to get memory size \n");
+ return ;
+ }
+
+ stress_pBuffer = (uint8_t*)malloc(buffer_size);
+ if (!stress_pBuffer)
+ {
+ LOG_PRINT("Out of memory \n");
+ return ;
+ }
+
+ if (!HRVOpen(stress_pBuffer))
+ {
+ LOG_PRINT("HRVOpen() failed. \n");
+ return ;
+ }
+ duration_total=0;
+ _hrv_state.collect_duration = 0;
+
+ _main.stress_is_enable = true;
+}
+
+void stress_algorithm_disable(void)
+{
+ if (!_main.stress_is_enable)
+ return;
+ HRVClose();
+ free(stress_pBuffer);
+
+ duration_total = 0;
+ SDNN_ReadyFlag = 0;
+ Get_Stress = 0;
+ Stress = 0.0f;
+ HRAvg = 0;
+ SQ_Stress = 0.0f;
+ AnomalyFlag = 0;
+ Stress_ReadyFlag = 0;
+ Weak_Flag = 0;
+ _main.stress_is_enable = false;
+}
+
+void stress_alg_task(int grade)
+{
+ if(Check_alg_enable())
+ {
+ if(Check_alg_enable()==MSG_ALG_ENABLE)
+ {
+ stress_algorithm_enable();
+ }else if(Check_alg_enable()==MSG_ALG_DISABLE)
+ {
+ stress_algorithm_disable();
+ }
+ Clear_alg_enable();
+ }
+
+ if(!isFIFOEmpty())
+ {
+ if(pah_pop(&ppg_mems_data_alg)) //Get data from FIFO
+ {
+ if(_main.stress_is_enable)
+ {
+ uint32_t raw_data = ppg_mems_data_alg.ppg_data.data[1];
+ uint8_t frame_count = ppg_mems_data_alg.ppg_frame_count;
+ uint8_t timeInterval = ppg_mems_data_alg.duration;
+ uint8_t touch_flag = ppg_mems_data_alg.touch_flag;
+
+ // If using Interrupt mode call this API
+ HRVSetDiffTime(timeInterval,1);
+
+ HRVProcess(touch_flag, raw_data);
+
+ duration_total+=timeInterval;
+
+
+ if(Stress_ReadyFlag==0)
+ {
+ Stress_ReadyFlag=HRVGetReadyFlag();
+ }
+
+ if((duration_total>25000)&& Stress_ReadyFlag==0)
+ {
+ LOG_PRINT("PPG Raw data is too Weak\r\n");
+ LOG_PRINT("TEST Stop\r\n");
+ stress_algorithm_disable();
+ Weak_Flag=1;
+ return ;
+ }
+
+ if(Stress_ReadyFlag == 1 || HRVGetReadyFlag())
+ {
+ Stress_ReadyFlag=1;
+ _hrv_state.collect_duration += timeInterval;
+
+ if(_hrv_state.collect_duration > (HRV_COLLECTION_SECONDS * 1000))
+ {
+ //int grade=100;
+ HRAvg = HRVGetHRAvg();
+ Stress= HRVGetStress(grade);
+ SQ_Stress=HRVGetRRIQuality();
+ AnomalyFlag= HRVGetAnomalyFlag();
+ LOG_PRINT("HRAvg = %d Stress = %f HRV_Quality = %f AnomalyFlag= %d \n", HRAvg ,Stress,SQ_Stress,AnomalyFlag);
+
+ if(SQ_Stress>0.4f)
+ {
+ LOG_PRINT("HRV_Quality Good\n");
+ }
+ else
+ {
+ LOG_PRINT("HRV_Quality Weak\n");
+ }
+
+
+ if(Stress >=80 && Stress<=100)
+ {
+ LOG_PRINT("Stress_Level High\n");
+ }
+ else if(Stress >=60 && Stress<80)
+ {
+ LOG_PRINT("Stress_Level Medium\n");
+ }
+ else if(Stress >=30 && Stress<60)
+ {
+ LOG_PRINT("Stress_Level Normal\n");
+ }
+ else
+ {
+ LOG_PRINT("Stress_Level Relaxed\n");
+ }
+
+ stress_algorithm_disable();
+ _hrv_state.has_got_sdnn = 1;
+ Get_Stress=1;
+ return ;
+ }
+ }
+ report_stress_data(&ppg_mems_data_alg,Stress_ReadyFlag, duration_total, _hrv_state.collect_duration );
+ }
+ else
+ {
+ report_ppg_data(&ppg_mems_data_alg);
+ LOG_PRINT("Stress_Level Normal\n");
+ }
+ }
+ }
+}
+
+
+
+
+//============================================================================
+// Private function
+//============================================================================
+static bool sensor_basic_verification(void)
+{
+ uint16_t product_id = 0;
+ pah_800x_get_product_id(&product_id);
+ if (product_id != PAH_800x_PRODUCT_ID)
+ {
+ pah_800x_power_toggle(&_main.pah_800x, true);
+ pah_800x_get_product_id( &product_id);
+ }
+
+ DEBUG_PRINT("product_id = 0x%04X\n", product_id);
+ if (product_id != PAH_800x_PRODUCT_ID)
+ {
+ DEBUG_PRINT("Failed to verify product_id\n");
+ return false;
+ }
+
+ return true;
+}
+
+static void process(uint64_t timestamp)
+{
+ float mems_data[3] = { 0.0f, 0.0f, 0.0f };
+
+ // Task
+ pah_ret ret = pah_800x_task(&_main.pah_800x);
+ if (PAH_FAILED(ret))
+ {
+ DEBUG_PRINT("pah_800x_task() failed, ret = %d\n", ret);
+ return;
+ }
+
+ int8_t touch_flag = -1;
+ pah_800x_get_touch_flag(&_main.pah_800x, &touch_flag);
+
+ // PRINT("touch_flag : %d\n",touch_flag);
+
+ pah8009_wearstatus_update(touch_flag);
+
+ if (_main.ppg_is_enabled)
+ {
+ pah_800x_ppg_data ppg_data;
+ pah_800x_ppg_get_data(&_main.pah_800x, &ppg_data);
+
+ if (ppg_data.has_data)
+ {
+ // LOG_PRINT("ppg data %d %d %d %d %d %d %d %d\n",ppg_data.has_data,ppg_data.overflow_num,ppg_data.data[0],ppg_data.data[1],ppg_data.data[2],
+ // ppg_data.exposure_time[0],ppg_data.exposure_time[1],ppg_data.exposure_time[2]);
+ // LOG_PRINT("ppg data %d %d %d %d %d %d\n",ppg_data.frame_period[0],ppg_data.frame_period[1],ppg_data.frame_period[2]
+ // ,ppg_data.adc[0],ppg_data.adc[1],ppg_data.adc[2]);
+
+ // PRINT("ppg data : %d %d %d\n",ppg_data.data[0],ppg_data.data[1],ppg_data.data[2]);
+
+ if(work_mode == 1)
+ factory_data = ppg_data.data[1];
+ else if(work_mode == 2)
+ factory_data = ppg_data.data[0];
+
+ uint64_t duration = timestamp - _main.ppg_prev_report_timestamp;
+
+ if (!_main.ppg_has_prev_report_timestamp)
+ {
+ _main.ppg_has_prev_report_timestamp = true;
+ duration = 50;
+ }
+ _main.ppg_prev_report_timestamp = timestamp;
+
+ if (duration > UINT8_MAX)
+ {
+ DEBUG_PRINT("duration > UINT8_MAX !!!\n");
+ return;
+ }
+
+ ppg_mems_data_t ppg_mems_data;
+
+ /***********************G sensor explanation start***********************************/
+ //If no G sensor, please set ppg_mems_data.MEMS_Data[3] = {0};
+ //Please set G sensor default range = ±2G.
+ //If G sensor output data format is not 16bit, please change to 16bit format.
+ //For example: G sensor is 12bit
+ //ReadGSensor(&mems_data[0],&mems_data[1],&mems_data[2])
+ //MEMS_SensorX() absolute data must be largest when let hand down at static state.
+ //MEMS_SensorZ() absolute data must be largest when let hand horizontal on the table at static state.
+ /***********************G sensor explanation end***********************************/
+
+ ReadGSensor(&mems_data[0],&mems_data[1],&mems_data[2]);
+
+ ppg_mems_data.MEMS_Data[0] = mems_data[0] * 2048 / 10;//ReadGSensorX();
+ ppg_mems_data.MEMS_Data[1] = mems_data[1] * 2048 / 10;//ReadGSensorY();
+ ppg_mems_data.MEMS_Data[2] = mems_data[2] * 2048 / 10;//ReadGSensorZ();
+
+ // ppg_mems_data.MEMS_Data[0] = mems_data[0] * 4096 / 10;//ReadGSensorX();
+ // ppg_mems_data.MEMS_Data[1] = mems_data[1] * 4096 / 10;//ReadGSensorY();
+ // ppg_mems_data.MEMS_Data[2] = mems_data[2] * 4096 / 10;//ReadGSensorZ();
+
+ // printf("x:%f y:%f z:%f\n",ppg_mems_data.MEMS_Data[0],ppg_mems_data.MEMS_Data[1],ppg_mems_data.MEMS_Data[2]);
+ ppg_mems_data.duration = duration;
+
+ ppg_mems_data.ppg_data = ppg_data ;
+
+ ppg_mems_data.touch_flag = history_wear_status ;
+ ppg_mems_data.ppg_frame_count = _main.ppg_frame_count; //SD-revise-20200320
+
+ pah_push(&ppg_mems_data);
+
+#ifdef PPG_MEMS_Synchronize
+ time_temp+=duration;
+ mems_data_polling_ready=0;
+ if(time_temp>=Time_Interval)
+ {
+ mems_data_polling_ready=1;
+ time_temp =0;
+ }
+ ppg_data_num++;
+#endif
+ ++_main.ppg_frame_count;
+
+ //report_ppg_data(&ppg_mems_data);
+
+ }
+ }
+
+ if (_main.is_Auto_mode || _main.touch_is_enabled)//SD-reivse-20191205
+ {
+ if ( _main.touch_last_is_touched != history_wear_status)
+ {
+ _main.touch_last_is_touched = history_wear_status;
+
+ report_touch_data(timestamp, &history_wear_status);
+
+ bool is_touched = (history_wear_status >= 1);
+
+ if (_main.is_Auto_mode && is_touched)//SD-revise-20191205
+ {
+ //touch_sensor_disable();
+ ppg_sensor_enable();
+ _main.alg_is_enabled = MSG_ALG_ENABLE;
+ }
+ else if (_main.is_Auto_mode && !is_touched)
+ {
+ // pah8009_unwearstatus_update();
+ ppg_sensor_disable();
+ pah_800x_update_touch_setting(&_main.pah_800x);
+ //touch_sensor_enable();
+ _main.alg_is_enabled = MSG_ALG_DISABLE;
+ }
+ }
+ }
+}
+
+static void ppg_sensor_enable(void)
+{
+ if (_main.ppg_is_enabled)
+ return;
+
+ pah_800x_ppg_toggle(&_main.pah_800x, true);
+
+ _main.debug_print_header = true;
+ _main.ppg_has_prev_report_timestamp = false;
+ _main.ppg_prev_report_timestamp = pah_get_tick_count();
+ _main.ppg_frame_count = 0;
+
+ _main.ppg_is_enabled = true;
+
+ _read_index = 0 ;
+ _write_index = 0 ;
+#ifdef PPG_MEMS_Synchronize
+ ppg_data_num = 0 ;
+#endif
+ //LOG_PRINT("ppg_sensor_enable\n");
+}
+
+static void ppg_sensor_disable(void)
+{
+ if (!_main.ppg_is_enabled)
+ return;
+
+ pah_800x_ppg_toggle(&_main.pah_800x, false);
+
+ _main.ppg_is_enabled = false;
+}
+
+static void touch_sensor_enable(void)
+{
+ if (_main.touch_is_enabled)
+ return;
+
+ pah_800x_touch_toggle(&_main.pah_800x, true);
+
+ _main.touch_last_is_touched = 0;//default no touch => touch mode
+
+ _main.touch_is_enabled = true;
+}
+
+static void touch_sensor_disable(void)
+{
+ if (!_main.touch_is_enabled)
+ return;
+
+ pah_800x_touch_toggle(&_main.pah_800x, false);
+
+ _main.touch_is_enabled = false;
+}
+
+static void report_touch_data(uint64_t timestamp, const int8_t *touch_flag)
+{
+ LOG_PRINT("TOUCH: time = { %llu }, touch_flag = { %d } \n", timestamp, *touch_flag );
+}
+
+static void report_ppg_data(const ppg_mems_data_t *ppg_mems_data_alg)
+{
+ if (_main.debug_print_header == true)
+ {
+ LOG_PRINT("Frame, Time, Touch_Flag, Data_Ready, Overflow_Flag, Overflow_Num, ppg0, ppg1, ppg2, Expo_A, Expo_B, Expo_C, MEMS_X, MEMS_Y, MEMS_Z\n");
+ _main.debug_print_header = false;
+ }
+
+ LOG_PRINT("%d, %d, %d, %d, %d, %d, %d, %d, %d, %d, %d, %d, %d, %d, %d\n",
+ ppg_mems_data_alg->ppg_frame_count, ppg_mems_data_alg->duration, ppg_mems_data_alg->touch_flag,
+ ppg_mems_data_alg->ppg_data.has_data, ppg_mems_data_alg->ppg_data.has_overflow, ppg_mems_data_alg->ppg_data.overflow_num,
+ ppg_mems_data_alg->ppg_data.data[0], ppg_mems_data_alg->ppg_data.data[1], ppg_mems_data_alg->ppg_data.data[2],
+ ppg_mems_data_alg->ppg_data.exposure_time[0], ppg_mems_data_alg->ppg_data.exposure_time[1], ppg_mems_data_alg->ppg_data.exposure_time[2],
+ (int)(ppg_mems_data_alg->MEMS_Data[0]), (int)(ppg_mems_data_alg->MEMS_Data[1]), (int)(ppg_mems_data_alg->MEMS_Data[2]));
+}
+
+static void report_hr_data(const ppg_mems_data_t *ppg_mems_data_alg, int32_t ret, float hr, float hr_grader)
+{
+ if (_main.debug_print_header == true)
+ {
+ LOG_PRINT("Frame, Time, Touch_Flag, Data_Ready, Overflow_Flag, Overflow_Num, PPG0, PPG1, PPG2, Expo_A, Expo_B, Expo_C, MEMS_X, MEMS_Y, MEMS_Z, ret, hr, hr_grade\n");
+
+ _main.debug_print_header = false;
+ }
+
+ LOG_PRINT("%d, %d, %d, %d, %d, %d, %d, %d, %d, %d, %d, %d, %d, %d, %d, %d, %f, %f\n",
+ ppg_mems_data_alg->ppg_frame_count, ppg_mems_data_alg->duration, ppg_mems_data_alg->touch_flag,
+ ppg_mems_data_alg->ppg_data.has_data, ppg_mems_data_alg->ppg_data.has_overflow, ppg_mems_data_alg->ppg_data.overflow_num,
+ ppg_mems_data_alg->ppg_data.data[0], ppg_mems_data_alg->ppg_data.data[1], ppg_mems_data_alg->ppg_data.data[2],
+ ppg_mems_data_alg->ppg_data.exposure_time[0], ppg_mems_data_alg->ppg_data.exposure_time[1], ppg_mems_data_alg->ppg_data.exposure_time[2],
+ (int)(ppg_mems_data_alg->MEMS_Data[0]), (int)(ppg_mems_data_alg->MEMS_Data[1]), (int)(ppg_mems_data_alg->MEMS_Data[2]), ret, hr, hr_grader);
+
+}
+
+//static void report_spo2_data(const ppg_mems_data_t *ppg_mems_data_alg, int32_t ret, float mySpO2, float myHR, float correlation, float trust_flag, float MEMS_RMS, int data_buf_count_spo2_alg, , float Opt_SpO2, int drop_flag, int ascend_flag, int mems_flag)
+static void report_spo2_data(const ppg_mems_data_t *ppg_mems_data_alg, int32_t ret, float myHR, float mySpO2, float correlation, float trust_flag, float MEMS_RMS, int data_buf_count_spo2_alg, uint8_t no_value_reason, float SignalPeriodicity, int time_accu )
+{
+ if (_main.debug_print_header == true)
+ {
+
+ LOG_PRINT("Frame, Time, Touch_Flag, Data_Ready, Overflow_Flag, Overflow_Num, PPG0, PPG1, PPG2, Expo_A, Expo_B, Expo_C, MEMS_X, MEMS_Y, MEMS_Z, ret, spo2, myHR, hr_grader, correlation, MEMS_RMS, trust, no_value_reason, SignalPeriodicity, time_accu \n");
+ _main.debug_print_header = false;
+ }
+
+ if(data_buf_count_spo2_alg>=25)
+ {
+ LOG_PRINT("%d, %d, %d, %d, %d, %d, %d, %d, %d, %d, %d, %d, %d, %d, %d, %d, %f, %f, %f, %f, %f, %f, %d, %f, %d\n",
+ ppg_mems_data_alg->ppg_frame_count, ppg_mems_data_alg->duration, ppg_mems_data_alg->touch_flag,
+ ppg_mems_data_alg->ppg_data.has_data, ppg_mems_data_alg->ppg_data.has_overflow, ppg_mems_data_alg->ppg_data.overflow_num,
+ ppg_mems_data_alg->ppg_data.data[0], ppg_mems_data_alg->ppg_data.data[1], ppg_mems_data_alg->ppg_data.data[2],
+ ppg_mems_data_alg->ppg_data.exposure_time[0], ppg_mems_data_alg->ppg_data.exposure_time[1], ppg_mems_data_alg->ppg_data.exposure_time[2],
+ (int)(ppg_mems_data_alg->MEMS_Data[0]), (int)(ppg_mems_data_alg->MEMS_Data[1]), (int)(ppg_mems_data_alg->MEMS_Data[2]), ret, mySpO2,myHR,hr_grader,correlation, MEMS_RMS,trust_flag,no_value_reason,SignalPeriodicity, time_accu);
+ }
+ else
+ {
+ LOG_PRINT("%d, %d, %d, %d, %d, %d, %d, %d, %d, %d, %d, %d, %d, %d, %d, %d\n",
+ ppg_mems_data_alg->ppg_frame_count, ppg_mems_data_alg->duration, ppg_mems_data_alg->touch_flag,
+ ppg_mems_data_alg->ppg_data.has_data, ppg_mems_data_alg->ppg_data.has_overflow, ppg_mems_data_alg->ppg_data.overflow_num,
+ ppg_mems_data_alg->ppg_data.data[0], ppg_mems_data_alg->ppg_data.data[1], ppg_mems_data_alg->ppg_data.data[2],
+ ppg_mems_data_alg->ppg_data.exposure_time[0], ppg_mems_data_alg->ppg_data.exposure_time[1], ppg_mems_data_alg->ppg_data.exposure_time[2],
+ (int)(ppg_mems_data_alg->MEMS_Data[0]), (int)(ppg_mems_data_alg->MEMS_Data[1]), (int)(ppg_mems_data_alg->MEMS_Data[2]), ret);
+
+
+ }
+}
+
+
+static void report_stress_data(const ppg_mems_data_t *ppg_mems_data_alg, uint8_t Stress_ReadyFlag, uint32_t measuretime, float collect_time)
+{
+ if (_main.debug_print_header == true)
+ {
+ LOG_PRINT("Frame, Time, Touch_Flag, Data_Ready, Overflow_Flag, Overflow_Num, PPG0, PPG1, PPG2, Expo_A, Expo_B, Expo_C, MEMS_X, MEMS_Y, MEMS_Z, Stress_Ready, measure_time, collect_time\n");
+
+ _main.debug_print_header = false;
+ }
+
+ LOG_PRINT("%d, %d, %d, %d, %d, %d, %d, %d, %d, %d, %d, %d, %d, %d, %d, %d, %d, %f\n",
+ ppg_mems_data_alg->ppg_frame_count, ppg_mems_data_alg->duration, ppg_mems_data_alg->touch_flag,
+ ppg_mems_data_alg->ppg_data.has_data, ppg_mems_data_alg->ppg_data.has_overflow, ppg_mems_data_alg->ppg_data.overflow_num,
+ ppg_mems_data_alg->ppg_data.data[0], ppg_mems_data_alg->ppg_data.data[1], ppg_mems_data_alg->ppg_data.data[2],
+ ppg_mems_data_alg->ppg_data.exposure_time[0], ppg_mems_data_alg->ppg_data.exposure_time[1], ppg_mems_data_alg->ppg_data.exposure_time[2],
+ (int)(ppg_mems_data_alg->MEMS_Data[0]), (int)(ppg_mems_data_alg->MEMS_Data[1]), (int)(ppg_mems_data_alg->MEMS_Data[2]), Stress_ReadyFlag,duration_total, _hrv_state.collect_duration );
+
+}
+#ifdef PPG_MEMS_Synchronize
+uint8_t get_ppg_data_num(void)
+{
+ return ppg_data_num;
+}
+void clear_ppg_data_num(void)
+{
+ ppg_data_num = 0;
+}
+
+void mems_interpolation(int16_t *mems_int_data_in,uint8_t mems_num,uint8_t ppg_num)
+{
+
+ // Customer Define MEMS-input X, Y, Z
+ // Put data in mems_int_data_in ,and mems_count
+ // Then call the PXI_MEMS_interp to do interpolation
+
+ PXI_MEMS_interp(mems_int_data_in, mems_num, mems_int_data_out, ppg_num);
+
+}
+#endif
+
+static void error_handle(void)
+{
+ while (true)
+ {
+ }
+}
+
+
+
+uint16_t pah8009_get_chip_id(void)
+{
+ uint16_t product_id = 0;
+ pah_800x_get_product_id(&product_id);
+ return product_id;
+}
+
+int32_t pah8009_get_factory_data(void)
+{
+ int32_t data = factory_data;
+ factory_data = 0;
+ return data;
+}
+
+void pah8009_int_handler(void)
+{
+ timeout = 1;
+ ppg_interrupt_handle();
+ ppg_sensor_task_interrupt();
+
+ if(work_mode == 1)
+ {
+ // PRINT("hr_alg_task \n");
+ hr_alg_task();
+ }
+ else if(work_mode == 2)
+ {
+ // PRINT("spo2_alg_task \n");
+ spo2_alg_task();
+ }
+
+}
+
+void pah8009_check_int(void)
+{
+ if(work_mode > 3 || work_mode < 1)
+ {
+ return;
+ }
+
+ if(!timeout)
+ {
+ // printf("pah8009_check_int \n");
+#ifdef SUPPORT_PPG_SERVICE
+ send_ppg_cmd_msg(PPG_SERVICE_CMD_TYPE_INT_HANDLER);
+#endif
+
+ }
+ else
+ {
+ timeout = 0;
+ }
+}
+
+void pah8009_timer_handler(void)
+{
+ // printf("pah8009_timer_handler \n");
+ ppg_sensor_task_polling();
+
+ if(work_mode == 1)
+ {
+ // PRINT("hr_alg_task \n");
+ hr_alg_task();
+ }
+ else if(work_mode == 2)
+ {
+ // PRINT("spo2_alg_task \n");
+ spo2_alg_task();
+ }
+
+}
+
+// static uint32_t last_exec_time = 0;
+
+static void pah8009_timer_callback(void)
+{
+ // printf("pah8009_timer_callback\n");
+#ifdef SUPPORT_PPG_SERVICE
+ send_ppg_cmd_msg(PPG_SERVICE_CMD_TYPE_DO_PPG_HRS_TIMER_CALLBACK);
+#endif
+ // pah8009_check_int();
+}
+
+static void pah8009_timer_init(void)
+{
+ if(g_pah8009_timer == NULL)
+ g_pah8009_timer = osTimerNew((osTimerFunc_t)pah8009_timer_callback, osTimerPeriodic, NULL, NULL);
+}
+
+
+void pah8009_reset(void)
+{
+ pah_sensor_stop();
+ pah_sensor_init(diw_4mm_g_ir_hrd);
+ ppg_only_sensor_start();
+}
+
+void pah8009_control(char *mode,uint8_t enable)
+{
+ // printf("mode : %s ,enable : %d\n",mode,enable);
+ if(!enable)
+ {
+
+ if(g_pah8009_timer != NULL && work_mode < 4 && work_mode > 0)
+ osTimerStop(g_pah8009_timer);
+
+ pah_sensor_stop();
+ work_mode = 0;
+ if(uapi_dev_runtime_get_sleep_monitoring_status() && uapi_dev_runtime_get_hrs_passivity_flag())
+ {
+ pah8009_wearstatus_update(history_wear_status);
+ uapi_dev_runtime_set_hrs_passivity(0);
+ }
+ }
+ else
+ {
+ if (strcmp(mode, "hrs") == 0) {
+ pah_sensor_init(diw_4mm_g_ir_hrd);
+ // auto_mode_sensor_start();
+ work_mode = 1;
+ // touch_only_sensor_start();
+ ppg_only_sensor_start();
+ time_out = 15;
+ } else if (strcmp(mode, "hrv") == 0) {
+ // md = HRV_MODE;
+ } else if (strcmp(mode, "spo2") == 0) {
+ // md = SPO2_MODE;
+ pah_sensor_init(diw_4mm_ir_r_ir_spo2);
+ ppg_only_sensor_start();
+ work_mode = 2;
+ time_out = 15;
+ } else if (strcmp(mode, "leaklight") == 0) {
+ // md = FT_LEAK_LIGHT_MODE;
+ factory_test_mode(factory_test_leak_light_target_sample, 0, 0, 0, 0);
+ // extern void test_library_HRD(void);
+ // test_library_HRD();
+ work_mode = 4;
+ } else if (strcmp(mode, "wear") == 0) {
+ pah_sensor_init(diw_4mm_g_ir_hrd);
+ touch_only_sensor_start();
+ work_mode = 3;
+ time_out = 5000;
+ }
+
+ if(work_mode < 4 && work_mode > 0)
+ pah8009_timer_init();
+
+ time_cnt = 0;
+
+ if(g_pah8009_timer != NULL && work_mode < 4 && work_mode > 0)
+ {
+ // printf("time_out : %d\n",time_out);
+ osTimerStart(g_pah8009_timer,time_out);
+ }
+
+
+ }
+}
+
+uint8_t pah8009_get_hrs(void)
+{
+ return avg_hrs;
+}
+
+uint8_t pah8009_get_spo2(void)
+{
+ return (uint8_t)mySpO2;
+}
+
+uint8_t pah8009_get_wear_status(void)
+{
+ return history_wear_status;
}
\ No newline at end of file