/******************************************************************************
版权所有：	
文件名称：	xdljd.c
文件版本：	01.01
创建作者:	sunxi
创建日期：	2016-12-22
功能说明：	小电流接地检测
其它说明：	!!!!!在不了解整个原理的情况下，不要轻易修改此文件中的算法!!!!!!
修改记录：
*/
/*------------------------------- 头文件 --------------------------------------
*/
#include "head.h"
#ifdef XDL_ZT
/*------------------------------- 宏定义 --------------------------------------
*/
#define XDL_ZERO_LEN		(ADC_REC_SAMPLE/4)

#define XDL_SEG_NUM			10

enum
{
	XDL_ST_IDLE,
	XDL_ST_QT,
	XDL_ST_END,
};

/*------------------------------ 类型结构 -------------------------------------
*/

struct point
{
 	s32 v;
 	u32	p;
};

struct segment
{
	u32 bgn;	// 段开始
	u32 end;	// 段结束
	u32 acc;	// 累加值
	u32 max_p;	// 最大值出现的位置
	u32 max_v;	// 最大值的绝对值
	s32 dir;	// 方向
};

struct xdl
{
	// 状态
	int st;
	unsigned long qd_us0;
	unsigned long dz_us0;
	unsigned long gj_us0;
	unsigned long dz_fh_us0;
	// unsigned long gj_fh_us0;

	// 动作
	int dz;

	// 动作值记忆
	int dz_i0[2];
	int dz_u0[2];
	int dz_Ia[2];
	int dz_Ib[2];
	int dz_Ic[2];

	// 零偏
	s16 zero_sum;
	s16 zero_cnt;

};

struct i0_argument
{
	s16 ref_v_set;
	bool scale_is_1A;
};

/*------------------------------ 全局变量 -------------------------------------
*/

// 零偏
//s16 g_xdl_zero_sum[SWITCH_NUM_MAX]; // 零偏和
//s16 g_xdl_zero_cnt[SWITCH_NUM_MAX]; // 零偏计数

//int g_xdl_qd[SWITCH_NUM_MAX]; 	// 小电流接地启动
//int g_xdl_dz[SWITCH_NUM_MAX];	// 小电流接地动作
//unsigned long g_xdl_qd_us0[SWITCH_NUM_MAX]; 	// 小电流接地启动时间

struct xdl g_xdl[SWITCH_NUM_MAX];
struct i0_argument g_i0_arg[SWITCH_NUM_MAX];

// 滤波后数据
s16 g_u0[XDL_SAMPLE_LEN];
s16 g_i0[XDL_SAMPLE_LEN];

// 原始的数据
s16 g_u0_raw[XDL_SAMPLE_LEN];
s16 g_i0_raw[XDL_SAMPLE_LEN];

// 小波处理的数据
float g_u0_dwt[XDL_SAMPLE_LEN];
float g_i0_dwt[XDL_SAMPLE_LEN];

struct segment g_u0_seg[XDL_SEG_NUM];
struct segment g_i0_seg[XDL_SEG_NUM];

// 小电流接地零压零流定时器
#ifdef RECORD_SW_U0TB
TIMERELAY 	g_tXDLTime_U0TB[SWITCH_NUM_MAX];  //零压突变定时器
#else
TIMERELAY 	g_tXDLTime_U[2]; 
#endif
TIMERELAY 	g_tXDLTime_I[SWITCH_NUM_MAX]; 

int g_cur_direction_adaptaion = 0; //临时在此定义 EWen  // TODO 方向自适应处理

int XDLCHZWC = 0;


char * g_xdl_rcd_a[] =
{
	"U0_原始",
	"U0_滤波",
	"U0_区域",
	"U0_最大",
	"I0_原始",
	"I0_滤波",
	"I0_区域",
	"I0_最大",
};

 
#define XDL_RCD_A_NUM (sizeof(g_xdl_rcd_a)/sizeof(char *))

const char * g_line_type[] =
{
	"界外动作:",
	"界内动作:",
};

const char * g_tbl_type[] =
{
	"小电流突变",
	"零压突变",
};

/*------------------------------ 函数声明 -------------------------------------
*/
#if 0
static void xdl_ar_dataset(void);
static void xdl_ar_recharge(DWORD  dStep);
static void xdl_ar_disable(void);
static void xdl_ar_start(DWORD  dStep);
static void xdl_ar_logic(DWORD  dStep);
static void xdl_ar_reset(DWORD  dStep);
static int xdl_ar_check(DWORD mode);			// 检同期	
#endif
static u32  xdl_get_LLTB_waves(int sw);
/*------------------------------ 外部函数 -------------------------------------
外部函数供其它实体文件引用，必须仔细检查传入参数的合法性.
*/
#if 0
/**************************************************************************
函数名称: xdl_auto_reclose
函数版本：1.00 
作者：    赵海洋
创建日期：2021.6.10    
函数功能说明：小电流重合闸功能块
输入参数：
输出参数：
返回值： 
***************************************************************************/
void xdl_auto_reclose(DWORD  dStep)
{
	xdl_ar_dataset();               	// 小电流重合闸数据准备
	xdl_ar_recharge(dStep);             // 小电流重合闸充电
	xdl_ar_disable();                   // 小电流重合闸闭锁
	xdl_ar_start(dStep);                // 小电流重合闸启动
	xdl_ar_logic(dStep);                // 小电流重合闸逻辑
	xdl_ar_reset(dStep);                // 小电流重合闸复归
	return;
}

/**************************************************************************
函数名称: xdl_ac_check
函数版本：1.00 
作者：    赵海洋
创建日期：2021.6.10    
函数功能说明：小电流重合闸加速块
输入参数：
输出参数：
返回值： 
***************************************************************************/
void xdl_ac_check(int sw,DWORD  dStep)
{
	static bool bFZOut, bQD=false;
	bool bQDD;  			
	TRELAY_T *pR=&g_tRelay[sw];
	TXDLCH_T *pSW=&pR->tXDLCHZ;
	TSWST *pSWST =&pR->tSWST;
	TSETSW *pSet = &pRunSet->tSwSet[sw];
	

	/***********启动	**********************************/	
	// 开放后加速允许有效延时3S展宽
	RunTR(&pSW->tTXDLCHJLY_HJS, XDLCHZWC, dStep); 

	// 时间到
	if (pSW->tTXDLCHJLY_HJS.boolTrip)
	{
		XDLCHZWC =0;
		ResetTR(&pSW->tTXDLCHJLY_HJS);  
		if (bFZOut)
		{
			soe_record_ev(EV_OVERU0+sw*EV_SW_NUM, 0, 0,0,0 ); 
			bFZOut = 0; 
		}
	}

	if(pRunSet->tSwSet[sw].bTT_Power_v2)
	{
		bQDD = OverRelay(g_ui[PUB_AC_U02].m2[0],pRunSet->dOverU0,pRunSet->dOverU0_fh,bQD);

	}
	else
	{
		bQDD = OverRelay(g_ui[PUB_AC_U01].m2[0],pRunSet->dOverU0,pRunSet->dOverU0_fh,bQD);
	}
	
	
	bQD = 					BH_ALL_EN(sw)
							&& pSet->bTT_xdltz//投退字
							&& pSWST->uSWST.bFlag.bHZWZ		//在合闸位置
							&& bQDD
							&& XDLCHZWC
							&& !pSW->tTXDLCHJLY_HJS.boolTrip;							
	/************事件记录及出口****************************/
	if(bQD) 
	{
		if (!bFZOut)				// 保证只动作一次未进行跳闸失败检测
		{
			DWORD U0;
			sw_do(sw,SW_DO_FZ,SW_DO_TYPE_ON);
		
			if(pRunSet->tSwSet[sw].bTT_Power_v2)
			{
				U0 = _Mul_Div_U(sqrt_32fix(g_ui[PUB_AC_U02].m2[0]), 256, g_ui[PUB_AC_U02].m2_factor_k);

			}
			else
			{
				U0 = _Mul_Div_U(sqrt_32fix(g_ui[PUB_AC_U01].m2[0]), 256, g_ui[PUB_AC_U01].m2_factor_k);
			}					
			soe_record_ev( EV_OVERU0+sw*EV_SW_NUM, 1, U0,0,0 ); 
			bFZOut = 1;
			rt_printf("开关%d小电流检零压跳闸\r\n", sw);
		}
	}
	
}

/**************************************************************************
函数名称: xdl_ar_dataset
函数版本：1.00 
作者：    赵海洋
创建日期：2021.6.10    
函数功能说明：小电流重合闸数据准备
输入参数：
输出参数：
返回值： 
***************************************************************************/
static void xdl_ar_dataset(void)
{
	u32 sw = g_protect.sw;	
	TRELAY_T *pR=&g_tRelay[sw];
	TXDLCH_T *pSW =&pR->tXDLCHZ;

	InitTR_Time(&pSW->tTXDLCHJD_ARDelay,	10*T_1s,	0   );        	   // 小电流重合闸延时
	InitTR_Time(&pSW->tTXDLCHFAIL_TW,		11*T_1s,	0   );         	   // 小电流重合闸延时
}

/**************************************************************************
函数名称: xdl_ar_disable
函数版本：1.00 
作者：    赵海洋
创建日期：2021.6.10    
函数功能说明：小电流重合闸闭锁
输入参数：
输出参数：
返回值： 
***************************************************************************/
static void xdl_ar_disable(void)
{
	u32 sw = g_protect.sw;	
	TRELAY_T *pR=&g_tRelay[sw];
	TXDLCH_T *pSW =&pR->tXDLCHZ;
	pSW->sta.bFlag.bBSCH = pR->tOC[OC_LX1].sta.bFlag.bBSCH 		// 零序1闭锁重合闸
					    || pR->tOC[OC_LX2].sta.bFlag.bBSCH 		// 零序2闭锁重合闸
						|| pR->tOC[OC_LX3].sta.bFlag.bBSCH 		// 零序3闭锁重合闸
			            || pR->tLostVot.uLostVot.bFlag.bTz		// 失压跳闸闭锁重合
			            || pR->tXXTZ.uZOV.bFlag.bTz
			            || pR->tU0TZ.uZOV.bFlag.bTz ;			// 零压分闸闭锁重合
}

/**************************************************************************
函数名称: xdl_ar_recharge
函数版本：1.00 
作者：    赵海洋
创建日期：2021.6.10    
函数功能说明：小电流重合闸充电
输入参数：
输出参数：
返回值： 
***************************************************************************/
static void xdl_ar_recharge(DWORD  dStep)
{
	u32 sw = g_protect.sw;	
	TRELAY_T *pR=&g_tRelay[sw];
	TXDLCH_T *pSW =&pR->tXDLCHZ;
	TSETSW *pSet = &pRunSet->tSwSet[sw];	
	bool bHCD1,bHCD2,bHCD3,bHCD4;

	// 放电条件
	RunTR(&pSW->tTXDLCHFAIL_TW, pR->tSWST.uSWST.bFlag.bTZWZ, dStep);    	// 三相跳位延时
	bHCD1 = pSW->tTXDLCHFAIL_TW.boolTrip;									// 放电标志1:三相跳位时间到

	bHCD2 =  (pR->tSWST.uSWST.bFlag.bTZWZ || pR->uBHDZ.bFlag.bBHT) 		   // 此处小电流动作标志或者有保护动作  
							&& (!pSW->sta.bFlag.bCDWC);                // 放电标志2:充电未完成，小电流动作

	bHCD3 = pR->uRmtSW.bHandTz							// 手动跳闸
							|| pR->uRmtSW.bYTExecute                   	// 遥控跳闸
							|| pR->uBHDZ.bFlag.bTZSB					// 跳闸失败
							|| pR->tSWST.uSWST.bFlag.bTWYL				// 跳位有流
							|| pR->tSWST.uSWST.bFlag.bWCN               // 弹簧未储能告警							
							|| pSW->sta.bFlag.bBSCH					// 重合闸闭锁条件
							|| pSW->sta.bFlag.bCHFD;                  // 重合闸动作放电
	
	pSW->sta.bFlag.bFDBZ = (!BH_ALL_EN(sw))							// 总保护未投入
							|| (!pSet->bTT_xdltz)         			// 小电流跳闸: 未投入
							|| (!pR->run_stu.chz)							// 重合闸硬压板未投入
							|| bHCD1 
							|| bHCD2
							|| bHCD3;
						

	// 充电条件
	bHCD4 = !pSW->sta.bFlag.bFDBZ                             // 重合放电标
			&& (((!pR->uBHQD.bFlag.bZQD) && pR->tSWST.uSWST.bFlag.bHZWZ )    // 非保护启动 && 合闸位置
			|| pSW->sta.bFlag.bCDWC);                          // 充电完成自保持

	RunTR(&pSW->tTXDLCHCD15s0s, bHCD4, dStep);                // 充电15秒
	pSW->sta.bFlag.bCDWC = pSW->tTXDLCHCD15s0s.boolTrip;

	if(pSW->sta.bFlag.bCDWC)
	{
		// TODO:液晶应增加小电流重合闸标志
		if((g_disp_flag.xdl_chz_cd & (1<<sw)) == 0)
		{
			g_disp_flag.xdl_chz_cd |= 1<<sw;
			rt_printf_time("开关%02d小电流充电完成!\r\n",sw+1);
		}
	}
	else
	{
		if((g_disp_flag.xdl_chz_cd & (1<<sw)))
		{
			g_disp_flag.xdl_chz_cd &= ~(1<<sw);
			pSW->sta.bFlag.bXDLDZ = false;
			pR->tXDLCHZ.sta.wfFlag=0;
			pR->tXDLCHZ.wAR_ActTimes=0;	
   			ResetTR(&pR->tXDLCHZ.tTXDLCHJD_HJS);
			rt_printf_time("开关%02d小电流放电完成!\r\n",sw+1);
		}
	}
}

/**************************************************************************
函数名称: xdl_ar_start
函数版本：1.00 
作者：    赵海洋
创建日期：2021.6.10    
函数功能说明：小电流重合闸启动
输入参数：
输出参数：
返回值： 
***************************************************************************/
static void xdl_ar_start(DWORD  dStep)
{
	bool bCHQD_CHQD;    // 三相启动重合闸
	u32 sw = g_protect.sw;	
	TRELAY_T *pR=&g_tRelay[sw];
	TXDLCH_T *pSW =&pR->tXDLCHZ;
	TSETSW *pSet = &pRunSet->tSwSet[sw];
	bool bCQD1,bCQD2,bCQD3,bCQD4,bCQD5,bCQD6;

    // 不对应启动
    // 开关位置从合闸位置切换至分闸位置完成后，保留50ms展宽
	RunTR(&pSW->tTXDLCHQDHW0ms50ms, pR->tSWST.uSWST.bFlag.bHZWZ, dStep); 
	bCQD1 = pSW->tTXDLCHQDHW0ms50ms.boolTrip;                              

	// 偷跳条件准备                                                                    
	bCQD2 =   pSet->bTT_TTCH
			                   && bCQD1                                         
			                   && (!pR->tSWST.uSWST.bFlag.bHZWZ);                                     

	// 保护启动	                           
	// 保护动作后至保护动作返回，保留50ms展宽
	RunTR(&pSW->tTXDLCHQD0ms50ms, pR->uBHDZ.bFlag.bBHT, dStep); 
	bCQD3 = pSW->tTXDLCHQD0ms50ms.boolTrip;                              

	// 重合已启动或保护启动，用于闭锁不对应启动                                                                    
	bCQD4 = pSW->sta.bFlag.bCHQD ||bCQD3;
	                                                                   
	// 此处保护跳信号，是实时信号，即保护已返回
	bCQD5 = bCQD3 && (!pR->uBHDZ.bFlag.bBHTrip);      

	// 重合启动自保持
	// 重合闸出口条件，用于防止重合闸合闸过程中，TWJ没有返回时，再次误发启动重合闸报文。
	bCQD6 = pSW->sta.bFlag.bCDWC             // 重合充电完成
		                   && pSW->sta.bFlag.bCHQD            // 三相启动重合闸
		                   && (!pSW->sta.bFlag.bCHCK);        // 重合闸出口，取反
	                                                                   
	// 偷跳启动重合闸                                                   
	pSW->sta.bFlag.bTTQD =  pSW->sta.bFlag.bCDWC         	// 重合闸充电完成
		                   && bCQD2            // 偷跳判断完成                
		                   && (!bCQD4);        // 重合未启动，无保护跳过程

	// 重合闸检同期
	// bCQD7 =xdl_ar_check(pSet->dCKModes);

	// 三跳启动重合闸         
	#if 0                                         
	pSW->sta.bFlag.bTLQD =  pSW->sta.bFlag.bCDWC            // 重合闸充电完成
						   &&bCQD7            				// 湖南专检重合闸检同期
						   &&pSW->sta.bFlag.bXDLDZ            				// 
		                   && (!pSW->sta.bFlag.bCHQD);        // 三相启动重合闸未启动
	#else
	pSW->sta.bFlag.bTLQD =  pSW->sta.bFlag.bCDWC            // 重合闸充电完成
						   &&pSW->sta.bFlag.bXDLDZ            				// 
		                   && (!pSW->sta.bFlag.bCHQD);        // 三相启动重合闸未启动
	#endif
	// 三相启动重合闸
	bCHQD_CHQD =  pSW->sta.bFlag.bTTQD                        // 偷跳启动重合闸
	           || pSW->sta.bFlag.bTLQD                        // 三跳启动重合闸
	           || bCQD6;                       // 三相启动重合闸

	if( bCHQD_CHQD )
	{
		if( !pSW->sta.bFlag.bCHQD )                           //重合开出标尚未置位
		{
			pSW->sta.bFlag.bCHQD = true;						//此标志会一直维持到重合闸出口或充电标志放电
			pSW->sta.bFlag.bQDFG=true;						//整组复归时有效
			soe_record_ev(EV_AR_START+sw*EV_SW_NUM, 1, 0,0,0);//重合启动SOE         
			//rcd_start(sw,RECORD_TYPE_CHZ, RECORD_LEN_TZQD); 	//录波类型：跳闸类
		}        
	}
	else
	{
		if( pSW->sta.bFlag.bCHQD )
		{
			pSW->sta.bFlag.bCHQD = false;
			soe_record_ev(EV_AR_START+sw*EV_SW_NUM,0 ,0,0,0);   
		}
	}
}

/**************************************************************************
函数名称: xdl_ar_logic
函数版本：1.00 
作者：    赵海洋
创建日期：2021.6.10    
函数功能说明：小电流重合闸逻辑
输入参数：
输出参数：
返回值： 
***************************************************************************/
static void xdl_ar_logic(DWORD  dStep)
{
	bool bWCN;
	u32 sw = g_protect.sw;	
	TRELAY_T *pR=&g_tRelay[sw];
	TXDLCH_T *pSW =&pR->tXDLCHZ;
	TSETSW *pSet = &pRunSet->tSwSet[sw];

	bool bHZL1,bHZL2,bHZL3,bHZL4,bHZL5,bHZL6,bHZL8,bHZL9;

	// 重合闸条件准备
	bHZL1 = BH_ALL_EN(sw)				// 总投退
							&& pSet->bTT_xdltz         	// 小电流跳闸投入
							&& pR->run_stu.chz			// 重合闸硬压板投入
							&& pSW->sta.bFlag.bCHQD   // 三相启动重合闸
							&& pSW->sta.bFlag.bCDWC;  // 重合闸充电完成
	                          
	RunTR(&pSW->tTXDLCHJD_ARDelay, bHZL1, dStep);    //重合延时
	bHZL2 = pSW->tTXDLCHJD_ARDelay.boolTrip;

	// 弹簧未储能下降沿展宽30ms
	bWCN = pR->run_stu.wcn && pSet->bTT_WCN;
	RunTR(&pSW->tTXDLCHJD_0ms30ms, bWCN, dStep);
	bHZL3 = pSW->tTXDLCHJD_0ms30ms.boolTrip;

	// 重合闸检同期
	bHZL9 =xdl_ar_check(pSet->dCKModes);

	// 重合闸条件准备好，弹簧储能OK，重合闸出口
	pSW->sta.bFlag.bCHCK = (!bHZL3) && bHZL2 && bHZL9;
	
	// 重合闸开出自保持
	bHZL4 = pSW->sta.bFlag.bHz || pSW->sta.bFlag.bCHCK;

	// 重合开出持续200ms
	RunTR(&pSW->tTXDLCHJD_200ms0ms, bHZL4, dStep);    
	bHZL5 = pSW->tTXDLCHJD_200ms0ms.boolTrip;                        

	// 重合闸开出,经过200ms后开出解除
	pSW->sta.bFlag.bHz =bHZL4 && (!bHZL5);                   

	// 重合闸指令发出后，且开关已经跳闸完成
	bHZL6 = pSW->sta.bFlag.bHz || pSW->sta.bFlag.bHZL7;
	pSW->sta.bFlag.bHZL7 = pR->tSWST.uSWST.bFlag.bTZWZ && bHZL6;

	// 开放后加速允许有效延时200ms展宽
	RunTR(&pSW->tTXDLCHJD_HJS, pSW->sta.bFlag.bHZL7, dStep);                
	bHZL8 = pSW->tTXDLCHJD_HJS.boolTrip;	
       pSW->sta.bFlag.bCHJS = pSW->tTXDLCHJD_HJS.boolTrip
                   			&& (!pR->tSWST.uSWST.bFlag.bTZWZ);

	//出口及发信
	if(pSW->sta.bFlag.bHz)
	{
		if(!pSW->sta.bFlag.bEvent)    //重合开出标尚未置位
		{   		
			// 检零压后加速标识
			XDLCHZWC = 1;

			//亮重合动作灯
			pSW->sta.bFlag.bCHZLed = true;
			
			pSW->wAR_ActTimes++;    //重合动作次数
			pSW->sta.bFlag.bEvent = true;
			//pSW->uCHFG.wfFlag=0;			//每次重合出口后，清重合复归标志  修改时注意，此处需对标志字特殊处理

			pSW->sta.bFlag.bCHDZ=false;    	//重合闸动作   
			pSW->sta.bFlag.bBYSD=false;    	//重合闸闭锁时间到 
			pSW->sta.bFlag.bCHFD=false;    	//重合闸动作放电   
			pSW->sta.bFlag.bCHBS=false;    	//重合闸闭锁   
			pSW->sta.bFlag.bCK=false;		//放电条件1			

			if(pSW->wAR_ActTimes==1)
			{
				soe_record_ev(EV_AR1+sw*EV_SW_NUM, 1, 0,0,0);                   //重合动作SOE
			}
			else if(pSW->wAR_ActTimes==2)
			{
				soe_record_ev(EV_AR2+sw*EV_SW_NUM, 1, 0,0,0);                   //重合动作SOE
			}
			else
			{
				soe_record_ev(EV_AR3+sw*EV_SW_NUM, 1, 0,0,0);                   //重合动作SOE
			}
			rcd_start(sw,RECORD_TYPE_CHZ,RECORD_LEN_TZQD);  
			ResetTR(&pSW->tTXDLCHFAIL_TW); // 重合出口后,重新复归时间继电器
			ResetTR(&pSW->tTXDLCHOKTime);	//重合成功时间继电器，在重合出口后重新计数
			ResetTR(&pSW->tTXDLCHBSDelay);		//闭锁重合延时继电器重新计数
			
		}
	}
	else
	{
		if( pSW->sta.bFlag.bEvent )                                    //重合开出标尚未置位
		{   
			pSW->sta.bFlag.bEvent = false;
			
			if(soe_check(EV_AR1+sw*EV_SW_NUM))
			{
				soe_record_ev(EV_AR1+sw*EV_SW_NUM, 0, 0,0,0  );        //重合1动作SOE返回
			}
			if(soe_check(EV_AR2+sw*EV_SW_NUM))
			{
				soe_record_ev(EV_AR2+sw*EV_SW_NUM, 0, 0,0,0  );        //重合2动作SOE返回
			}
			if(soe_check(EV_AR3+sw*EV_SW_NUM))
			{
				soe_record_ev(EV_AR3+sw*EV_SW_NUM, 0, 0,0,0  );        //重合3动作SOE返回
			}	
		}
	}
}

/**************************************************************************
函数名称: xdl_ar_reset
函数版本：1.00 
作者：    赵海洋
创建日期：2021.6.10    
函数功能说明：小电流重合闸复归
输入参数：
输出参数：
返回值： 
***************************************************************************/
static void xdl_ar_reset(DWORD  dStep)
{
	u32 sw = g_protect.sw;	
	TRELAY_T *pR=&g_tRelay[sw];
	TXDLCH_T *pSW =&pR->tXDLCHZ;
	bool bFD1;

	// 重合闸动作后，即启动重合闸闭锁时间，在此时间内，有保护动作即闭锁重合闸
	// 重合闸出口且重合闸动作次数<整定动作次数,重合闸闭锁
	pSW->sta.bFlag.bCHDZ = (pSW->sta.bFlag.bCHCK && (pSW->wAR_ActTimes<1))       
	                  		|| pSW->sta.bFlag.bCHBS;                              	

	// 重合闸闭锁延时,从重合闸动作开始统计                                                                    
	RunTR(&pSW->tTXDLCHBSDelay, pSW->sta.bFlag.bCHDZ, dStep);
	pSW->sta.bFlag.bBYSD = pSW->tTXDLCHBSDelay.boolTrip;
	                                                                     
	pSW->sta.bFlag.bCHBS = (pSW->wAR_ActTimes<1) // 未到最后一次重合
	                   		&& pSW->sta.bFlag.bCHDZ           // 一次重合闸动作
	                   		&& (!pSW->sta.bFlag.bBYSD);       // 重合闸闭锁时间未到

	// 重合闸出口后，经过重合闸确认时间，无保护动作，即放电，完成该次重合过程
	// 如果在确认时间内，重合闭锁时间外，有保护动作，即进入下一计数重合
	pSW->sta.bFlag.bCK = ((!pR->uBHDZ.bFlag.bBHT)
						&& (pSW->sta.bFlag.bCHCK || pSW->sta.bFlag.bCK));	//重合出口及自保持
	RunTR(&pSW->tTXDLCHOKTime, pSW->sta.bFlag.bCK, dStep);
	bFD1 = pSW->tTXDLCHOKTime.boolTrip;	

	// 重合闸闭锁时间内，有保护动作，放电
	// 重合闸次数已到最大，放电
	// 重合成功，重合闸确认时间内无保护动作，放电
	pSW->sta.bFlag.bCHFD = (pR->uBHDZ.bFlag.bBHT && pSW->sta.bFlag.bCHBS)
                   			|| (pSW->sta.bFlag.bCHCK && (pSW->wAR_ActTimes>=1))
                   			|| bFD1;

	// 整组复归条件:(1)重合闸放电(2)重合已启动，但充电标志已清零
	if(pSW->sta.bFlag.bCHFD					
	 	|| (!pSW->sta.bFlag.bCDWC && pSW->sta.bFlag.bQDFG))
	{
	 	pSW->sta.bFlag.bQDFG = false;	
	}
}

/**************************************************************************
函数名称: xdl_ar_check
函数版本：1.00 
函数版本：1.00 
作者：    赵海洋
创建日期：2021.06.10    

函数功能说明：重合闸检同期
			0：无检定；1：检一侧有压一侧无压；2：检同期；3：检一侧有压一侧无压+检同期		
输入参数：
输出参数：
返回值： 
***************************************************************************/
#if 1
static int xdl_ar_check(DWORD mode)
{
	// 0：无检定；1：检一侧有压一侧无压；2：检同期；3：检一侧有压一侧无压+检同期
	u32 sw = g_protect.sw;
	TSETSW *pSet = &pRunSet->tSwSet[sw];
	TRELAY_T *pR=&g_tRelay[sw];
	DWORD deltaU1=0,deltaU2=0,deltaU3=0;
	bool Volt,Volt1,Volt2,Volt3,angle1,bFEQ=false;
	bool bY1 ,bY2 , sp , bLock = false;
	int ret = 0;

	bY1= ((!g_run_stu.pt1yy) && g_run_stu.pt2yy); 
	bY2= (g_run_stu.pt1yy && (!g_run_stu.pt2yy));
	bLock = bY1 || bY2;

	switch (mode)
	{
		case 0: 
			ret =  1 ;
			break;		
		case 1: 
			/* 检一侧有压一侧无压 */
			ret = bLock;
			break;
		case 2:
			/* 检同期 */
			// 分别取出两侧各线电压压差
			deltaU1 = _AbsL(g_ui[PUB_AC_UAB1].fz - g_ui[PUB_AC_UAB2].fz);
			deltaU2 = _AbsL(g_ui[PUB_AC_UBC1].fz - g_ui[PUB_AC_UBC2].fz);
			deltaU3 = _AbsL(g_ui[PUB_AC_UCA1].fz - g_ui[PUB_AC_UCA2].fz);

			// 角差与定值比对
			angle1 = SynAngle(g_ui[PUB_AC_UAB1].p, g_ui[PUB_AC_UAB2].p,pSet->dTQ_ANGLE_FZ);

			// 频差与定值比对
			bFEQ = SynFrequency(g_sw_pub.ac_in[PUB_AC_IN_F1],g_sw_pub.ac_in[PUB_AC_IN_F2],pSet->dTQ_FREQ_FZ);

			// 压差与定值比对
			Volt1 = LowRelay(deltaU1, pSet->dTQ_U_FZ, pSet->dTQ_U_FZ_fh, false); 
			Volt2 = LowRelay(deltaU2, pSet->dTQ_U_FZ, pSet->dTQ_U_FZ_fh, false); 
			Volt3 = LowRelay(deltaU3, pSet->dTQ_U_FZ, pSet->dTQ_U_FZ_fh, false); 

			Volt = Volt1&&Volt2&&Volt3;

			sp = Volt&&angle1&&bFEQ&&(!pR->tSWST.uSWST.bFlag.bDIHW);
			ret = sp;
			break;	
		case 3: 
			/* 检一侧有压一侧无压+检同期	 */
			// 分别取出两侧各线电压压差
			deltaU1 = _AbsL(g_ui[PUB_AC_UAB1].fz - g_ui[PUB_AC_UAB2].fz);
			deltaU2 = _AbsL(g_ui[PUB_AC_UBC1].fz - g_ui[PUB_AC_UBC2].fz);
			deltaU3 = _AbsL(g_ui[PUB_AC_UCA1].fz - g_ui[PUB_AC_UCA2].fz);

			// 角差与定值比对
			angle1 = SynAngle(g_ui[PUB_AC_UAB1].p, g_ui[PUB_AC_UAB2].p,pSet->dTQ_ANGLE_FZ);

			// 频差与定值比对
			bFEQ = SynFrequency(g_sw_pub.ac_in[PUB_AC_IN_F1],g_sw_pub.ac_in[PUB_AC_IN_F2],pSet->dTQ_FREQ_FZ);

			// 压差与定值比对
			Volt1 = LowRelay(deltaU1, pSet->dTQ_U_FZ, pSet->dTQ_U_FZ_fh, false); 
			Volt2 = LowRelay(deltaU2, pSet->dTQ_U_FZ, pSet->dTQ_U_FZ_fh, false); 
			Volt3 = LowRelay(deltaU3, pSet->dTQ_U_FZ, pSet->dTQ_U_FZ_fh, false); 

			Volt = Volt1&&Volt2&&Volt3;

			sp = Volt&&angle1&&bFEQ&&(!pR->tSWST.uSWST.bFlag.bDIHW);

			if (bLock)
			{
				ret =1;
			}
			else if ((!bLock) && sp)
			{
				ret =1;
			}
			else
			{
				ret = 0;
			}
			// ret = (!bLock) && sp;
			break;		
		default:
			ret = 0 ;
			break;
	}
	return ret;
}
#endif
#endif
int xdl_get_chan_cnt(void)
{

	return XDL_RCD_A_NUM;
}


char * xdl_get_chan_name(int chan_no)
{

	if(chan_no >= XDL_RCD_A_NUM)
	{
		return NULL;
	}

	return g_xdl_rcd_a[chan_no];
}


// 小电流录波点值获取函数
s16 xdl_rcd_a(int chn,int idx)
{
	int i;
	//s16 v;
	
	switch(chn)
	{
	case 0:
		return g_u0_raw[idx];
	case 1:
		return g_u0[idx];
	case 2:
		for(i=0;i<XDL_SEG_NUM;i++)
		{
			if(g_u0_seg[i].dir)
			{
	#if 1
				if(idx>=g_u0_seg[i].bgn && idx <= g_u0_seg[i].end)
				{
					return g_u0[idx];
				}
	#else
				if(idx == (g_u0_seg[i].end + g_u0_seg[i].bgn)/2)
				{
					v = g_u0_seg[i].acc/2;
					v = v > 32767 ? 32767 : v;
					
					return v*g_u0_seg[i].dir;
				}
				else if(idx>=g_u0_seg[i].bgn && idx <= g_u0_seg[i].end)
				{
					v = g_u0_seg[i].acc/4;
					//v = g_u0_seg[i].acc/(g_u0_seg[i].end - g_u0_seg[i].bgn +1);
					v = v > 32767 ? 32767 : v;
					
					return v*g_u0_seg[i].dir;
				}
	#endif
			}
		}
		return 0;
	case 3:
		for(i=0;i<XDL_SEG_NUM;i++)
		{
			if(g_u0_seg[i].dir && idx == g_u0_seg[i].max_p)
			{
				return g_u0_seg[i].max_v*g_u0_seg[i].dir;
			}
		}
		return 0;
	case 4:
		return g_i0_raw[idx];
	case 5:
		return g_i0[idx];
	case 6:
		for(i=0;i<XDL_SEG_NUM;i++)
		{
			if(g_i0_seg[i].dir)
			{
	#if 1
				if(idx>=g_i0_seg[i].bgn && idx <= g_i0_seg[i].end)
				{
					return g_i0[idx];
				}
	#else
				if(idx == (g_i0_seg[i].end + g_i0_seg[i].bgn)/2)
				{
					v = g_i0_seg[i].acc/2;
					v = v > 32767 ? 32767 : v;
					
					return v*g_i0_seg[i].dir;
				}
				else if(idx>=g_i0_seg[i].bgn && idx <= g_i0_seg[i].end)
				{
					v = g_i0_seg[i].acc/4;
//					v = g_i0_seg[i].acc/(g_i0_seg[i].end - g_i0_seg[i].bgn +1);
					v = v > 32767 ? 32767 : v;
					
					return v*g_i0_seg[i].dir;
				}
	#endif
			}
		}
		return 0;
	case 7:
		for(i=0;i<XDL_SEG_NUM;i++)
		{
			if(g_i0_seg[i].dir && idx == g_i0_seg[i].max_p)
			{
				return g_i0_seg[i].max_v*g_i0_seg[i].dir;
			}
		}
		return 0;
	}

	return 0;
}


// 方向包括-1、0、+1,共3个值
// 方向相反:两个数一正一负，不包含0
// 主要用于检测电压、电流是否反向。
static inline int _xdl_dir_inv(int v0,int v1)
{
	if((v0>0 && v1<0) || (v0<0 && v1>0))
	{
		return 1;
	}

	return 0;
}

// 方向相同:两个数同时为3个方向值中的一个，包含0
// 主要用在判断区域是否完整。
static inline int _xdl_dir_equ(int v0,int v1)
{
	if((v0>0 && v1>0) || (v0<0 && v1<0) || (v0==0 && v1==0))
	{
		return 1;
	}

	return 0;
}


// 区域更新，保留最大值最大的几个区域
void _xdl_seg_update(struct segment * seg_a,struct segment * seg,struct segment * u_seg, uint *updateNum)
{
	int i,j;
	int space_num = 0;
	// s32 u_end = u_seg->end + 4;	//扩大4个点

	if(seg->dir == 0)
	{
		return;
	}
	//上下两部分：重点->上半部主要处理以“电压段范围内”索引的电流区域
	//           下半部为大于“电压段范围内”索引的电压区域，通常不会用到
	// 如果u_end < seg->end，当u_seg->bgn == seg->bgn，则判定为稳态触发，需放入上半部
	// if((u_end < seg->end) && (u_seg->bgn != seg->bgn))	
	// 	space_num = XDL_SEG_NUM>>1;
	// else
	// 	space_num = 0;
	
	for(i=space_num;i<XDL_SEG_NUM;i++)
	{
		if(seg->max_v > seg_a[i].max_v)
		{
			if( (*updateNum)++ >= XDL_SEG_NUM)	 /**< 更新满XDL_SEG_NUM后，不再更新 */
			{
				return;
			}

			for(j=XDL_SEG_NUM-1;j>i;j--)
			{
				seg_a[j] = seg_a[j-1];
			}
			
			seg_a[i] = *seg;	

			return;
		}
	}

	return;
}


// 区域排序，根据时间顺序排序
void _xdl_seg_sort(struct segment * seg_a)
{
	u32 i,j,index;
	struct segment s;

	for(i=0;i<XDL_SEG_NUM-1;i++)
	{
		index = i;
		for(j=i+1;j<XDL_SEG_NUM;j++)
		{
			// 区域存在，且在前面就调整
			if(seg_a[j].dir && seg_a[j].bgn < seg_a[index].bgn)
			{
				index = j;
			}
		}

		if(index != i)
		{
			s = seg_a[i];
			seg_a[i] = seg_a[index];
			seg_a[index] = s;
		}	
	}

	return;
}


// 检查区域是否完整，完整的区域指区域首尾和区域外的值的方向不同。
int _xdl_seg_is_complete(s16 *dot,struct segment * seg,s32 zero)
{
	if(_xdl_dir_equ(dot[seg->bgn -1]-zero,dot[seg->bgn]-zero))
	{
		return 0;
	}
	
	if(_xdl_dir_equ(dot[seg->end]-zero,dot[seg->end+1]-zero))
	{
		return 0;
	}

	return 1;
}


// 如果mode为1，扩大区域范围(0算在范围内)，电压使用。
int _xdl_seg_search(s16 *dot,u32 bgn,u32 end,struct segment * seg,int mode)
{
	s32 v,dir;
	u32 i;

	memset(seg,0,sizeof(*seg));

	if(mode)
	{
		seg->bgn = bgn;
	}
	for(i=bgn;i<=end;i++)
	{
		// 得到值和方向
		v = dot[i];
		if(v)
		{
			dir = v > 0 ? 1 : -1;
		}
		else
		{
			dir = 0;
		}
		v = abs(v);
		
		// 初始化区间
		if(seg->dir == 0)
		{
			if(dir)
			{
				seg->dir = dir;
				seg->acc += v;
				seg->max_v = v;
				
				if(mode == 0)
				{
					seg->bgn = i;
				}
				seg->end = i;
				seg->max_p = i;
			}
		}
		// 当前区间处理
		else
		{
			// 反向,当前区间结束
			if(mode)
			{
				if(dir == -seg->dir)
				{
					return 1;
				}
			}
			else
			{
				if(dir != seg->dir)
				{
					return 1;
				}			
			}
			
			// 同相，扩大区间
			seg->acc += v;
			seg->end = i;

			if(v > seg->max_v)
			{
				seg->max_v = v;
				seg->max_p = i;
			}
		}
	}

	seg->dir = 0;
	return 0;
}

// 微分增速段搜索,微分0在前面
int _xdl_seg_search_ds_f(s16 *dot,u32 u_set,u32 bgn,u32 end,struct segment * seg)
{
	s32 v,dir,dir_old;
	u32 i;
	s32 next_v;
	s32 zct = 0, zct_pre = 0, zct_i = 0;

	memset(seg,0,sizeof(*seg));

	dir = 0;
	dir_old = 0;
	seg->bgn = bgn;
	for(i=bgn;i<=end;i++)
	{	
		
		// 得到值和方向
		dir_old= dir;
		v = dot[i]-dot[i-1];
		if(v)
		{
			dir = v > 0 ? 1 : -1;
		}
		else
		{
			dir = 0;
		}
		v = abs(v);
		
		// 初始化区间
		if(seg->dir == 0)
		{
			if(dir)
			{
				seg->acc = v;
				seg->dir = dir;
				seg->end = i;
				zct_i = i;			//by ygl 电压需要逼近突变点，第一个微分不为0的点

			}
			zct_pre = (dot[i] > 0 ? 1 : -1);
		}
		// 当前区间处理
		else
		{					
			/* 寻找过零点，为了开始平缓转突变的数据*/
			zct = (dot[i] > 0 ? 1 : -1);
			if(zct != zct_pre){
				zct_i = i;
				zct_pre = zct;
			}

			if((dir == -seg->dir)|| ((dir == 0) && (dir_old==0)))
			{
				if (i < end)
				{
					next_v = (dot[i+1]-dot[i]) > 0? 1:-1;	//ygl 再往下推算一个点的方向
					if(dir == -next_v)						//如果和当前方向不一致，则判为瞬变
					{
						dir = next_v;	//保存方向 注意此时：next_v == seg->dir	
						seg->acc += v;
						seg->end = i;
						continue;		//继续搜索下一个点
					}
				}

				// 退一步，退回本区域内
				i--;
				seg->max_p = i;
				seg->max_v = abs(dot[i]);

				// 最大值计算忽略末尾微分为0的点	
				while(i>seg->bgn)
				{
					if(dot[i] != dot[i-1])
					{
						break;
					}
					i--;
				}
				break;
			}						
			// 同相，扩大区间
			seg->acc += v;
			seg->end = i;
		}
	}

	if(seg->dir){
		
		if( abs(dot[seg->max_p] - dot[seg->bgn-1]) < u_set)
		{
			seg->dir = 0;
		}
		
		/* 开始点大于定值，则电压差值必须是开始点的4倍以上。否则清零 */
		v = abs(dot[seg->bgn-1]);
		if(v > u_set && (v*4 > abs(dot[seg->max_p] - dot[seg->bgn-1])))
		{
				seg->dir = 0;
		}	

		/*如果方向有效，则调整电流开始点为“过零点”或“第一个微分不为0的点”开始*/
		if(zct_i != 0 && seg->dir != 0){
			seg->bgn = zct_i-1;		
			// if(zct == zct_pre)	//如果没产生过零点
			// 	seg->bgn -= 1;
		}
		return 1;
	}
	return 0;
}

// 微分增速段搜索,微分0在后面
int _xdl_seg_search_ds_r(s16 *dot,u32 i_set,u32 bgn,u32 end,struct segment * seg,struct segment * u_seg)
{
	s32 v,dir,dir_old,b_add;
	u32 i;
	s32 next_v = 0;
	s32 U_mean = 0;		//对应零压均值

	memset(seg,0,sizeof(*seg));
	
	u_seg->acc = 0;
	dir = 0;
	dir_old = 0;
	b_add = 0;
	for(i=bgn;i<=end;i++)
	{
		if(u_seg->acc)		// 有首次后，累加
			u_seg->acc += g_u0[i-1];
		// 得到值和方向
		dir_old= dir;
		v = dot[i]-dot[i-1];
		if(v)
		{
			dir = v > 0 ? 1 : -1;
		}
		else
		{
			dir = 0;
		}
		v = abs(v);
		
		// 初始化区间
		if(seg->dir == 0)
		{
			if(dir)
			{
				seg->acc += v;
				u_seg->acc += g_u0[i-1];	//by ygl 记录首零压值
				seg->dir = dir;
				seg->bgn = i;
				seg->end = i;
			}
		}
		// 当前区间处理
		else
		{
			// 反向,当前区间结束
		//	if((dir == -seg->dir)|| (dir && (dir_old==0)))
			if((dir == -seg->dir && seg->dir)|| ((dir == 0) && (dir_old==0)))
			{
				if (i < end)
				{
					next_v = (dot[i+1]-dot[i]) > 0? 1:-1;	//ygl 再往下推算一个点的方向
					//1、如果启动方向和当前方向一致，则判为瞬变 例：5、3、3、2、3、3、4、、、、
					//2、连续微分为0的点，则判阶梯波形 		   例：1、2、3、3、3、3、4、、、、
					if(seg->dir == next_v || ((next_v == 0) && (dir == 0)))		
					{
						dir = next_v;	//保存方向 注意此时：next_v == seg->dir	
						seg->acc += v;
						seg->end = i;
						continue;		//继续搜索下一个点
					}
				}

				U_mean = (abs(u_seg->acc)-abs(g_u0[i-1]))/(i-seg->bgn);	//ygl 根据当前零流段，计算对应区间零压均值
				if(U_mean)	
					u_seg->max_v = abs(g_u0[seg->end] - g_u0[seg->bgn])*10/U_mean; //零压区间变化率 

				// 退一步，退回本区域内
				i--;
				seg->max_p = i;
				seg->end = i;
				seg->max_v = abs(dot[i] - dot[seg->bgn-1]);
				// seg->max_v = abs(dot[i]);	- ygl 

				// 最大值计算忽略末尾微分为0的点
				while(i>seg->bgn)
				{
					if(dot[i] != dot[i-1])
					{
						break;
					}
					i--;
				}

				break;
				//return 1;
			}			
			
			// 同相，扩大区间
			seg->acc += v;
			seg->end = i;
		}
	}

#if 0
	// 如果区域不是以微分0结尾，延长到下一个微分最大值的位置
	// 否则有可能判不到电压方向
	if(dir_old)
	{
		for(i++;i<=end;i++)
		{
			if(_xdl_dir_equ(dir,dot[i] - dot[i-1]) == 0)
			{
				seg->end = i;
				break;
			}
			
		}
	}
#endif
	
	if(seg->dir)
	{
#if 1
	// 零流首半波条件：
	// 1、开始点小于定值或电流差值是开始点的4倍以上。其中第二点是考虑在定值尽量小的情况下，
	//	  如果有大的零序电流出现，其开始点可能大于定值，但其结束点拥有更大的值，有利于提高
	//	  判断的准确性。
	// 2、开始点结束点之差大于定值。使用此条件，导致启动后可能找不到首半波，
	//	  但如果不使用此条件，仅使用最大值大于定值条件，则可能导致极小电流差值启动判断，
	//	  最终导致判断失误。
	v = abs(dot[seg->bgn-1]);
	if(v == 0) 
	{
		v = 1;
	}
	// if(v > i_set && (v*4 > abs(dot[seg->max_p] - dot[seg->bgn-1])))
	// {
	// 	seg->dir = 0;
	// }

	//如果已经电流区域结束点超载了“电压区域结束点”，则要增加差值
	if(u_seg->end < seg->end){
		i_set *= 2;
		// rt_printf("丢弃1：bgn=%d,end=%d,dot=%d, i_set=%d.\r\n",seg->bgn,seg->end,dot[seg->bgn-1],i_set);
	}
	//零压变化率小于30%”，定值尽量小,则要增加差值
	if(u_seg->max_v < 3){
		i_set *= 6;
		// rt_printf("丢弃2：bgn=%d,end=%d,dot=%d, i_set=%d.\r\n",seg->bgn,seg->end,dot[seg->bgn-1],i_set);
	}	
		
	if( abs(dot[seg->max_p] - dot[seg->bgn-1]) < i_set)
	{
		seg->dir = 0;
		// rt_printf("丢弃3：bgn=%d,end=%d,dot=%d, i_set=%d.\r\n",seg->bgn,seg->end,dot[seg->bgn-1],i_set);
	}
#else

		if(i_set == 0)
		{
			if((abs(dot[seg->bgn-1])*4 > abs(dot[seg->max_p] - dot[seg->bgn-1])))
			{
				seg->dir = 0;
			}
		}
		else
		{
			 if(abs(dot[seg->bgn-1]) > i_set)
			{
	//			rt_printf("丢弃：bgn=%d,end=%d,dot=%d.\r\n",seg->bgn,seg->end,dot[seg->bgn-1]);
				seg->dir = 0;
			}
		}
#endif

		return 1;
	}
	
	return 0;
}


// 微分增速段搜索
int _xdl_seg_search_ds(s16 *dot,u32 bgn,u32 end,struct segment * seg)
{
	s32 v,dir,dir_old;
	u32 i;

	memset(seg,0,sizeof(*seg));

	dir = 0;
	dir_old = 0;
	for(i=bgn;i<=end;i++)
	{
	
		// 得到值和方向
		dir_old= dir;
		v = dot[i]-dot[i-1];
		if(v)
		{
			dir = v > 0 ? 1 : -1;
		}
		else
		{
			dir = 0;
		}
		v = abs(v);
		
		// 初始化区间
		if(seg->dir == 0)
		{
			if(dir)
			{
				seg->acc += v;
				seg->dir = dir;
				seg->bgn = i;
				seg->end = i;
			}
		}
		// 当前区间处理
		else
		{
			// 反向,当前区间结束
		//	if(dir != seg->dir)
			if((dir == -seg->dir)|| (dir && (dir_old==0)))
			{
				i--;
				if(_xdl_dir_inv(dot[i],dot[bgn-1]))
				{
//					seg->max_v = abs(dot[i]);
					seg->max_v = abs(dot[i])*10/(i-seg->bgn+1);
				}
				else
				{
					seg->max_v = abs(dot[i] - dot[seg->bgn-1])*10/(i-seg->bgn+1);
//					seg->max_v = abs(dot[i] - dot[seg->bgn-1]);
				}
				seg->max_p = i;

//				rt_printf("v=%d,p=%d.\r\n",seg->max_v,seg->max_p);
				return 1;
			}			
			
			// 同相，扩大区间
			seg->acc += v;
			seg->end = i;
		}
	}

	seg->dir = 0;
	return 0;
}


// 处理波形的微分和零轴移动
int  _xdl_proc_diff_zero(s16 *ui0,s16 *ui0_d,int wave_len,u32 set,struct point *max_d)
{
	int i,v0,v;
	u32 min_d; //  最小微分值
	struct point max_ui;

	// 求导,并得到最大差分值
	min_d = -1;
	max_d->p = 0;
	max_d->v = 0;
	max_ui.p = 0;
	max_ui.v = abs(ui0[0]);
	for(i=1; i<wave_len;i++)
	{
		// 微分求导
		v0 = ui0[i] -ui0[i-1];
		v = abs(v0);

		// 取得最小微分值
		if(v != 0 && v < min_d)
		{
			min_d = v;
		}

		// 计算微分最大值
		if(v > abs(max_d->v))
		{
			max_d->v = v0;
			max_d->p = i;
		}

		// 计算原值最大值
		if(abs(ui0[i]) > abs(max_ui.v))
		{
			max_ui.v = ui0[i];
			max_ui.p = i;
		}
	}
	max_d->v = max_ui.v;
	v = set == 0 ? (5*min_d) : set;
	if(abs(max_ui.v) < v)
	{
		return -2;

	}

	return 0;

}

// 三点线性平滑
void xdl_f_smooth3 ( short in[], float out[], int N )
{
    int i;
    if ( N < 3 )
    {
        for ( i = 0; i <= N - 1; i++ )
        {
            out[i] = in[i];
        }
    }
    else
    {
        out[0] = ( 5 * in[0] + 2 * in[1] - in[2]) / 6;

        for ( i = 1; i <= N - 2; i++ )
        {
            out[i] = ( in[i - 1] + in[i] + in[i + 1]) / 3;
        }

        out[N - 1] = ( 5 * in[N - 1] + 2 * in[N - 2] - in[N - 3]) / 6;
    }
}

// 三点线性平滑
void xdl_smooth3 ( short in[], short out[], int N )
{
    int i;
    if ( N < 3 )
    {
        for ( i = 0; i <= N - 1; i++ )
        {
            out[i] = in[i];
        }
    }
    else
    {
        out[0] = ( 5 * in[0] + 2 * in[1] - in[2]) / 6;

        for ( i = 1; i <= N - 2; i++ )
        {
            out[i] = ( in[i - 1] + in[i] + in[i + 1]) / 3;
        }

        out[N - 1] = ( 5 * in[N - 1] + 2 * in[N - 2] - in[N - 3]) / 6;
    }
}


//五点线性平滑
void xdl_smooth5 ( short in[], short out[], int N )
{
    int i;
    if ( N < 5 )
    {
        for ( i = 0; i <= N - 1; i++ )
        {
            out[i] = in[i];
        }
    }
    else
    {
        out[0] = ( 3 * in[0] + 2 * in[1] + in[2] - in[4] +3) / 5;
        out[1] = ( 4 * in[0] + 3 * in[1] + 2 * in[2] + in[3] +5) / 10;
        for ( i = 2; i <= N - 3; i++ )
        {
            out[i] = ( in[i - 2] + in[i - 1] + in[i] + in[i + 1] + in[i + 2] +3) / 5;
        }
        out[N - 2] = ( 4 * in[N - 1] + 3 * in[N - 2] + 2 * in[N - 3] + in[N - 4] +5) / 10;
        out[N - 1] = ( 3 * in[N - 1] + 2 * in[N - 2] + in[N - 3] - in[N - 5] +3) / 5;
    }
}


// 零序电压启动后，调用此函数查找故障线路
// 采用暂态零序电流方向法:
// 1、零序电压求导。
// 2、暂态零序电流与零序电压导数比较极性。
// 3、极性相反的选定为故障线路。
// 4、此判据只在首半波内有效。
// 
int xdl_proc_u0(int len,u32 u_set)
{
	int bgn,ret;
	struct segment seg;
	struct point max_d;
	
//	memset(g_u0_raw,0,sizeof(g_u0_raw));
	memset(g_u0_seg,0,sizeof(g_u0_seg));
	
	// 零序电压求导
	ret = _xdl_proc_diff_zero(g_u0_raw,0,len,0,&max_d);
	if(ret < 0)
	{
		return ret;
	}
	
	// 搜索几个电压变化最大的区间
	bgn = 1;
//	while(_xdl_seg_search(g_u0_raw,bgn,len-1,&seg,1))
	while(_xdl_seg_search_ds_f(g_u0,u_set,bgn,len-1,&seg))
	{
		_xdl_seg_update(g_u0_seg,&seg,NULL, NULL);

#if 1
		bgn = seg.end + 1;
#else
		// 得到下一个区域的开始点，和上一个区域的零结束点重合
		for(bgn = seg.end;bgn>0;bgn--)
		{
			if(g_u0_raw[bgn] != 0)
			{
				break;
			}
		}
		bgn++;
#endif		
	}

	// 将搜索出的区域按时间排序
	_xdl_seg_sort(g_u0_seg);

	return 0;
}

#define seg_extend_dot 8
int xdl_seg_u0_ok(int len,struct segment * seg, u32 u_set)
{
	u32 bgn,end,i;
	s16 v_bgn;
	s16 max_v = 0;
	s16 limit_end = seg->end + seg_extend_dot;	//扩展首半波至少范围，

	// 扩展首半波范围,电压延后16个点，45度。
	bgn = seg->bgn > 0 ? (seg->bgn -1) : seg->bgn;
	end = seg->end + (seg->end - seg->bgn);	//seg_extend_dot -> （seg->end - seg->bgn）
	end = end > limit_end ? end : limit_end;
	if(end >= len)
	{
		end = len;
	}
	
	// 检查电压幅值，在此区域后，必须有电压大于设定值
	// v_bgn = g_u0[bgn];
	max_v = v_bgn = g_u0[bgn];
	// rt_printf("u0 check fail  bgn=%d,end =%d,max_v = %d,v_bgn = %d,set = %d\r\n",bgn,end,max_v,v_bgn,u_set);
	for(i=bgn;i<end;i++)
	{
		if(max_v < g_u0[i])
			max_v = g_u0[i];
		if(abs(g_u0[i] - v_bgn) > u_set)
		{
			// rt_printf("u0 check ok  bgn=%d,end =%d,max_v = %d,v_bgn = %d,v_p = %d,set = %d\r\n",bgn,end,max_v,v_bgn,i,u_set);
			return 1;
		}
	}
	
	return 0;
}

int xdl_seg_i0_ok(int len,struct segment * seg, u32 i_set)
{
	u32 bgn,end,i;
	s16 v_bgn;

	// 扩展首半波范围,电流延后16个点，45度。
	bgn = seg->bgn > 0 ? (seg->bgn -1) : seg->bgn;
	end = seg->end + 8;
	if(end >= len)
	{
		end = len;
	}
	
	// 检查电压幅值，在此区域后，必须有电压大于设定值
	v_bgn = g_i0[bgn];
	for(i=bgn;i<end;i++)
	{
		if(abs(g_i0[i] - v_bgn) > i_set)
		{
			return 1;
		}
	}

	return 0;
}

#define U_SET_COEF	1	
int xdl_proc_i0(uint32_t sw,int len,u32 i_set,u32 u_set,int *ps,int h_sign)
{
	int i,s,bgn=1,end,ret,dir=0;
	int j =0, hilbert_count =0;
	u32 updateNum =0, zero_set;
	s32 zero_max;
	struct point di_max;
	struct segment seg,u_seg;

	// 全局电流数据清零
//	memset(g_i0_raw,0,sizeof(g_i0_raw));
	memset(g_i0_seg,0,sizeof(g_i0_seg));
	
	// 零序电流求导,并得到最大差分值
	if(g_i0_arg[sw].scale_is_1A)
	{
		// 零流采样电阻51Ω更换为了300Ω，经过系数转换为码值后，值变大了6倍左右，直接用i_set判断
		zero_set = i_set;
	}
	else
	{
		//没换电阻还是按照原本方式处理
		if (h_sign > 0)		//H变换计算成功后，需要判断全段有流值大于4倍定值，防止无流误判
		{
			zero_set = i_set*4;
		}
		else
		{
			zero_set = i_set*2;
		}
		if(zero_set < 18)	 /**< zero_set判值不能小于18，保证小电流阀值不能过低 */
			zero_set = 18;
	}
	zero_max = _xdl_proc_diff_zero(g_i0,0,len,zero_set,&di_max);
	if(zero_max < 0)
	{
		rt_printf("第%d行, %s无流(zero_max=%d) zero_set=%d.\r\n",__LINE__, g_line_type[0],zero_max,zero_set);
		return -1;
	}

	// 零压触发时，搜索第一个电压突变的区间，精确捕捉过零点
	while(_xdl_seg_search_ds_f(g_u0,u_set,bgn,len-1,&u_seg))
	{
		if(u_seg.dir)
			break;
		bgn = u_seg.end + 1;
	}
	if(pRunSet->bTT_TBLTYPE)
	{
		u_seg.bgn -= 1 ;	//电压突变沿作为电流首半波始启点,需再回退一个采样点。
		bgn = u_seg.bgn;
	}
	else
		bgn = u_seg.bgn;

	// rt_printf("v0=%d,p=%d.u_dir=%d,u_bgn=%d,u_end=%d.\r\n",u_seg.max_v,u_seg.max_p,u_seg.dir,bgn,u_seg.end);

	// 方法2: 电流微分首半波判断
	// 搜索几个电流变化最大的区间
	while(_xdl_seg_search_ds_r(g_i0,i_set,bgn,len-1,&seg,&u_seg))
	{
		bgn = seg.end+1;

		// 区间有效，且区间电压有效才更新
		// 零压定值过大会导致暂态零压设定
		// 值过高进而导致索引零压失败最终
		// 误判或者无法启动小电流接地判断
		if(seg.dir && xdl_seg_u0_ok(len,&seg,u_set))
		{
			// rt_printf("v1=%d,p=%d.dir=%d,bgn=%d,end=%d,ubgn=%d,uend=%d,updateNum=%d.\r\n",seg.max_v,seg.max_p,seg.dir,seg.bgn,seg.end,u_seg.bgn,u_seg.end,updateNum);
			_xdl_seg_update(g_i0_seg,&seg,&u_seg,&updateNum);
		}
	}

	// 将搜索出的区域按时间排序
	_xdl_seg_sort(g_i0_seg) ;
	// 寻找电流首半波,条件是最大值最大的区域，是其前面区域最大值的8倍以上
	s = 0;
	if(pRunSet->bTT_GZJD != 1)	//选高阻接地方式，不考虑最大区域选择
	{
		for(i=1; i<(XDL_SEG_NUM>>1);i++)	//XDL_SEG_NUM ->暂时只使用前5组区域
		{
			if(g_i0_seg[i].max_v > (g_i0_seg[s].max_v <<3))
			{
				s = i;
			}
		}
	}
	

	// 如果首半波条件成立，寻找零序电压方向。
	if(g_i0_seg[s].dir)
	{
		// 扩展首半波范围
		if(g_i0_seg[s].bgn > 0)
		{
			g_i0_seg[s].bgn--;
		}

		// 检查电压幅值，在此区域后，必须有电压大于设定值
		{
			s16 v,v_bgn,v_max;

			// 延长电压检查区间 ,延长为180度+额外增加8个点
			end = g_i0_seg[s].bgn + ADC_REC_SAMPLE/2+8;

			//极端情况下会进这里，例如零流：为长而平衡的小波，造成区段划分过长，长度超越了半个周波（180度）
			if(g_i0_seg[s].end > end)	
			{
				end = g_i0_seg[s].end + 16;
			}

			if(end > len)
			{
				end = len;
			}
			
			v_max = 0;
			v_bgn = g_u0[g_i0_seg[s].bgn];
			for(i=g_i0_seg[s].bgn;i<=end;i++)
			{
				v = g_u0[i] - v_bgn;
				if(abs(v) > abs(v_max))
				{
					v_max = v;
				}

				if (i == g_i0_seg[s].end)		//区间内搜索，采用区域中的最大值计算
				{
					// 区域电压差值必须大于定值
					if(abs(v_max) > u_set*U_SET_COEF)
					{
						// dir = v;
						// 如果电流区间起点的电压的绝对值减终点的电压的绝对值大于定值，反向
						// if(abs(g_u0[g_i0_seg[s].bgn]) < abs(g_u0[i])) // 此算法跨零点时容易失误
						if(abs(g_u0[g_i0_seg[s].bgn]) < (abs(g_u0[g_i0_seg[s].end])+u_set))
						{
							dir = g_u0[g_i0_seg[s].end];	//使用区域结束点，判趋势
							rt_printf("电压正向v_max:bgn=%d,end=%d,u_set=%d,v_bgn=%d,v_end=%d,dir=%d.\r\n",
								g_i0_seg[s].bgn,g_i0_seg[s].end,u_set,g_u0[g_i0_seg[s].bgn],g_u0[g_i0_seg[s].end],dir);
						}
						else
						{
							dir = -g_u0[g_i0_seg[s].end];
							rt_printf("电压反向v_max:bgn=%d,end=%d,u_set=%d,v_bgn=%d,v_end=%d,dir =%d.\r\n",
								g_i0_seg[s].bgn,g_i0_seg[s].end,u_set,g_u0[g_i0_seg[s].bgn],g_u0[g_i0_seg[s].end],dir);
						}
						
						break;
					}
				}
				else if(i > g_i0_seg[s].end)		//延长区间搜索
				{
					// 区域电压差值必须大于定值
					if(abs(v) > u_set*U_SET_COEF)
					{
						dir = v;

						// 如果电流区间起点的电压的绝对值减终点的电压的绝对值大于定值，反向
						// if(abs(g_u0[g_i0_seg[s].bgn]) < abs(g_u0[i])) // 此算法跨零点时容易失误
						if(abs(g_u0[g_i0_seg[s].bgn]) < (abs(g_u0[g_i0_seg[s].end])+u_set))
						{
							dir = v;
						}
						else
						{
							dir = -v;
							rt_printf("电压反向v:bgn=%d,end=%d,u_set=%d,v_bgn=%d,v_end=%d,dir =%d.\r\n",
								g_i0_seg[s].bgn,g_i0_seg[s].end,u_set,g_u0[g_i0_seg[s].bgn],g_u0[g_i0_seg[s].end],dir);
						}
						
						break;
					}
				}
			}

			if(i == end)
			{
				rt_printf("电压欠幅:seg_bgn=%d,seg_end=%d,end=%d,u_set=%d,v_max=%d.\r\n",g_i0_seg[s].bgn,g_i0_seg[s].end,end,u_set,v_max);
				*ps = s;
				ret = -2;
				goto RET;
			}
			else if(i > g_i0_seg[s].end)
			{
				rt_printf("电压查找超范围:bgn=%d,end=%d,uv0=%d,uvd=%d,uv=%d,up=%d,u_set=%d.\r\n",
					g_i0_seg[s].bgn,g_i0_seg[s].end,g_u0[g_i0_seg[s].bgn],g_u0[g_i0_seg[s].end],g_u0[i],i,u_set);
			}
		}

		// 判电压方向
		if(dir)
		{
			ret=_xdl_dir_inv(dir,g_i0_seg[s].dir);
			if(ret == 0 && pRunSet->bTT_HILEBERT)	//H 变换
			{
				for(j = (g_i0_seg[s].bgn&0x7f); j < XDL_SAMPLE_LEN ; j+=ADC_REC_SAMPLE )
				{
					hilbert_count++;
					ret = (hilbert_filter(&g_u0_dwt[j],  &g_i0_dwt[j], ADC_REC_SAMPLE)>0)? 1:0;
					if(ret)
					{
						rt_printf("hilbert activation,begin at %d,计算：%d 次 \r\n",(g_i0_seg[s].bgn&0x7f),hilbert_count);
						break;
					}
				}
				if(!ret)
				{
					rt_printf("hilbert not find,begin at %d,计算：%d 次 \r\n",(g_i0_seg[s].bgn&0x7f),hilbert_count);
				}	
			}
			rt_printf("%s(s=%d,dir=%d,bgn=%d,iv=%d,ip=%d,uv=%d,up=%d,i_set=%d,u_set=%d).\r\n",
				g_line_type[ret],s,dir,g_i0_seg[s].bgn,g_i0[g_i0_seg[s].max_p],g_i0_seg[s].max_p,g_u0[i],i,i_set,u_set);
			*ps = s;
			return ret;
		}
		else
		{
			rt_printf("电压无方向：s=%d,bgn=%d,end=%d,i=%d,u0=%d.\r\n",
				s,g_i0_seg[s].bgn,g_i0_seg[s].end,i,g_u0[i]);
			*ps = s;
			ret = -3;
			goto RET;
		}
	}
	else
	{
		if(h_sign > 0 && pRunSet->bTT_HILEBERT)		//H变换，缩小区间再判一次
		{
			for(j = (ADC_REC_SAMPLE<<0) ; j < XDL_SAMPLE_LEN ; j+=ADC_REC_SAMPLE )
			{
				hilbert_count++;
				ret = (hilbert_filter(&g_u0_dwt[j],  &g_i0_dwt[j], ADC_REC_SAMPLE)>0)? 1:0;
				if(ret)
				{
					rt_printf("%s hilbert activation,计算：%d 次 \r\n",g_line_type[ret],hilbert_count);
					return ret;
				}
			}
			if(!ret)
			{
				rt_printf("%s hilbert not find,计算：%d 次 \r\n",g_line_type[ret],hilbert_count);
			}	
		}

		rt_printf("首半波未找到:s=%d,dir=%d,max=%d.\r\n",s,g_i0_seg[s].dir,g_i0_seg[s].max_v);
		*ps = s;
		ret = -4;
	}

RET:
	return ret;
}

#define COEF	1.0
int xdl_proc_adc(u32 sw,u32 dt,struct timespec *ts)
{
	int i,ret,seg;
	int ch0,cha,chb,chc;
	int i0,u0;
	char Hilbert_Sign = 0;	//H变换计算成功标志
	float H_ret = 0;		//H变换计算返回值

	// 三项电流
	int ui_begin;
	DWORD Ia,Ib,Ic;

	wave_object obj=NULL;
	wt_object wt=NULL;
	char *method = "modwt";
	if(pRunSet->bTT_FILTER == 2)
	{
		// char *name = "haar";
		obj = wave_init(pRunSet->bTT_WAVE);// Initialize the wavelet
		wt = wt_init(obj, method, XDL_SAMPLE_LEN, pRunSet->bTT_WAVE_ORDER);// Initialize the wavelet transform object
	}

	// 得到零序电压采样值
	if(pRunSet->tSwSet[sw].bTT_Power_v2 == 0)
	{
		ch0 = (short)g_sw_pub.ac_cfg_index[PUB_AC_U01];
		cha = (short)g_sw_pub.ac_cfg_index[PUB_AC_UA1];
		chb = (short)g_sw_pub.ac_cfg_index[PUB_AC_UB1];
		chc = (short)g_sw_pub.ac_cfg_index[PUB_AC_UC1];
	}
	else
	{
		ch0 = (short)g_sw_pub.ac_cfg_index[PUB_AC_U02];
		cha = (short)g_sw_pub.ac_cfg_index[PUB_AC_UA2];
		chb = (short)g_sw_pub.ac_cfg_index[PUB_AC_UB2];
		chc = (short)g_sw_pub.ac_cfg_index[PUB_AC_UC2];
	}
	
	if(ch0 != INDEX_INVALLID)
	{
		ch0=equ_get_ac_channel(g_equ_config_ac[ch0].slot, g_equ_config_ac[ch0].index);
		for(i=0; i<XDL_SAMPLE_LEN;i++)
		{
			g_u0_raw[i] = g_adc_dots_rec[((dt+i)&ADC_REC_DOTS_MASK)][ch0];
			g_u0_dwt[i] = (float)g_u0_raw[i];
		}
		if(pRunSet->bTT_GZJD != 1)	//选高阻接地方式，则小波计算不考虑：三点滤波算法
		{
			memset(g_u0_dwt,0,sizeof(g_u0_dwt));
			xdl_f_smooth3(g_u0_raw, g_u0_dwt, XDL_SAMPLE_LEN);
		}

		switch (pRunSet->bTT_FILTER)
		{
		case 0:
			xdl_smooth3(g_u0_raw, g_u0, XDL_SAMPLE_LEN);
			break;
		case 1:
			DWT_Matrix_Transform(g_u0_dwt, g_u0, XDL_SAMPLE_LEN);
			break;
		case 2:
			modwt(wt, g_u0_dwt);// Perform MODWT
			for(i=0; i<XDL_SAMPLE_LEN;i++)
			{
				g_u0[i] = (s16)(wt->output[i]*COEF);
			}
			break;
		default:
			memcpy(g_u0, g_u0_raw, XDL_SAMPLE_LEN);
			break;
		}
	}
	else if(	(cha != INDEX_INVALLID)
			&&	(chb != INDEX_INVALLID)
			&&	(chc != INDEX_INVALLID))
	{
		cha=equ_get_ac_channel(g_equ_config_ac[cha].slot, g_equ_config_ac[cha].index);
		chb=equ_get_ac_channel(g_equ_config_ac[chb].slot, g_equ_config_ac[chb].index);
		chc=equ_get_ac_channel(g_equ_config_ac[chc].slot, g_equ_config_ac[chc].index);
		for(i=0; i<XDL_SAMPLE_LEN;i++)
		{
			g_u0[i]  = g_adc_dots_rec[((dt+i)&ADC_REC_DOTS_MASK)][cha];
			g_u0[i] += g_adc_dots_rec[((dt+i)&ADC_REC_DOTS_MASK)][chb];
			g_u0[i] += g_adc_dots_rec[((dt+i)&ADC_REC_DOTS_MASK)][chc];
		}
		
	}
	else
	{
		ret = -11;
		goto RET;
	}

	if(sw < g_sw_num)
	{
		TSETSW *pSet = &pRunSet->tSwSet[sw];
		TSDHZ_T *pSW=&g_tRelay[sw].tSDHZ;
		int zero;
		if((BH_ALL_EN(sw) && (pSet->bTT_xdlgj || pSet->bTT_xdltz)) == 0)
		{
			ret = -12;
			goto RET;
		}

		zero = 0;
		if(!pRunSet->bTT_TBLTYPE)	//使用电流突变
		{
			if(g_xdl[sw].zero_cnt)
			{
				zero = g_xdl[sw].zero_sum/g_xdl[sw].zero_cnt; 
			}
		}

		// 得到零序电流采样值
		ch0 = (short)g_sw[sw].ac_cfg_index[SW_AC_I0];
		if(ch0 != INDEX_INVALLID)
		{
			ch0=equ_get_ac_channel(g_equ_config_ac[ch0].slot, g_equ_config_ac[ch0].index);
			for(i=0; i<XDL_SAMPLE_LEN;i++)
			{
				g_i0_raw[i] = g_adc_dots_rec[((dt+i)&ADC_REC_DOTS_MASK)][ch0] - zero;
				g_i0_dwt[i] = (float)g_i0_raw[i];
			}

			if(pRunSet->bTT_GZJD != 1)	//选高阻接地方式，则小波计算不考虑：三点滤波算法
			{
				memset(g_i0_dwt,0,sizeof(g_i0_dwt));
				xdl_f_smooth3(g_i0_raw, g_i0_dwt, XDL_SAMPLE_LEN);
			}

			switch (pRunSet->bTT_FILTER)
			{
			case 0:
				xdl_smooth3(g_i0_raw, g_i0, XDL_SAMPLE_LEN);
				break;
			case 1:
				DWT_Matrix_Transform(g_i0_dwt, g_i0, XDL_SAMPLE_LEN);
				break;
			case 2:				
				modwt(wt, g_i0_dwt);// Perform MODWT
				for(i=0; i<XDL_SAMPLE_LEN;i++)
				{
					g_i0[i] = (s16)(wt->output[i]*COEF);
				}
				break;
			default:
				memcpy(g_i0, g_i0_raw, XDL_SAMPLE_LEN);
				break;
			}
		}
		else if(	(cha != INDEX_INVALLID)
				&&	(chb != INDEX_INVALLID)
				&&	(chc != INDEX_INVALLID))
		{
			cha=equ_get_ac_channel(g_equ_config_ac[cha].slot, g_equ_config_ac[cha].index);
			chb=equ_get_ac_channel(g_equ_config_ac[chb].slot, g_equ_config_ac[chb].index);
			chc=equ_get_ac_channel(g_equ_config_ac[chc].slot, g_equ_config_ac[chc].index);
			for(i=0; i<XDL_SAMPLE_LEN;i++)
			{
				g_i0[i]  = g_adc_dots_rec[((dt+i)&ADC_REC_DOTS_MASK)][cha];
				g_i0[i] += g_adc_dots_rec[((dt+i)&ADC_REC_DOTS_MASK)][chb];
				g_i0[i] += g_adc_dots_rec[((dt+i)&ADC_REC_DOTS_MASK)][chc];
			}
		}
		else
		{
			ret = -13;
			goto RET;
		}

		// rt_free(p_uo);
		// rt_free(p_io);
		wave_free(obj);
		wt_free(wt);

		// 检查处理是否及时 
 		// 启动(1.28-0.3-0.02×6）秒内必须完成小电流数据copy，否则录波缓冲中的数据将被覆盖。
 		{
 			u32 n;
	 		n = (u32)(g_adc_dots_count<<3) - dt;
			if(n > ADC_REC_DOTS_CHANNEL)
			{
				rt_printf("处理超时:n=%d,now=%d,old=%d.\r\n",n,(g_adc_dots_count<<3),dt);
				ret = -14;
				goto RET;
			}
		}
 		
		// 零序电流处理
		seg = -1;
		if(pRunSet->bTT_TBLTYPE)	//选零压突变，使用H变换计算以增加准确率
 		{
			for(i = 0 ; i < XDL_SAMPLE_LEN ; i+=ADC_REC_SAMPLE )		//(ADC_REC_SAMPLE<<1)
			{
				H_ret = hilbert_filter(&g_u0_dwt[i],  &g_i0_dwt[i], ADC_REC_SAMPLE);
				if(H_ret > 0)
				{
					Hilbert_Sign++;
					// break;
				}		
//				rt_printf("i= %d,status =%d,hilbert_filter ret = %f.\r\n",i,Hilbert_Sign,H_ret);
			}

			ret = xdl_proc_i0(sw,XDL_SAMPLE_LEN,pSet->dI_xdl_zt,pRunSet->dU_xdl_u0_zt[pRunSet->tSwSet[sw].bTT_Power_v2],&seg,Hilbert_Sign);

		}
		else	// 零序电流处理
			ret = xdl_proc_i0(sw,XDL_SAMPLE_LEN,pSet->dI_xdl_zt,pRunSet->dU_xdl_u0_zt[pRunSet->tSwSet[sw].bTT_Power_v2],&seg,0);
		//如果电流反向，结果反向。
		if((pRunSet->tSwSet[sw].bTT_Current_Inv || pRunSet->tSwSet[sw].bTT_lxpolar || g_cur_direction_adaptaion) && (ret >= 0))
		{
			ret = !ret;
		}
		

		// 计算I0、U0
		i0 = 0;
		u0 = 0;
		if(seg >= 0)
		{
			for(i=g_i0_seg[seg].bgn; i<g_i0_seg[seg].end; i++)
			{
				if(abs(g_i0[i]) > abs(i0))
				{
					i0 = g_i0[i];
				}
				if(abs(g_u0[i]) > abs(u0))
				{
					u0 = g_u0[i];
				}
			}
			i0 = i0*(g_ui[UI_SW_INDEX(sw,SW_AC_I0)].e_factor0*1.4142)*Q16_BASE;
			u0 = u0*(g_ui[PUB_AC_U01].e_factor0*1.4142)*Q16_BASE;
		}

		// 计算三项电流
		ui_begin = UI_SW_INDEX_BEGIN(sw);
		Ia = _Mul_Div_U(sqrt_32fix(g_ui[ui_begin + SW_AC_IA].m2[0]), 256, g_ui[ui_begin + SW_AC_IA].m2_factor_k);
		Ib = _Mul_Div_U(sqrt_32fix(g_ui[ui_begin + SW_AC_IB].m2[0]), 256, g_ui[ui_begin + SW_AC_IB].m2_factor_k);
		Ic = _Mul_Div_U(sqrt_32fix(g_ui[ui_begin + SW_AC_IC].m2[0]), 256, g_ui[ui_begin + SW_AC_IC].m2_factor_k);
		// 启动事件
		soe_record_ev(EV_LX_XDL_QD+sw*EV_SW_NUM, 1, i0,u0,ret);
		if(ret>0)  //小电流接地：启动[界内动作]
		{
			g_xdl[sw].dz = 1;
			// 提前打开启动继电器,以节省硬件耗用时间
			// 并等待超时释放
			sw_do(sw,SW_DO_FZ,SW_DO_TYPE_SELECT_ON);
			
			if(pSet->dT_xdl_T)  //暂态时间不为0时,才启动间歇性接地逻辑
			{
				// jxjd_set_JDGZ(sw);  //间歇接地故障(用于计次) // TODO
			}

			if (!pSet->dT_xdl_GT)					    // 告警
			{
				// 告警
				if(pSet->bTT_xdlgj)
				{
					soe_record_ev(EV_LX_XDL_GJ+sw*EV_SW_NUM, 1, i0,u0,0 );
					soe_record_ev(EV_ABC_JDTZ+sw*EV_SW_NUM, 1, i0,u0,0 );
					pSW->uSdhz_S.bFlag.bJD = true;
				}
			}
			else
			{
				pSW->uSdhz_S.bFlag.bJD = true;
				g_xdl[sw].gj_us0 = g_xdl[sw].qd_us0;
				g_xdl[sw].dz_i0[0] = i0;
				g_xdl[sw].dz_u0[0] = u0;
				g_xdl[sw].dz_Ia[0] = Ia;
				g_xdl[sw].dz_Ib[0] = Ib;
				g_xdl[sw].dz_Ic[0] = Ic;
			}
			
			if (!pSet->dT_xdl_T)						// 跳闸
			{
				// 跳闸
				if(pSet->bTT_xdltz)
				{
					sw_do(sw,SW_DO_FZ,SW_DO_TYPE_ON);
					soe_record_ev(EV_LX_XDL_TZ+sw*EV_SW_NUM, 1, i0,u0,0 );
					soe_record_ev(EV_ABC_JDTZ+sw*EV_SW_NUM, 1, i0,u0,0 );
					if ((Ia>Ib)&&(Ia>Ic))
					{
						soe_record_ev(EV_A_JDTZ+sw*EV_SW_NUM, 1, Ia,0,0 );
					}
					else if ((Ib>Ia)&&(Ib>Ic))
					{
						soe_record_ev(EV_B_JDTZ+sw*EV_SW_NUM, 1, Ib,0,0 );
					}
					else if ((Ic>Ia)&&(Ic>Ib))
					{
						soe_record_ev(EV_C_JDTZ+sw*EV_SW_NUM, 1, Ic,0,0 );
					}
					pSW->uSdhz_S.bFlag.bJD = true;
					// g_tRelay[sw].tXDLCHZ.sta.bFlag.bXDLDZ = true; //TODO EWen
					
					//jxjd_sw_reset(sw);
				}
			}
			else
			{
				pSW->uSdhz_S.bFlag.bJD = true;
				g_xdl[sw].dz_us0 = g_xdl[sw].qd_us0;
				g_xdl[sw].dz_i0[1] = i0;
				g_xdl[sw].dz_u0[1] = u0;
				g_xdl[sw].dz_Ia[1] = Ia;
				g_xdl[sw].dz_Ib[1] = Ib;
				g_xdl[sw].dz_Ic[1] = Ic;
			}
		}

		rcd_start_xdl(NULL,sw,g_xdl_rcd_a,XDL_RCD_A_NUM,XDL_SAMPLE_LEN,ts);  

		g_xdl[sw].st = XDL_ST_END;
		ret = 0;
	}
	else
	{
		ret = -15;
	}


RET:

	if(ret != 0)
	{
		soe_record_ev(EV_LX_XDL_QD+sw*EV_SW_NUM, 1, 0,0,ret);
	}

	rt_printf_time("小电流接地完成(sw=%d,ret=%d,inv=%d,dir=%d).\r\n",sw,ret,pRunSet->tSwSet[sw].bTT_Current_Inv, g_cur_direction_adaptaion);
	return ret;
}

// 零流突变判断定值
#define REV_F_CT_DEFAULT	20	// CT默认，额定5A
#define REV_F_CT_1A			10	// CT_1V/1A, 额定1A
// 小电流突变量启动，放在156us中断中
void xdl_tbl_qd(int mod)
{
	s16 v,v0,v1,zero;
	s16 iv,iv0,iv1,ref_i,ref_v=0;	//零压触发，电流参考辅助触发
	u32 sw,ui;
	u32 xdl_zt_v; 
	int ret;
	TSETSW *pSet = NULL;
//	u32 index = g_adc_dots_index_rec - 1;
	u32 index = ((g_adc_dots_count-1)<<3) + mod;
	
	for (sw=0; sw<g_sw_num; sw++)
	{
		// 已启动，退出
		if(g_xdl[sw].st)
		{			
			continue;
		}

		pSet = &pRunSet->tSwSet[sw];
		
		//突变量启动前提条件:保护总投入，小电流接地告警或出口投入或录波投入。
		if(BH_ALL_EN(sw) == 0 || (pSet->bTT_xdlgj || pSet->bTT_xdltz)== 0)
		{
			continue;
		}
		
		// 如果索引通道不存在，不需计算
		if(pRunSet->bTT_TBLTYPE)	//高阻接地方式，零序电流较小，可以采用电压突变启动
		{
			ui = PUB_AC_U01;
			xdl_zt_v = pRunSet->dU_xdl_u0_zt[0];
		}	
		else
		{
			ui = UI_SW_INDEX(sw,SW_AC_I0);
			xdl_zt_v = pRunSet->tSwSet[sw].dI_xdl_zt;
		}

		if(g_ui[ui].chn_index == CFG_ADC_CHANNEL_ZERO)
		{
			continue;
		}

		// 检查硬件通道是否正常
		if(equ_ac_channel_is_ok(g_ui[ui].chn_index) == 0)
		{
			continue;
		}

		// 得到当前值和一个周期前的值
		v0 = g_adc_dots_rec[(index - ADC_REC_SAMPLE*2) & ADC_REC_DOTS_MASK][g_ui[ui].chn_index];  
		v1 = g_adc_dots_rec[(index - ADC_REC_SAMPLE) & ADC_REC_DOTS_MASK][g_ui[ui].chn_index];  
		v = g_adc_dots_rec[(index) & ADC_REC_DOTS_MASK][g_ui[ui].chn_index];  

		//电流零偏处理，由于在DTU3.0电磁式的设计中，AD一个码值代表6mA，10个码值代表60mA，所以必须修正
		// 电流零偏计算
		g_xdl[sw].zero_sum += v1;
		if(g_xdl[sw].zero_cnt < ADC_REC_SAMPLE)
		{
			g_xdl[sw].zero_cnt++;
			return;
		}
		else
		{
			g_xdl[sw].zero_sum -= v0;
		}

		// 根据zero，修正v值，
		zero = g_xdl[sw].zero_sum/g_xdl[sw].zero_cnt; 
		v -= zero;

		// 如果大于定值，启动录波   间隔电流大于无流值
		if((abs(v) > xdl_zt_v))    
		{
			// if(sw > 0)	 /**< sw = 0 默认间隔不判无流值，其它间隔要判 */
			{
				// if(g_sw[sw].m2_max < pRunSet->dIWL_SQR[sw])
				// 	continue;
				if(pRunSet->bTT_TBLTYPE){	 /**< 改为所有间隔都要判零流 */

					ref_i = UI_SW_INDEX(sw,SW_AC_I0);
					iv0 = g_adc_dots_rec[(index - ADC_REC_SAMPLE*2) & ADC_REC_DOTS_MASK][g_ui[ref_i].chn_index];  
					iv1 = g_adc_dots_rec[(index - ADC_REC_SAMPLE) & ADC_REC_DOTS_MASK][g_ui[ref_i].chn_index];  
					iv  = g_adc_dots_rec[(index) & ADC_REC_DOTS_MASK][g_ui[ref_i].chn_index];  
					ref_v = abs(iv0 - iv1*2 + iv); 
					if(ref_v < g_i0_arg[sw].ref_v_set)
						continue;
				}
			}
			
			// 置启动标志，不能放在录波启动成功的条件里，避免启动失败后频繁打印
			g_xdl[sw].st = XDL_ST_QT;
			g_xdl[sw].qd_us0 = ustimer_get_origin();

			ret = rcd_start(sw, RECORD_TYPE_XDLJD, xdl_get_LLTB_waves(sw));
			if(ret == 0)
			{
				rt_printf("%s启动:sw=%d,v=%d,ref_v=%d,i_m2=%d,nor_i=%d,i_set=%d,zero=%d.\r\n",g_tbl_type[pRunSet->bTT_TBLTYPE],sw,v,ref_v,g_sw[sw].m2_max,pRunSet->dIWL_SQR[sw],xdl_zt_v,zero);
			}
			else
			{
				rt_printf("录波启动失败(ret=%d,sw=%d.\r\n)",ret,sw);
			}

			// if(pRunSet->bTT_TBLTYPE){	//零压触发每次只判一间隔，下次进来再判下一间隔单元
			// 	return;		
			// }
		}
	}
}

/******************************************************************************
函数名称：	max3
函数版本：	01.01
创建作者：	赵海洋
创建日期：	2022-03-22
函数说明：	三值比较函数
参数说明：	无
返回值：  	无.
修改记录：	
*/
#if 0
static int max3 (int a, int b, int c)
{
	if (a>=b)
	if (a>=c)
	return a;
	else
	return c;
	else
	if (b>=c)
	return b;
	else
	return c;
}
#endif


#define  XDL_FG_INTERVAL    (1) //(5)   //复归间隔 unit: s

//小电流跳闸、复归
static void _xdl_protect_TZ_FG(int sw, DWORD dStep)
{
	int  soeno;
	bool bQD;
	static uint8_t gj_flag[SWITCH_NUM_MAX]={0};
	static uint8_t dz_flag[SWITCH_NUM_MAX]={0};

	// 电流闭锁复归
	bQD = OverRelay(g_ui[UI_SW_INDEX(sw,SW_AC_I0)].m2[0], pRunSet->tSwSet[sw].dI_xdl,pRunSet->tSwSet[sw].dI_xdl_fh, g_tXDLTime_I[sw].boolTrip);
	RunTR(&g_tXDLTime_I[sw], bQD, dStep);

	// 小电流告警延时动作
	if (pRunSet->tSwSet[sw].dT_xdl_GT)
	{
		if((g_xdl[sw].gj_us0) && (ustimer_get_duration(g_xdl[sw].gj_us0) >= (pRunSet->tSwSet[sw].dT_xdl_GT*USTIMER_SEC)))
		{
			if (gj_flag[sw]==0)
			{
				g_xdl[sw].gj_us0 = 0;
			#ifdef RECORD_SW_U0TB
				if(g_tXDLTime_U0TB[sw].boolTrip || g_tXDLTime_I[sw].boolTrip)
			#else
				if(g_tXDLTime_U[pRunSet->tSwSet[sw].bTT_Power_v2].boolTrip || g_tXDLTime_I[sw].boolTrip)
			#endif
				{
					// 告警
					if(pRunSet->tSwSet[sw].bTT_xdlgj)
					{
						// g_xdl[sw].gj_fh_us0 = ustimer_get_origin();
						soe_record_ev(EV_LX_XDL_GJ+sw*EV_SW_NUM, 1, g_xdl[sw].dz_i0[0],g_xdl[sw].dz_u0[0],0 );
						if(!soe_check(EV_ABC_JDTZ +sw*EV_SW_NUM))
						{
							soe_record_ev(EV_ABC_JDTZ +sw*EV_SW_NUM, 1, g_xdl[sw].dz_i0[0],g_xdl[sw].dz_u0[0],0 );
						}
					}
					gj_flag[sw] = 1;
				}
			}
		}
		else
		{
			gj_flag[sw] = 0;
		}
	}

	// 小电流跳闸延时动作
	if (pRunSet->tSwSet[sw].dT_xdl_T)
	{
		if((g_xdl[sw].dz_us0) && (ustimer_get_duration(g_xdl[sw].dz_us0) >= (pRunSet->tSwSet[sw].dT_xdl_T*USTIMER_SEC)))
		{
			if (dz_flag[sw]==0)
			{
				g_xdl[sw].dz_us0 = 0;
			#ifdef RECORD_SW_U0TB
				if(g_tXDLTime_U0TB[sw].boolTrip || g_tXDLTime_I[sw].boolTrip)
			#else
				if(g_tXDLTime_U[pRunSet->tSwSet[sw].bTT_Power_v2].boolTrip || g_tXDLTime_I[sw].boolTrip)
			#endif
				{
					// 跳闸
					if(pRunSet->tSwSet[sw].bTT_xdltz)
					{
						sw_do(sw,SW_DO_FZ,SW_DO_TYPE_SELECT_ON);
						sw_do(sw,SW_DO_FZ,SW_DO_TYPE_ON);
						// g_xdl[sw].dz_fh_us0 = ustimer_get_origin();
						soe_record_ev(EV_LX_XDL_TZ+sw*EV_SW_NUM, 1, g_xdl[sw].dz_i0[1],g_xdl[sw].dz_u0[1],0 );

						if(!soe_check(EV_ABC_JDTZ +sw*EV_SW_NUM))
						{
							soe_record_ev(EV_ABC_JDTZ +sw*EV_SW_NUM, 1, g_xdl[sw].dz_i0[1],g_xdl[sw].dz_u0[1],0 );
						}
						if ((g_xdl[sw].dz_Ia[1]>g_xdl[sw].dz_Ib[1])&&(g_xdl[sw].dz_Ia[1]>g_xdl[sw].dz_Ic[1]))
						{
							soe_record_ev(EV_A_JDTZ+sw*EV_SW_NUM, 1, g_xdl[sw].dz_Ia[1],0,0 );
						}
						else if ((g_xdl[sw].dz_Ib[1]>g_xdl[sw].dz_Ia[1])&&(g_xdl[sw].dz_Ib[1]>g_xdl[sw].dz_Ic[1]))
						{
							soe_record_ev(EV_B_JDTZ+sw*EV_SW_NUM, 1, g_xdl[sw].dz_Ib[1],0,0 );
						}
						else if ((g_xdl[sw].dz_Ic[1]>g_xdl[sw].dz_Ia[1])&&(g_xdl[sw].dz_Ic[1]>g_xdl[sw].dz_Ib[1]))
						{
							soe_record_ev(EV_C_JDTZ+sw*EV_SW_NUM, 1, g_xdl[sw].dz_Ic[1],0,0 );
						}

						// g_tRelay[sw].tXDLCHZ.sta.bFlag.bXDLDZ = true; //TODO
					}
					dz_flag[sw] = 1;
				}
			}
		}
		else
		{
			dz_flag[sw] = 0;
		}
	}

	// 小电流启动重合闸
	#if 0 // TODO
	if((g_xdl[sw].st) && g_tRelay[sw].tXDLCHZ.sta.bFlag.bXDLDZ)
	{
	#ifdef RECORD_SW_U0TB
		if((!g_tXDLTime_U0TB[sw].boolTrip) && (!g_tXDLTime_I[sw].boolTrip))
	#else
		if((!g_tXDLTime_U[pRunSet->tSwSet[sw].bTT_Power_v2].boolTrip) && (!g_tXDLTime_I[sw].boolTrip))
	#endif
		{
			g_tRelay[sw].tXDLCHZ.sta.bFlag.bXDLDZ = false;
			g_tRelay[sw].tXDLCHZ.sta.bFlag.bXDLCH = true;
		}
	}
	#endif

	// 此标志置1时可以确定产生了界内故障
	if(g_xdl[sw].dz)
	{
		if(g_xdl[sw].dz_fh_us0 == 0)
		{
			g_xdl[sw].dz_fh_us0 = ustimer_get_origin();
		}
		else
		{
			#ifdef RECORD_SW_U0TB
			if(g_tXDLTime_U0TB[sw].boolTrip || g_tXDLTime_I[sw].boolTrip)
			#else
			if(g_tXDLTime_U[pRunSet->tSwSet[sw].bTT_Power_v2].boolTrip || g_tXDLTime_I[sw].boolTrip)
			#endif
			{
				// 故障还没消失，更新计时
				g_xdl[sw].dz_fh_us0 = ustimer_get_origin();	
			}
		}
	}

	// 小电流信号延迟复归
	if((g_xdl[sw].dz_fh_us0) && (ustimer_get_duration(g_xdl[sw].dz_fh_us0) >= (uint32_t)(tRunPara.fT_LBXH_RST*USTIMER_SEC)))
	{
		g_xdl[sw].dz_fh_us0 = 0;

		// 告警
		soeno = EV_LX_XDL_GJ+sw*EV_SW_NUM;
		if(soe_check(soeno))
		{
			soe_record_ev(soeno, 0, 0,0,0 );
		}

		// 跳闸
		soeno = EV_LX_XDL_TZ+sw*EV_SW_NUM;
		if(soe_check(soeno))
		{
			soe_record_ev(soeno, 0, 0,0,0 );
		}

		// 接地故障总
		soeno = EV_ABC_JDTZ+sw*EV_SW_NUM;
		if(soe_check(soeno))
		{
			soe_record_ev(soeno, 0, 0,0,0 );
		}

		// A相
		soeno = EV_A_JDTZ+sw*EV_SW_NUM;
		if(soe_check(soeno))
		{
			soe_record_ev(soeno, 0, 0,0,0 );
		}

		// B相
		soeno = EV_B_JDTZ+sw*EV_SW_NUM;
		if(soe_check(soeno))
		{
			soe_record_ev(soeno, 0, 0,0,0 );
		}

		// C相
		soeno = EV_C_JDTZ+sw*EV_SW_NUM;
		if(soe_check(soeno))
		{
			soe_record_ev(soeno, 0, 0,0,0 );
		}

		//间歇接地故障消失
		// jxjd_clr_JDGZ(sw);//TODO EWen
	}

	// 两次判断最小间隔为5S.
	if((g_xdl[sw].st) && (ustimer_get_duration(g_xdl[sw].qd_us0) > (XDL_FG_INTERVAL*USTIMER_SEC)))
	{

		// 如果接地启动，必须等小电流零压零流消失后才能复归
	#ifdef RECORD_SW_U0TB
		if(g_tXDLTime_U0TB[sw].boolTrip || g_tXDLTime_I[sw].boolTrip)
	#else
		if(g_tXDLTime_U[pRunSet->tSwSet[sw].bTT_Power_v2].boolTrip || g_tXDLTime_I[sw].boolTrip)
	#endif
		{
			return;
		}

		rt_printf_time("小电流接地复归(sw=%d,st=%d)。\r\n",sw,g_xdl[sw].st);

		g_xdl[sw].st = XDL_ST_IDLE;
		g_xdl[sw].dz = 0;

		g_xdl[sw].zero_sum = 0;
		g_xdl[sw].zero_cnt = 0;

		// g_tRelay[sw].tXDLCHZ.sta.bFlag.bXDLCH = false;

		// 启动
		soeno = EV_LX_XDL_QD+sw*EV_SW_NUM;
		if(soe_check(soeno))
		{
			soe_record_ev(soeno, 0, 0,0,0 );
		}

		//间歇接地故障消失
		// jxjd_clr_JDGZ(sw); //TODO EWen
	}

}

#ifndef RECORD_SW_U0TB  //保留原有逻辑
void xdl_protect(DWORD  dStep)
{
	int sw,soeno;
	bool bQD;

	// 小电流零序电压过压逻辑1
	bQD = OverRelay(g_ui[PUB_AC_U01].m2[0], pRunSet->dU_xdl_u0[0], pRunSet->dU_xdl_u0_fh[0], g_tXDLTime_U[0].boolTrip);
	RunTR(&g_tXDLTime_U[0], bQD, dStep);

	// 记录事件
	soeno = EV_XDL_U0GJ1;
	if(g_tXDLTime_U[0].boolTrip && pRunSet->dU_xdl_u0[0]) 
	{
		
		if(soe_check(soeno)==false)
		{
			DWORD u0;
			u0 = _Mul_Div_U(sqrt_32fix(g_ui[PUB_AC_U01].m2[0]), 256, g_ui[PUB_AC_U01].m2_factor_k);
			soe_record_ev(soeno, 1, u0,0,0);

			// 小电流零压启动录波，但不进行接地判断
			for(sw=0;sw<g_sw_num;sw++)
			
			{
				if(pRunSet->tSwSet[sw].bTT_Power_v2 == 0)
				{
					rcd_start(sw,RECORD_TYPE_LXDY,RECORD_LEN_TZQD); 
				}
			}
		}
	}
	else
	{
	
		if(soe_check(soeno)==true)
		{
			soe_record_ev( soeno, 0, 0,0,0 );  
		}
	} 

	// 小电流零序电压过压逻辑2
	bQD = OverRelay(g_ui[PUB_AC_U02].m2[0], pRunSet->dU_xdl_u0[1], pRunSet->dU_xdl_u0_fh[1], g_tXDLTime_U[1].boolTrip);
	RunTR(&g_tXDLTime_U[1], bQD, dStep);

	// 记录事件
	soeno = EV_XDL_U0GJ2;
	if(g_tXDLTime_U[1].boolTrip && pRunSet->dU_xdl_u0[1]) 
	{
		
		if(soe_check(soeno)==false)
		{
			DWORD u0;
			u0 = _Mul_Div_U(sqrt_32fix(g_ui[PUB_AC_U02].m2[0]), 256, g_ui[PUB_AC_U02].m2_factor_k);
			soe_record_ev(soeno, 1, u0,0,0);

			// 小电流零压启动录波，但不进行接地判断
			for(sw=0;sw<g_sw_num;sw++)
			
			{
				if(pRunSet->tSwSet[sw].bTT_Power_v2)
				{
					rcd_start(sw,RECORD_TYPE_LXDY,RECORD_LEN_TZQD); 
				}
			}
		}
	}
	else
	{
	
		if(soe_check(soeno)==true)
		{
			soe_record_ev( soeno, 0, 0,0,0 );  
		}
	} 

	// 小电流启动延时复归
	for(sw=0;sw<g_sw_num;sw++)
	{
		_xdl_protect_TZ_FG(sw, dStep);
	}
 }
#endif

#ifdef RECORD_SW_U0TB  //专业检测用
void xdl_protect_sw(int sw, DWORD dStep)
{
	/*	//零压突变录波单独做功能，不再用此函数
	int  soeno;
	bool bQD;
	TSETSW *pSet = &pRunSet->tSwSet[sw];

	// 小电流零序电压过压
	bQD = OverRelay(g_ui[PUB_AC_U01].m2[0], pSet->dU_xdl_u0TB, pSet->dU_xdl_u0TB_fh, g_tXDLTime_U0TB[sw].boolTrip);
	RunTR(&g_tXDLTime_U0TB[sw], bQD, dStep);

	// 记录事件
	soeno = EV_XDL_U0TB + sw*EV_SW_NUM;
	if(g_tXDLTime_U0TB[sw].boolTrip && pSet->dU_xdl_u0TB)
	{
		if(soe_check(soeno)==false)
		{
			DWORD u0;
			u0 = _Mul_Div_U(sqrt_32fix(g_ui[PUB_AC_U01].m2[0]), 256, g_ui[PUB_AC_U01].m2_factor_k);
			soe_record_ev(soeno, 1, u0,0,0);

			// 小电流零压启动录波，但不进行接地判断
			rcd_start(sw, RECORD_TYPE_LXDY, RECORD_LEN_TZQD);
		}
	}
	else
	{
		if(soe_check(soeno)==true)
		{
			soe_record_ev( soeno, 0, 0,0,0 );
		}
	}
	*/
	_xdl_protect_TZ_FG(sw, dStep);

 }
#endif


// 查询小电流接地是否动作过
int xdl_is_dz(int sw)
{
	if(g_xdl[sw].dz == 0)
	{
		return 0;
	}

	return 1;
}

static u32	xdl_get_LLTB_waves(int sw)
{
#if(EN_REC_MORE_WAVES==1)
	float set_xdl_T;
	u32   waves;

	set_xdl_T = pRunSet->tSwSet[sw].dT_xdl_T;

	waves = (u32)(set_xdl_T *1000 /20);   // 周波数= X毫秒/20ms
	if(waves > XDL_RECORD_WAVES) {
		waves = XDL_RECORD_WAVES;  //最大5秒
	}
	waves += RECORD_LEN_TZQD;

	return waves;

#else
	return  RECORD_LEN_TZQD;
#endif
}

int xdl_proc_file(char *szname,int *ok_num,int * err_num,int *skip_num,int *question_num)
{
	static char cfg_file[128];
	static char dat_file[128];
	static int ui[32];
	int fd;
	struct stat s;
	char *pdata,*name;
	int ret,seg;
	int A,D,N;
	int i,j,k;
	int d_bytes; //数字开关数占用的字节数
	int flag = 0;


	sprintf(cfg_file, "%s.cfg", szname);
	sprintf(dat_file, "%s.dat", szname); 

		//获取文件属性
	if(sys_newstat(cfg_file, &s))
	{
		rt_printf("%s:sys_newstat error\n", __func__);
		return -1;
	}

	pdata = rt_malloc(s.st_size+1);
	if(!pdata)
	{
		return -1;
	}

	pdata[s.st_size] = 0;
	
	fd = sys_open(cfg_file, O_RDONLY, 0);
	if(fd < 0)
	{
		rt_printf("%s:sys_open(%s) error\r\n", __func__, cfg_file);
		rt_free(pdata);
		return -1; 
	}

	ret = sys_read(fd, pdata, s.st_size);

	if(ret != s.st_size)
	{
		rt_printf("%s:sys_read(%d) error\r\n", __func__, ret);
		rt_free(pdata);
		return -1; 		
	}

	sys_close(fd);

	

	ret = rcd_get_A_D_N(pdata, &A, &D, &N, ui);
	if(N > XDL_SAMPLE_LEN)
	{
		N = XDL_SAMPLE_LEN;
	}

	if(ret!=0)
	{
		
		return -1;
	}

	rt_free(pdata);


		//获取文件属性
	if(sys_newstat(dat_file, &s))
	{
		rt_printf("%s:sys_newstat error\n",__func__);
		return -1;
	}	

	pdata = rt_malloc(s.st_size);
	
	if(!pdata)
	{
		return -1;
	}	

	fd = sys_open(dat_file, O_RDONLY, 0);
	if(fd < 0)
	{
		rt_printf("%s:sys_open(%s) error\r\n", __func__, dat_file);
		rt_free(pdata);
		return -1; 
	}

	ret = sys_read(fd, pdata, s.st_size);

	if(ret != s.st_size)
	{
		rt_printf("%s:sys_read(%d) error\r\n", __func__, ret);
		rt_free(pdata);
		return -1; 		
	}

	sys_close(fd);

	//查找零序电压
	for(k=0;k<A;k++)
	{
		if(ui[k]==0)
		{
			flag = 1;
			break;
			
		}
	}

	if(!flag)
	{
		rt_printf("没有找到U0\r\n");
		rt_free(pdata);
		return -1;
	}
	
	d_bytes  = ((D + 15)/16 ) * 2; 
		
	for(i=0; i<XDL_SAMPLE_LEN;i++)
	{
		if(i < N)
		{
			int r = i*(8+2*A+d_bytes)+8+2*k;
			
			g_u0_raw[i]  =  (s16)(u8)pdata[r];
			g_u0_raw[i] |=  (s16)(u8)pdata[r+1] << 8;
		}
		else
		{
			g_u0_raw[i] = 0;	
		}

	}
	// 数字信号不需要滤波
	//xdl_smooth3(g_u0_raw, g_u0, XDL_SAMPLE_LEN);
	// memcpy(g_u0,g_u0_raw,sizeof(g_u0));
	DWT_Matrix_Transform(g_u0_dwt, g_u0, XDL_SAMPLE_LEN);


	// 处理零序电压
//	ret = xdl_proc_u0(N);
//	if(ret < 0)
//	{
//		*err_num = A-1;
//		return ret;
//	}

	name = strrchr(szname,'/');
	name++;
	for(i=0;i <A; i++)
	{
		// 得到零序电流采样值
		for(j=0; j<XDL_SAMPLE_LEN;j++)
		{
			if(j < N)
			{
				if(ui[i]>0)
				{
					int r = j*(8+2*A+d_bytes)+8+2*i;
					g_i0_raw[j]  =  (s16)(u8)pdata[r];
					g_i0_raw[j] |=  (s16)((u8)pdata[r+1] << 8);	
					g_i0_dwt[j]	= (float)g_i0_raw[j];					
				}
			}
			else
			{
				g_i0_raw[j] = 0;
				g_i0_dwt[j] = 0;
			}
		}

		//xdl_smooth3(g_i0_raw, g_i0, XDL_SAMPLE_LEN);
		// memcpy(g_i0,g_i0_raw,sizeof(g_i0));
		DWT_Matrix_Transform(g_i0_dwt, g_i0, XDL_SAMPLE_LEN);

		if(ui[i]>0)
		{
			int ret,jd;
			struct timespec ts;
			TSETSW *pSet = &pRunSet->tSwSet[0];
			
			memset(&ts,0,sizeof(ts));
			
			// 零序电流处理
		//	ret = xdl_proc_i0(N,0,7,&seg);
			ret = xdl_proc_i0(0,N,pSet->dI_xdl_zt,pRunSet->dU_xdl_u0_zt[0],&seg,0);
			jd = ui[i]-1;
			if(ui[i] >10)
			{
				*question_num = *question_num + 1;
				rt_printf("线路%s_%d:波形异常(ret=%d,jd=%d)!\r\n",name,i,ret,ui[i]-11);
			}
			else if(ret < 0 && jd == 0)
			{
				*skip_num = *skip_num + 1;
				//rt_printf("线路%s_%d:无算法(ret=%d,jd=%d)!\r\n",name,i,ret,jd);
			}
			else if(((ret-1) == jd)|| (ret == jd))
			{
				*ok_num = *ok_num + 1;
				//rt_printf("线路%s_%d:判断正确(ret=%d,jd=%d)!\r\n",name,i,ret,jd);
			}
			else
			{
				*err_num = *err_num + 1;
				rt_printf("线路%s_%d:判断错误(ret=%d,jd=%d)!\r\n",name,i,ret,jd);
			//	xdl_info_printf();
				rt_printf("\r\n");
			}
			
			rcd_start_xdl(name,i,g_xdl_rcd_a,XDL_RCD_A_NUM,XDL_SAMPLE_LEN,&ts);  
		}

	}

	
	rt_free(pdata);
	
	return 0;
}

/**
 * @brief  	xdl_i0_argument_init_by_scale
 * @details 通过零流采样通道CT类型初始化小电流i0判断参数
 * @param  	none
 * @return  none
 * @author 	EW
 * @date 	2024-01-16
 * @remarks 根据采样电阻的不同，小电流用不同的突变判断定值，目的是兼容未改电阻的采样板
 */
void xdl_i0_argument_init_by_scale(void)
{
	int cfg_index;
	uint32_t sw = 0;

	memset(&g_i0_arg, 0,sizeof(g_i0_arg));
	for(sw = 0; sw < g_sw_num; sw++)
	{
		cfg_index = g_sw[sw].ac_cfg_index[SW_AC_I0];
		// TODO 待确认是否更改硬件
		// if(g_equ_config_ac[cfg_index].scale == EQU_SCALE_CT_20A_300)
		// {
		// 	g_i0_arg[sw].ref_v_set = REV_F_CT_1A;
		// 	g_i0_arg[sw].scale_is_1A = true;
		// 	// rt_printf("sw: %d,i0 CT is CT_1V/1A\n", sw+1);
		// }
		// else
		{
			g_i0_arg[sw].ref_v_set = REV_F_CT_DEFAULT;
			g_i0_arg[sw].scale_is_1A = false;
		}
	}

}

/*------------------------------ 内部函数 -------------------------------------
内部函数以下划线‘_’开头，不需要检查参数的合法性.
*/


/*------------------------------ 测试函数 -------------------------------------
一个实体文件必须带一个本模块的测试函数来进行单元测试，如果的确不方便在本模块中
进行单元测试，必须在此注明实际的测试位置（例如在哪个实体文件中使用哪个测试函数）.
*/
int xdl_info_printf(void)
{
	int i;
	rt_printf("小电流接地信息:\r\n");
	rt_printf("名称\t方向\t开始\t结束\t累加\t最大值\t最大位置\r\n");
	for(i=0;i<XDL_SEG_NUM;i++)
	{
		rt_printf("U0[%d]:\t%d\t%d\t%d\t%d\t%d\t%d\t\r\n",
				i,
				g_u0_seg[i].dir,
				g_u0_seg[i].bgn,
				g_u0_seg[i].end,
				g_u0_seg[i].acc,
				g_u0_seg[i].max_v,
				g_u0_seg[i].max_p);
		rt_printf("I0[%d]:\t%d\t%d\t%d\t%d\t%d\t%d\t\r\n",
				i,
				g_i0_seg[i].dir,
				g_i0_seg[i].bgn,
				g_i0_seg[i].end,
				g_i0_seg[i].acc,
				g_i0_seg[i].max_v,
				g_i0_seg[i].max_p); 
				
		rt_printf("\r\n");
	}

	return 0;
}


int xdl_info_fromfile_printf(void)
{
	int f_cnt=0;
	int i;
	int len;
	int ok_num,err_num,skip_num,question_num,ok_num0,err_num0,skip_num0,question_num0;
	struct dir_file_ext_struct  *p_dfes;
	static char buf[128];

	p_dfes = hf_get_dir_file_ext(RCD_XDL_FILE_PATH, &f_cnt, "/tmp/xdl.tmp");
	if(!p_dfes)
	{
		rt_printf("没有录波文件 \r\n");
		rt_file_del("/tmp/xdl.tmp");
		return -1;
	}

	ok_num=0;
	err_num=0;
	skip_num=0;
	question_num = 0;
	for(i=0;i<f_cnt;i=i+2)
	{
		sprintf(buf, "%s%s",RCD_XDL_FILE_PATH,  p_dfes[i].file_name);  
		len=strlen(buf);
		buf[len-4]=0;
		rt_printf("文件:%s\r\n", p_dfes[i].file_name);
		ok_num0=0;
		err_num0=0;
		skip_num0=0;
		question_num0 =0;
		if(xdl_proc_file(buf,&ok_num0,&err_num0,&skip_num0,&question_num0)==0)
		{
	//		xdl_info_printf();
		}
		ok_num += ok_num0;
		err_num += err_num0;
		skip_num += skip_num0;
		question_num += question_num0;
		rt_printf("\r\n");
	}

	i = ok_num+err_num+skip_num+question_num;
	if(i== 0)i=1;
	rt_printf("小电流算法:文件数=%d,线路总数=%d,正确线路=%d,忽略线路=%d,错误线路=%d,问题线路=%d,正确率=%d%%!\r\n",
		f_cnt/2,i,ok_num,skip_num,err_num,question_num,(ok_num+skip_num)*100/(i));

	rt_free(p_dfes);
	rt_file_del("/tmp/xdl.tmp");

	return 0;

}

int xdl_proc_highz(int sw)  // 根据电流值大小判断接地属性，小于200ma，认为是高阻接地，用其他算法
{

	int i;	
	int v_max,v_min,v;
	u32 i0_300ma,u0_22v,i0_30ma;
	int dotmax,dotmin,dotv;
	int bgn,end;
	int ret=0;

	if(sw<0)return -1;
	u0_22v=pRunSet->dU_xdl_22V[pRunSet->tSwSet[sw].bTT_Power_v2];
	i0_300ma=pRunSet->tSwSet[sw].dI_xdl_300ma;

	i0_30ma=i0_300ma/9;
	
	v_max = INT_MIN;
	for(i=0; i<XDL_SAMPLE_LEN;i++)
	{
		v=abs(g_i0[i]);
		if(v> v_max )
		{
			v_max =v;
		}
	}	
	if(v_max>i0_300ma||v_max<i0_300ma/4)  //最大电流大于200ma，认为不是高阻接地,最大值小于66ma，认为无零序电流
	{
		return -1;
	}

	for(i=0; i<XDL_SAMPLE_LEN;i++)
	{
		if(abs(g_u0[i])>u0_22v)break;

	}
	if(i>=XDL_SAMPLE_LEN-ADC_REC_SAMPLE/2)return -1; //电压值太小
	v_max = INT_MIN;
	v_min=INT_MAX;
	bgn=i;
	end=bgn+ADC_REC_SAMPLE/2;
	dotmax=bgn;
	dotmin=bgn;
	for(i=bgn;i<end;i++)  //找第一个极值点
	{
		v=g_u0[i];
		if(v> v_max )
		{
			v_max = v;	
			dotmax=i;
		}

		if(v< v_min )
		{
			v_min = v;	
			dotmin=i;
		}
	}
	if(dotmax>dotmin) 
	{
		dotv=dotmin;		
	}
	else
	{	
		dotv=dotmax;
	}
	bgn=dotv-ADC_REC_SAMPLE/4;
	if(bgn<0)bgn=0;
	end=dotv+ADC_REC_SAMPLE/4;
	v_max = INT_MIN;
	v_min=INT_MAX;
	for(i=bgn;i<end;i++)  //找第一个极值点
	{
		v=g_i0[i];
		if(v> v_max )
		{
			v_max = v;	
			dotmax=i;
		}

		if(v< v_min )
		{
			v_min = v;	
			dotmin=i;
		}
	}
	rt_printf("\r\ndot:bgn=%d,top=%d ,low=%d,vol=%d, top差值=%d  low差值=%d\r\n  ",dotv,dotmax,dotmin,dotv,abs(g_i0[dotv]-g_i0[dotmax]),abs(g_i0[dotv]-g_i0[dotmin]));	
	// 判断电压极值点是否也是电流极值点
	if((abs(g_i0[dotv]-g_i0[dotmax])<=i0_30ma)	
		||(abs(g_i0[dotv]-g_i0[dotmin])<=i0_30ma)
		||(abs(dotmax-dotv)<=10)
		||(abs(dotmin-dotv)<=10)
		)  // 极值点相同，为界内
	{
		rt_printf("\r\ngz界内故障\r\n");
		ret=1;
	}
	else
	{
		rt_printf("\r\ngz界外故障\r\n");
	}
	return ret;

}



#endif





/*------------------------------ 文件结束 -------------------------------------
*/

