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

#define XDL_SEG_NUM			5

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;
	uint32_t qd_us0;

	// 动作
	int dz;

	// 零偏
	s16 zero_sum;
	s16 zero_cnt;

};


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

// 零偏
//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];	// 小电流接地动作
//uint32_t g_xdl_qd_us0[SWITCH_NUM_MAX]; 	// 小电流接地启动时间

struct xdl g_xdl[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];

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

// 小电流接地零压定时器
TIMERELAY 	g_tXDLTime_U[2]; 
TIMERELAY 	g_tXDLTime_I[SWITCH_NUM_MAX]; 

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

int b_jwtz=0;

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

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

/*------------------------------ 函数声明 -------------------------------------
*/


/*------------------------------ 外部函数 -------------------------------------
外部函数供其它实体文件引用，必须仔细检查传入参数的合法性.
*/
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)
{
	int i,j;

	if(seg->dir == 0)
	{
		return;
	}
	
	for(i=0;i<XDL_SEG_NUM;i++)
	{
		if(seg->max_v > seg_a[i].max_v)
		{
			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 bgn,u32 end,struct segment * seg)
{
	s32 v,dir,dir_old;
	u32 i;

	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;
			}
		}
		// 当前区间处理
		else
		{
			// 反向,当前区间结束
			if(_xdl_dir_equ(dir,seg->dir)== 0)
			{
				i--;
				if(_xdl_dir_inv(dot[i],dot[seg->bgn-1]))
				{
					seg->max_v = abs(dot[i]);
				}
				else
				{
					seg->max_v = abs(dot[i] - dot[seg->bgn-1]);
				}
				seg->max_p = i;
			
				return 1;
			}			
			
			// 同相，扩大区间
			seg->acc += v;
			seg->end = i;
		}
	}

	seg->dir = 0;
	return 0;
}

// 微分增速段搜索,微分0在后面
int _xdl_seg_search_ds_r(s16 *dot,u32 i_set,u32 bgn,u32 end,struct segment * seg)
{
	s32 v,dir,dir_old,b_add;
	u32 i;

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

	dir = 0;
	dir_old = 0;
	b_add = 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)))
			if((dir == -seg->dir)|| ((dir == 0) && (dir_old==0)))
			{
				// 退一步，退回本区域内
				i--;
				seg->max_p = i;
				//seg->max_v = abs(dot[i] - dot[seg->bgn-1]);
				seg->max_v = abs(dot[i]);

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

				#if 0
				// 计算微分不为0区间的平均每点的变化量
				if(_xdl_dir_inv(dot[i],dot[seg->bgn-1]))
				{
					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);
				}
				#endif

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

	if(seg->dir)
	{
	// 首半波条件：
	// 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;
	}

		i_set = i_set*1.5+0.5;
		if( abs(dot[seg->max_p]) < i_set)
		{
				seg->dir = 0;
		}

		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;
		}
	}

	v = set == 0 ? (5*min_d) : set;
	if(abs(max_ui.v) < v)
	{
		return -2;

	}

	return 0;

}

// 三点线性平滑
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)
{
	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,bgn,len-1,&seg))
	{
		_xdl_seg_update(g_u0_seg,&seg);

#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;
}

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

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

	return 0;
}
int xdl_proc_i0(int len,u32 i_set,u32 u_set,int *ps)
{
	int i,s,bgn,end,ret,dir=0;
//	s32 zero_max;
//	struct point di_max;
	struct segment seg;

	// 全局电流数据清零
	memset(g_i0_seg,0,sizeof(g_i0_seg));
	
	// 零序电流求导,并得到最大差分值
#if 0
	zero_max = _xdl_proc_diff_zero(g_i0_raw,0,len,i_set,&di_max);
	if(zero_max < 0)
	{
		rt_printf("%s无流(zero_max=%d).\r\n",g_line_type[0],zero_max);
		return -1;
	}
#endif
	// 方法2: 电流微分首半波判断
	// 搜索几个电流变化最大的区间
	bgn = 1;
	while(_xdl_seg_search_ds_r(g_i0,i_set,bgn,len-1,&seg))
	{
		bgn = seg.end+1;

		// 区间有效，且区间电压有效才更新
		if(seg.dir && xdl_seg_u0_ok(len,&seg,u_set))
		{
			_xdl_seg_update(g_i0_seg,&seg);
		}
	}

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

	// 寻找电流首半波,条件是最大值最大的区域，是其前面区域最大值的2倍以上
	s = 0;
	for(i=1; i<XDL_SEG_NUM;i++)
	{
		if(g_i0_seg[i].max_v > g_i0_seg[s].max_v*2)
		{
			s = i;
		}
	}

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

		if(g_i0_seg[s].end < len)
		{
			g_i0_seg[s].end++;
		}

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

			// 延长电压检查区间
			//end = g_i0_seg[s].bgn + ADC_REC_SAMPLE/2;
			end = g_i0_seg[s].end + XDL_U0_EXTEND;
			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(abs(v) > u_set)
				{
					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("电压反向:bgn=%d,end=%d,u_set=%d,v_bgn=%d,v_end=%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]);
					}
					
					break;
				}
			}

			if(i == end)
			{
				rt_printf("电压欠幅:bgn=%d,end=%d,u_set=%d,v_max=%d.\r\n",g_i0_seg[s].bgn,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,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[i],i,u_set);
			}
		}

		// 判电压方向
		if(dir)
		{
			ret=_xdl_dir_inv(dir,g_i0_seg[s].dir);
			rt_printf("%s(s=%d,dir=%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[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
	{
		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;
}


int xdl_proc_adc(u32 sw,u32 dt,struct timespec *ts)
{
	int i,ret,seg;
	int ch0,cha,chb,chc;
	int i0,u0;

	TSETSW *pSet = &pRunSet->tSwSet[sw];
	int zero,zero_min,zero_max;

	// 得到零序电压采样值
	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];
		}

		xdl_smooth3(g_u0_raw, g_u0, XDL_SAMPLE_LEN);
	}
	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)
		{
			ret = -12;
			goto RET;
		}

		zero = 0;
		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];
			}

			xdl_smooth3(g_i0_raw, g_i0, XDL_SAMPLE_LEN);
		}
		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;
		}

		// 检查处理是否及时 
 		// 启动(1.28-0.3-0.02×6）秒内必须完成小电流数据copy，否则录波缓冲中的数据将被覆盖。
 		{
 			u32 n;
	 		n = (u32)(g_adc_dots_count<<2) - dt;
			if(n > ADC_REC_DOTS_CHANNEL)
			{
				rt_printf("处理超时:n=%d,now=%d,old=%d.\r\n",n,(g_adc_dots_count<<2),dt);
				ret = -14;
				goto RET;
			}
		}
 		
	// 零漂及动态门限处理
	zero = 0; 
	zero_min = INT_MAX;
	zero_max = INT_MIN;
	for(i=0; i<XDL_ZERO_LEN;i++)
	{
		zero += g_i0[i];
		if(g_i0[i] < zero_min )
		{
			zero_min = g_i0[i];
		}
		if(g_i0[i] > zero_max )
		{
			zero_max = g_i0[i];
		}
	}

	zero /= XDL_ZERO_LEN;
	zero_min -= zero;
	zero_min = abs(zero_min);
	zero_max -= zero;
	zero_max = abs(zero_max);
	if(zero_min > zero_max)
	{
		zero_max = zero_min;
	}
	for(i=0; i<XDL_SAMPLE_LEN;i++)
	{
		g_i0[i] -= zero;
	}
	rt_printf("zero=%d,zero_max=%d.\r\n",zero,zero_max);
	zero_max = zero_max*1.2+0.5;

	// 零序电流处理
		seg = -1;
	//	ret = xdl_proc_i0(XDL_SAMPLE_LEN,pSet->dI_xdl_zt,pRunSet->dU_xdl_u0_zt[pRunSet->tSwSet[sw].bTT_Power_v2],&seg);
	ret = xdl_proc_i0(XDL_SAMPLE_LEN,zero_max,pRunSet->dU_xdl_u0_zt[pRunSet->tSwSet[sw].bTT_Power_v2],&seg);
		//如果电流反向，结果反向。
		if(pRunSet->tSwSet[sw].bTT_Current_Inv && (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;
		}

		// 启动事件
		soe_record_ev(EV_LX_XDL_QD+sw*EV_SW_NUM, 1, i0,u0,ret);		
		if(b_jwtz>0)
		{
			if(b_jwtz==1)ret=1;
			else ret=0;
		}		
		if(ret>0) 
		{
			g_xdl[sw].dz = 1;

			// 告警
			if(pSet->bTT_xdlgj || pSet->bTT_xdltz)
			{
				soe_record_ev(EV_LX_XDL_GJ+sw*EV_SW_NUM, 1, i0,u0,0 );
			}

			// 跳闸
			if(pSet->bTT_xdltz)
			{
				sw_do_yuk(SW_DO_TYPE_ON);
				sw_do(sw,SW_DO_FZ,SW_DO_TYPE_ON);
				sw_do(sw,SW_DO_BHT,SW_DO_TYPE_ON);
				soe_record_ev(EV_LX_XDL_TZ+sw*EV_SW_NUM, 1, i0,u0,0 );
				
			}
		}

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

		ret = 0;

RET:

	g_xdl[sw].st = XDL_ST_END;
	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).\r\n",sw,ret,pRunSet->tSwSet[sw].bTT_Current_Inv);	
	return ret;
}

// 小电流突变量启动，放在156us中断中
void xdl_tbl_qd(int mod)
{
	s16 v,v0,v1,zero;
	u32 sw,ui;
//	u32 index = g_adc_dots_index_rec - 1;
	u32 index = ((g_adc_dots_count-1)<<2) + mod;
	
	for (sw=0; sw<g_sw_num; sw++)
	{
		TSETSW *pSet = &pRunSet->tSwSet[sw];
		
		//电流突变量启动前提条件:1、录波投入。2、保护总投入，小电流接地电流启动投入。
		if(g_rcd_type_enable[RECORD_TYPE_LLTB]== 0)
		{
			if((BH_ALL_EN(sw) == 0)|| (pSet->bTT_xdlqd != XDL_QD_I0))
		{
			continue;
		}
		}
		
		// 如果索引通道不存在，不需计算
		ui = UI_SW_INDEX(sw,SW_AC_I0);
		if(g_ui[ui].chn_index == CFG_ADC_CHANNEL_ZERO)
		{
			continue;
		}

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

		// 已启动，退出
		if(g_xdl[sw].st)
		{
			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) > pRunSet->tSwSet[sw].dI_xdl_zt))    
		{
			int ret;

			// 置启动标志，不能放在录波启动成功的条件里，避免启动失败后频繁打印
			g_xdl[sw].st = XDL_ST_QT;
			g_xdl[sw].qd_us0 = ustimer_get_origin();

			ret = rcd_start(sw,RECORD_TYPE_LLTB,RECORD_LEN_TZQD);
			if(ret == 0)
			{
				//rt_printf("小电流突变启动:sw=%d,v=%d,i_set=%d,zero=%d.\r\n",sw,v,pRunSet->tSwSet[sw].dI_xdl_zt,zero);
			}
			else
			{
				rt_printf("录波启动失败(ret=%d,sw=%d.\r\n)",ret,sw);
			}
		}
	}
}


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)
				{
					if((BH_ALL_EN(sw)) && (pRunSet->tSwSet[sw].bTT_xdlqd == XDL_QD_U0))
					{
						// 置启动标志，不能放在录波启动成功的条件里，避免启动失败后频繁打印
						g_xdl[sw].st = XDL_ST_QT;
						g_xdl[sw].qd_us0 = ustimer_get_origin();
					}
					
					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)
				{
					if((BH_ALL_EN(sw)) && (pRunSet->tSwSet[sw].bTT_xdlqd == XDL_QD_U0))
					{
						// 置启动标志，不能放在录波启动成功的条件里，避免启动失败后频繁打印
						g_xdl[sw].st = XDL_ST_QT;
						g_xdl[sw].qd_us0 = ustimer_get_origin();
					}
					
					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++)
	
	{
		// 电流闭锁复归
		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);

		// 两次判断最小间隔为5S.
		if((g_xdl[sw].st) && (ustimer_get_duration(g_xdl[sw].qd_us0) > (5*USTIMER_SEC)))
		{
	
			// 如果接地启动，必须等小电流零压零流消失后才能复归
			if(g_tXDLTime_U[pRunSet->tSwSet[sw].bTT_Power_v2].boolTrip || g_tXDLTime_I[sw].boolTrip)
			{
				continue;
			}

			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;

			// 启动
			soeno = EV_LX_XDL_QD+sw*EV_SW_NUM;
			if(soe_check(soeno))
			{
				soe_record_ev(soeno, 0, 0,0,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 );
			}

		}
	}
 }


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

	return 1;
}


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;
	}

	rt_free(pdata); // sunxi 20180719 原先释放内存放在判断后面，需要放在前面。

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


		//获取文件属性
	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));


	// 处理零序电压
//	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);						
				}
			}
			else
			{
				g_i0_raw[j] = 0;
			}
		}

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

		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(N,pSet->dI_xdl_zt,pRunSet->dU_xdl_u0_zt[0],&seg);
			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 == 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;
}


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


/*------------------------------ 测试函数 -------------------------------------
一个实体文件必须带一个本模块的测试函数来进行单元测试，如果的确不方便在本模块中
进行单元测试，必须在此注明实际的测试位置（例如在哪个实体文件中使用哪个测试函数）.
*/
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





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

