xdljd.c 73 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374237523762377237823792380238123822383238423852386238723882389239023912392239323942395239623972398239924002401240224032404240524062407240824092410241124122413241424152416241724182419242024212422242324242425242624272428242924302431243224332434243524362437243824392440244124422443244424452446244724482449245024512452245324542455245624572458245924602461246224632464246524662467246824692470247124722473247424752476247724782479248024812482248324842485248624872488248924902491249224932494249524962497249824992500250125022503250425052506250725082509251025112512251325142515251625172518251925202521252225232524252525262527252825292530253125322533253425352536253725382539254025412542254325442545254625472548254925502551255225532554255525562557255825592560256125622563256425652566256725682569257025712572257325742575257625772578257925802581258225832584258525862587258825892590259125922593259425952596259725982599260026012602260326042605260626072608260926102611261226132614261526162617261826192620262126222623262426252626262726282629263026312632263326342635263626372638263926402641264226432644264526462647264826492650265126522653265426552656265726582659266026612662266326642665266626672668266926702671267226732674267526762677267826792680268126822683268426852686268726882689269026912692269326942695269626972698269927002701270227032704270527062707270827092710271127122713271427152716271727182719272027212722272327242725272627272728272927302731273227332734273527362737273827392740274127422743274427452746274727482749275027512752275327542755275627572758275927602761276227632764276527662767276827692770277127722773277427752776277727782779278027812782278327842785278627872788278927902791279227932794279527962797279827992800280128022803280428052806280728082809281028112812281328142815281628172818281928202821282228232824282528262827282828292830283128322833283428352836283728382839284028412842284328442845284628472848284928502851285228532854285528562857285828592860286128622863286428652866286728682869287028712872287328742875287628772878287928802881288228832884288528862887288828892890289128922893289428952896289728982899290029012902290329042905290629072908290929102911291229132914291529162917291829192920292129222923292429252926292729282929293029312932293329342935293629372938293929402941294229432944294529462947294829492950295129522953295429552956295729582959296029612962296329642965296629672968296929702971297229732974297529762977297829792980298129822983298429852986298729882989299029912992299329942995299629972998299930003001300230033004300530063007300830093010301130123013301430153016301730183019302030213022302330243025302630273028302930303031303230333034303530363037303830393040304130423043304430453046304730483049305030513052305330543055305630573058305930603061306230633064306530663067306830693070307130723073307430753076307730783079308030813082308330843085308630873088308930903091309230933094309530963097309830993100310131023103310431053106310731083109311031113112311331143115311631173118311931203121312231233124312531263127312831293130313131323133313431353136313731383139314031413142314331443145314631473148314931503151315231533154315531563157315831593160316131623163316431653166316731683169317031713172317331743175317631773178317931803181318231833184318531863187318831893190319131923193319431953196319731983199320032013202320332043205320632073208320932103211321232133214321532163217321832193220322132223223322432253226322732283229323032313232323332343235323632373238
  1. /******************************************************************************
  2. 版权所有:
  3. 文件名称: xdljd.c
  4. 文件版本: 01.01
  5. 创建作者: sunxi
  6. 创建日期: 2016-12-22
  7. 功能说明: 小电流接地检测
  8. 其它说明: !!!!!在不了解整个原理的情况下,不要轻易修改此文件中的算法!!!!!!
  9. 修改记录:
  10. */
  11. /*------------------------------- 头文件 --------------------------------------
  12. */
  13. #include "head.h"
  14. #ifdef XDL_ZT
  15. /*------------------------------- 宏定义 --------------------------------------
  16. */
  17. #define XDL_ZERO_LEN (ADC_REC_SAMPLE/4)
  18. #define XDL_SEG_NUM 10
  19. enum
  20. {
  21. XDL_ST_IDLE,
  22. XDL_ST_QT,
  23. XDL_ST_END,
  24. };
  25. /*------------------------------ 类型结构 -------------------------------------
  26. */
  27. struct point
  28. {
  29. s32 v;
  30. u32 p;
  31. };
  32. struct segment
  33. {
  34. u32 bgn; // 段开始
  35. u32 end; // 段结束
  36. u32 acc; // 累加值
  37. u32 max_p; // 最大值出现的位置
  38. u32 max_v; // 最大值的绝对值
  39. s32 dir; // 方向
  40. };
  41. struct xdl
  42. {
  43. // 状态
  44. int st;
  45. unsigned long qd_us0;
  46. unsigned long dz_us0;
  47. unsigned long gj_us0;
  48. unsigned long dz_fh_us0;
  49. // unsigned long gj_fh_us0;
  50. // 动作
  51. int dz;
  52. // 动作值记忆
  53. int dz_i0[2];
  54. int dz_u0[2];
  55. int dz_Ia[2];
  56. int dz_Ib[2];
  57. int dz_Ic[2];
  58. // 零偏
  59. s16 zero_sum;
  60. s16 zero_cnt;
  61. };
  62. struct i0_argument
  63. {
  64. s16 ref_v_set;
  65. bool scale_is_1A;
  66. };
  67. /*------------------------------ 全局变量 -------------------------------------
  68. */
  69. // 零偏
  70. //s16 g_xdl_zero_sum[SWITCH_NUM_MAX]; // 零偏和
  71. //s16 g_xdl_zero_cnt[SWITCH_NUM_MAX]; // 零偏计数
  72. //int g_xdl_qd[SWITCH_NUM_MAX]; // 小电流接地启动
  73. //int g_xdl_dz[SWITCH_NUM_MAX]; // 小电流接地动作
  74. //unsigned long g_xdl_qd_us0[SWITCH_NUM_MAX]; // 小电流接地启动时间
  75. struct xdl g_xdl[SWITCH_NUM_MAX];
  76. struct i0_argument g_i0_arg[SWITCH_NUM_MAX];
  77. // 滤波后数据
  78. s16 g_u0[XDL_SAMPLE_LEN];
  79. s16 g_i0[XDL_SAMPLE_LEN];
  80. // 原始的数据
  81. s16 g_u0_raw[XDL_SAMPLE_LEN];
  82. s16 g_i0_raw[XDL_SAMPLE_LEN];
  83. // 小波处理的数据
  84. float g_u0_dwt[XDL_SAMPLE_LEN];
  85. float g_i0_dwt[XDL_SAMPLE_LEN];
  86. struct segment g_u0_seg[XDL_SEG_NUM];
  87. struct segment g_i0_seg[XDL_SEG_NUM];
  88. // 小电流接地零压零流定时器
  89. #ifdef RECORD_SW_U0TB
  90. TIMERELAY g_tXDLTime_U0TB[SWITCH_NUM_MAX]; //零压突变定时器
  91. #else
  92. TIMERELAY g_tXDLTime_U[2];
  93. #endif
  94. TIMERELAY g_tXDLTime_I[SWITCH_NUM_MAX];
  95. int g_cur_direction_adaptaion = 0; //临时在此定义 EWen // TODO 方向自适应处理
  96. int XDLCHZWC = 0;
  97. char * g_xdl_rcd_a[] =
  98. {
  99. "U0_原始",
  100. "U0_滤波",
  101. "U0_区域",
  102. "U0_最大",
  103. "I0_原始",
  104. "I0_滤波",
  105. "I0_区域",
  106. "I0_最大",
  107. };
  108. #define XDL_RCD_A_NUM (sizeof(g_xdl_rcd_a)/sizeof(char *))
  109. const char * g_line_type[] =
  110. {
  111. "界外动作:",
  112. "界内动作:",
  113. };
  114. const char * g_tbl_type[] =
  115. {
  116. "小电流突变",
  117. "零压突变",
  118. };
  119. /*------------------------------ 函数声明 -------------------------------------
  120. */
  121. #if 0
  122. static void xdl_ar_dataset(void);
  123. static void xdl_ar_recharge(DWORD dStep);
  124. static void xdl_ar_disable(void);
  125. static void xdl_ar_start(DWORD dStep);
  126. static void xdl_ar_logic(DWORD dStep);
  127. static void xdl_ar_reset(DWORD dStep);
  128. static int xdl_ar_check(DWORD mode); // 检同期
  129. #endif
  130. static u32 xdl_get_LLTB_waves(int sw);
  131. /*------------------------------ 外部函数 -------------------------------------
  132. 外部函数供其它实体文件引用,必须仔细检查传入参数的合法性.
  133. */
  134. #if 0
  135. /**************************************************************************
  136. 函数名称: xdl_auto_reclose
  137. 函数版本:1.00
  138. 作者: 赵海洋
  139. 创建日期:2021.6.10
  140. 函数功能说明:小电流重合闸功能块
  141. 输入参数:
  142. 输出参数:
  143. 返回值:
  144. ***************************************************************************/
  145. void xdl_auto_reclose(DWORD dStep)
  146. {
  147. xdl_ar_dataset(); // 小电流重合闸数据准备
  148. xdl_ar_recharge(dStep); // 小电流重合闸充电
  149. xdl_ar_disable(); // 小电流重合闸闭锁
  150. xdl_ar_start(dStep); // 小电流重合闸启动
  151. xdl_ar_logic(dStep); // 小电流重合闸逻辑
  152. xdl_ar_reset(dStep); // 小电流重合闸复归
  153. return;
  154. }
  155. /**************************************************************************
  156. 函数名称: xdl_ac_check
  157. 函数版本:1.00
  158. 作者: 赵海洋
  159. 创建日期:2021.6.10
  160. 函数功能说明:小电流重合闸加速块
  161. 输入参数:
  162. 输出参数:
  163. 返回值:
  164. ***************************************************************************/
  165. void xdl_ac_check(int sw,DWORD dStep)
  166. {
  167. static bool bFZOut, bQD=false;
  168. bool bQDD;
  169. TRELAY_T *pR=&g_tRelay[sw];
  170. TXDLCH_T *pSW=&pR->tXDLCHZ;
  171. TSWST *pSWST =&pR->tSWST;
  172. TSETSW *pSet = &pRunSet->tSwSet[sw];
  173. /***********启动 **********************************/
  174. // 开放后加速允许有效延时3S展宽
  175. RunTR(&pSW->tTXDLCHJLY_HJS, XDLCHZWC, dStep);
  176. // 时间到
  177. if (pSW->tTXDLCHJLY_HJS.boolTrip)
  178. {
  179. XDLCHZWC =0;
  180. ResetTR(&pSW->tTXDLCHJLY_HJS);
  181. if (bFZOut)
  182. {
  183. soe_record_ev(EV_OVERU0+sw*EV_SW_NUM, 0, 0,0,0 );
  184. bFZOut = 0;
  185. }
  186. }
  187. if(pRunSet->tSwSet[sw].bTT_Power_v2)
  188. {
  189. bQDD = OverRelay(g_ui[PUB_AC_U02].m2[0],pRunSet->dOverU0,pRunSet->dOverU0_fh,bQD);
  190. }
  191. else
  192. {
  193. bQDD = OverRelay(g_ui[PUB_AC_U01].m2[0],pRunSet->dOverU0,pRunSet->dOverU0_fh,bQD);
  194. }
  195. bQD = BH_ALL_EN(sw)
  196. && pSet->bTT_xdltz//投退字
  197. && pSWST->uSWST.bFlag.bHZWZ //在合闸位置
  198. && bQDD
  199. && XDLCHZWC
  200. && !pSW->tTXDLCHJLY_HJS.boolTrip;
  201. /************事件记录及出口****************************/
  202. if(bQD)
  203. {
  204. if (!bFZOut) // 保证只动作一次未进行跳闸失败检测
  205. {
  206. DWORD U0;
  207. sw_do(sw,SW_DO_BHT,SW_DO_TYPE_ON);
  208. if(pRunSet->tSwSet[sw].bTT_Power_v2)
  209. {
  210. U0 = _Mul_Div_U(sqrt_32fix(g_ui[PUB_AC_U02].m2[0]), 256, g_ui[PUB_AC_U02].m2_factor_k);
  211. }
  212. else
  213. {
  214. U0 = _Mul_Div_U(sqrt_32fix(g_ui[PUB_AC_U01].m2[0]), 256, g_ui[PUB_AC_U01].m2_factor_k);
  215. }
  216. soe_record_ev( EV_OVERU0+sw*EV_SW_NUM, 1, U0,0,0 );
  217. bFZOut = 1;
  218. rt_printf("开关%d小电流检零压跳闸\r\n", sw);
  219. }
  220. }
  221. }
  222. /**************************************************************************
  223. 函数名称: xdl_ar_dataset
  224. 函数版本:1.00
  225. 作者: 赵海洋
  226. 创建日期:2021.6.10
  227. 函数功能说明:小电流重合闸数据准备
  228. 输入参数:
  229. 输出参数:
  230. 返回值:
  231. ***************************************************************************/
  232. static void xdl_ar_dataset(void)
  233. {
  234. u32 sw = g_protect.sw;
  235. TRELAY_T *pR=&g_tRelay[sw];
  236. TXDLCH_T *pSW =&pR->tXDLCHZ;
  237. InitTR_Time(&pSW->tTXDLCHJD_ARDelay, 10*T_1s, 0 ); // 小电流重合闸延时
  238. InitTR_Time(&pSW->tTXDLCHFAIL_TW, 11*T_1s, 0 ); // 小电流重合闸延时
  239. }
  240. /**************************************************************************
  241. 函数名称: xdl_ar_disable
  242. 函数版本:1.00
  243. 作者: 赵海洋
  244. 创建日期:2021.6.10
  245. 函数功能说明:小电流重合闸闭锁
  246. 输入参数:
  247. 输出参数:
  248. 返回值:
  249. ***************************************************************************/
  250. static void xdl_ar_disable(void)
  251. {
  252. u32 sw = g_protect.sw;
  253. TRELAY_T *pR=&g_tRelay[sw];
  254. TXDLCH_T *pSW =&pR->tXDLCHZ;
  255. pSW->sta.bFlag.bBSCH = pR->tOC[OC_LX1].sta.bFlag.bBSCH // 零序1闭锁重合闸
  256. || pR->tOC[OC_LX2].sta.bFlag.bBSCH // 零序2闭锁重合闸
  257. || pR->tOC[OC_LX3].sta.bFlag.bBSCH // 零序3闭锁重合闸
  258. || pR->tLostVot.uLostVot.bFlag.bTz // 失压跳闸闭锁重合
  259. || pR->tXXTZ.uZOV.bFlag.bTz
  260. || pR->tU0TZ.uZOV.bFlag.bTz ; // 零压分闸闭锁重合
  261. }
  262. /**************************************************************************
  263. 函数名称: xdl_ar_recharge
  264. 函数版本:1.00
  265. 作者: 赵海洋
  266. 创建日期:2021.6.10
  267. 函数功能说明:小电流重合闸充电
  268. 输入参数:
  269. 输出参数:
  270. 返回值:
  271. ***************************************************************************/
  272. static void xdl_ar_recharge(DWORD dStep)
  273. {
  274. u32 sw = g_protect.sw;
  275. TRELAY_T *pR=&g_tRelay[sw];
  276. TXDLCH_T *pSW =&pR->tXDLCHZ;
  277. TSETSW *pSet = &pRunSet->tSwSet[sw];
  278. bool bHCD1,bHCD2,bHCD3,bHCD4;
  279. // 放电条件
  280. RunTR(&pSW->tTXDLCHFAIL_TW, pR->tSWST.uSWST.bFlag.bTZWZ, dStep); // 三相跳位延时
  281. bHCD1 = pSW->tTXDLCHFAIL_TW.boolTrip; // 放电标志1:三相跳位时间到
  282. bHCD2 = (pR->tSWST.uSWST.bFlag.bTZWZ || pR->uBHDZ.bFlag.bBHT) // 此处小电流动作标志或者有保护动作
  283. && (!pSW->sta.bFlag.bCDWC); // 放电标志2:充电未完成,小电流动作
  284. bHCD3 = pR->uRmtSW.bHandTz // 手动跳闸
  285. || pR->uRmtSW.bYTExecute // 遥控跳闸
  286. || pR->uBHDZ.bFlag.bTZSB // 跳闸失败
  287. || pR->tSWST.uSWST.bFlag.bTWYL // 跳位有流
  288. || pR->tSWST.uSWST.bFlag.bWCN // 弹簧未储能告警
  289. || pSW->sta.bFlag.bBSCH // 重合闸闭锁条件
  290. || pSW->sta.bFlag.bCHFD; // 重合闸动作放电
  291. pSW->sta.bFlag.bFDBZ = (!BH_ALL_EN(sw)) // 总保护未投入
  292. || (!pSet->bTT_xdltz) // 小电流跳闸: 未投入
  293. || (!pR->run_stu.chz) // 重合闸硬压板未投入
  294. || bHCD1
  295. || bHCD2
  296. || bHCD3;
  297. // 充电条件
  298. bHCD4 = !pSW->sta.bFlag.bFDBZ // 重合放电标
  299. && (((!pR->uBHQD.bFlag.bZQD) && pR->tSWST.uSWST.bFlag.bHZWZ ) // 非保护启动 && 合闸位置
  300. || pSW->sta.bFlag.bCDWC); // 充电完成自保持
  301. RunTR(&pSW->tTXDLCHCD15s0s, bHCD4, dStep); // 充电15秒
  302. pSW->sta.bFlag.bCDWC = pSW->tTXDLCHCD15s0s.boolTrip;
  303. if(pSW->sta.bFlag.bCDWC)
  304. {
  305. // TODO:液晶应增加小电流重合闸标志
  306. if((g_disp_flag.xdl_chz_cd & (1<<sw)) == 0)
  307. {
  308. g_disp_flag.xdl_chz_cd |= 1<<sw;
  309. rt_printf_time("开关%02d小电流充电完成!\r\n",sw+1);
  310. }
  311. }
  312. else
  313. {
  314. if((g_disp_flag.xdl_chz_cd & (1<<sw)))
  315. {
  316. g_disp_flag.xdl_chz_cd &= ~(1<<sw);
  317. pSW->sta.bFlag.bXDLDZ = false;
  318. pR->tXDLCHZ.sta.wfFlag=0;
  319. pR->tXDLCHZ.wAR_ActTimes=0;
  320. ResetTR(&pR->tXDLCHZ.tTXDLCHJD_HJS);
  321. rt_printf_time("开关%02d小电流放电完成!\r\n",sw+1);
  322. }
  323. }
  324. }
  325. /**************************************************************************
  326. 函数名称: xdl_ar_start
  327. 函数版本:1.00
  328. 作者: 赵海洋
  329. 创建日期:2021.6.10
  330. 函数功能说明:小电流重合闸启动
  331. 输入参数:
  332. 输出参数:
  333. 返回值:
  334. ***************************************************************************/
  335. static void xdl_ar_start(DWORD dStep)
  336. {
  337. bool bCHQD_CHQD; // 三相启动重合闸
  338. u32 sw = g_protect.sw;
  339. TRELAY_T *pR=&g_tRelay[sw];
  340. TXDLCH_T *pSW =&pR->tXDLCHZ;
  341. TSETSW *pSet = &pRunSet->tSwSet[sw];
  342. bool bCQD1,bCQD2,bCQD3,bCQD4,bCQD5,bCQD6;
  343. // 不对应启动
  344. // 开关位置从合闸位置切换至分闸位置完成后,保留50ms展宽
  345. RunTR(&pSW->tTXDLCHQDHW0ms50ms, pR->tSWST.uSWST.bFlag.bHZWZ, dStep);
  346. bCQD1 = pSW->tTXDLCHQDHW0ms50ms.boolTrip;
  347. // 偷跳条件准备
  348. bCQD2 = pSet->bTT_TTCH
  349. && bCQD1
  350. && (!pR->tSWST.uSWST.bFlag.bHZWZ);
  351. // 保护启动
  352. // 保护动作后至保护动作返回,保留50ms展宽
  353. RunTR(&pSW->tTXDLCHQD0ms50ms, pR->uBHDZ.bFlag.bBHT, dStep);
  354. bCQD3 = pSW->tTXDLCHQD0ms50ms.boolTrip;
  355. // 重合已启动或保护启动,用于闭锁不对应启动
  356. bCQD4 = pSW->sta.bFlag.bCHQD ||bCQD3;
  357. // 此处保护跳信号,是实时信号,即保护已返回
  358. bCQD5 = bCQD3 && (!pR->uBHDZ.bFlag.bBHTrip);
  359. // 重合启动自保持
  360. // 重合闸出口条件,用于防止重合闸合闸过程中,TWJ没有返回时,再次误发启动重合闸报文。
  361. bCQD6 = pSW->sta.bFlag.bCDWC // 重合充电完成
  362. && pSW->sta.bFlag.bCHQD // 三相启动重合闸
  363. && (!pSW->sta.bFlag.bCHCK); // 重合闸出口,取反
  364. // 偷跳启动重合闸
  365. pSW->sta.bFlag.bTTQD = pSW->sta.bFlag.bCDWC // 重合闸充电完成
  366. && bCQD2 // 偷跳判断完成
  367. && (!bCQD4); // 重合未启动,无保护跳过程
  368. // 重合闸检同期
  369. // bCQD7 =xdl_ar_check(pSet->dCKModes);
  370. // 三跳启动重合闸
  371. #if 0
  372. pSW->sta.bFlag.bTLQD = pSW->sta.bFlag.bCDWC // 重合闸充电完成
  373. &&bCQD7 // 湖南专检重合闸检同期
  374. &&pSW->sta.bFlag.bXDLDZ //
  375. && (!pSW->sta.bFlag.bCHQD); // 三相启动重合闸未启动
  376. #else
  377. pSW->sta.bFlag.bTLQD = pSW->sta.bFlag.bCDWC // 重合闸充电完成
  378. &&pSW->sta.bFlag.bXDLDZ //
  379. && (!pSW->sta.bFlag.bCHQD); // 三相启动重合闸未启动
  380. #endif
  381. // 三相启动重合闸
  382. bCHQD_CHQD = pSW->sta.bFlag.bTTQD // 偷跳启动重合闸
  383. || pSW->sta.bFlag.bTLQD // 三跳启动重合闸
  384. || bCQD6; // 三相启动重合闸
  385. if( bCHQD_CHQD )
  386. {
  387. if( !pSW->sta.bFlag.bCHQD ) //重合开出标尚未置位
  388. {
  389. pSW->sta.bFlag.bCHQD = true; //此标志会一直维持到重合闸出口或充电标志放电
  390. pSW->sta.bFlag.bQDFG=true; //整组复归时有效
  391. soe_record_ev(EV_AR_START+sw*EV_SW_NUM, 1, 0,0,0);//重合启动SOE
  392. //rcd_start(sw,RECORD_TYPE_CHZ, RECORD_LEN_TZQD); //录波类型:跳闸类
  393. }
  394. }
  395. else
  396. {
  397. if( pSW->sta.bFlag.bCHQD )
  398. {
  399. pSW->sta.bFlag.bCHQD = false;
  400. soe_record_ev(EV_AR_START+sw*EV_SW_NUM,0 ,0,0,0);
  401. }
  402. }
  403. }
  404. /**************************************************************************
  405. 函数名称: xdl_ar_logic
  406. 函数版本:1.00
  407. 作者: 赵海洋
  408. 创建日期:2021.6.10
  409. 函数功能说明:小电流重合闸逻辑
  410. 输入参数:
  411. 输出参数:
  412. 返回值:
  413. ***************************************************************************/
  414. static void xdl_ar_logic(DWORD dStep)
  415. {
  416. bool bWCN;
  417. u32 sw = g_protect.sw;
  418. TRELAY_T *pR=&g_tRelay[sw];
  419. TXDLCH_T *pSW =&pR->tXDLCHZ;
  420. TSETSW *pSet = &pRunSet->tSwSet[sw];
  421. bool bHZL1,bHZL2,bHZL3,bHZL4,bHZL5,bHZL6,bHZL8,bHZL9;
  422. // 重合闸条件准备
  423. bHZL1 = BH_ALL_EN(sw) // 总投退
  424. && pSet->bTT_xdltz // 小电流跳闸投入
  425. && pR->run_stu.chz // 重合闸硬压板投入
  426. && pSW->sta.bFlag.bCHQD // 三相启动重合闸
  427. && pSW->sta.bFlag.bCDWC; // 重合闸充电完成
  428. RunTR(&pSW->tTXDLCHJD_ARDelay, bHZL1, dStep); //重合延时
  429. bHZL2 = pSW->tTXDLCHJD_ARDelay.boolTrip;
  430. // 弹簧未储能下降沿展宽30ms
  431. bWCN = pR->run_stu.wcn && pSet->bTT_WCN;
  432. RunTR(&pSW->tTXDLCHJD_0ms30ms, bWCN, dStep);
  433. bHZL3 = pSW->tTXDLCHJD_0ms30ms.boolTrip;
  434. // 重合闸检同期
  435. bHZL9 =xdl_ar_check(pSet->dCKModes);
  436. // 重合闸条件准备好,弹簧储能OK,重合闸出口
  437. pSW->sta.bFlag.bCHCK = (!bHZL3) && bHZL2 && bHZL9;
  438. // 重合闸开出自保持
  439. bHZL4 = pSW->sta.bFlag.bHz || pSW->sta.bFlag.bCHCK;
  440. // 重合开出持续200ms
  441. RunTR(&pSW->tTXDLCHJD_200ms0ms, bHZL4, dStep);
  442. bHZL5 = pSW->tTXDLCHJD_200ms0ms.boolTrip;
  443. // 重合闸开出,经过200ms后开出解除
  444. pSW->sta.bFlag.bHz =bHZL4 && (!bHZL5);
  445. // 重合闸指令发出后,且开关已经跳闸完成
  446. bHZL6 = pSW->sta.bFlag.bHz || pSW->sta.bFlag.bHZL7;
  447. pSW->sta.bFlag.bHZL7 = pR->tSWST.uSWST.bFlag.bTZWZ && bHZL6;
  448. // 开放后加速允许有效延时200ms展宽
  449. RunTR(&pSW->tTXDLCHJD_HJS, pSW->sta.bFlag.bHZL7, dStep);
  450. bHZL8 = pSW->tTXDLCHJD_HJS.boolTrip;
  451. pSW->sta.bFlag.bCHJS = pSW->tTXDLCHJD_HJS.boolTrip
  452. && (!pR->tSWST.uSWST.bFlag.bTZWZ);
  453. //出口及发信
  454. if(pSW->sta.bFlag.bHz)
  455. {
  456. if(!pSW->sta.bFlag.bEvent) //重合开出标尚未置位
  457. {
  458. // 检零压后加速标识
  459. XDLCHZWC = 1;
  460. //亮重合动作灯
  461. pSW->sta.bFlag.bCHZLed = true;
  462. pSW->wAR_ActTimes++; //重合动作次数
  463. pSW->sta.bFlag.bEvent = true;
  464. //pSW->uCHFG.wfFlag=0; //每次重合出口后,清重合复归标志 修改时注意,此处需对标志字特殊处理
  465. pSW->sta.bFlag.bCHDZ=false; //重合闸动作
  466. pSW->sta.bFlag.bBYSD=false; //重合闸闭锁时间到
  467. pSW->sta.bFlag.bCHFD=false; //重合闸动作放电
  468. pSW->sta.bFlag.bCHBS=false; //重合闸闭锁
  469. pSW->sta.bFlag.bCK=false; //放电条件1
  470. if(pSW->wAR_ActTimes==1)
  471. {
  472. soe_record_ev(EV_AR1+sw*EV_SW_NUM, 1, 0,0,0); //重合动作SOE
  473. }
  474. else if(pSW->wAR_ActTimes==2)
  475. {
  476. soe_record_ev(EV_AR2+sw*EV_SW_NUM, 1, 0,0,0); //重合动作SOE
  477. }
  478. else
  479. {
  480. soe_record_ev(EV_AR3+sw*EV_SW_NUM, 1, 0,0,0); //重合动作SOE
  481. }
  482. rcd_start(sw,RECORD_TYPE_CHZ,RECORD_LEN_TZQD);
  483. ResetTR(&pSW->tTXDLCHFAIL_TW); // 重合出口后,重新复归时间继电器
  484. ResetTR(&pSW->tTXDLCHOKTime); //重合成功时间继电器,在重合出口后重新计数
  485. ResetTR(&pSW->tTXDLCHBSDelay); //闭锁重合延时继电器重新计数
  486. }
  487. }
  488. else
  489. {
  490. if( pSW->sta.bFlag.bEvent ) //重合开出标尚未置位
  491. {
  492. pSW->sta.bFlag.bEvent = false;
  493. if(soe_check(EV_AR1+sw*EV_SW_NUM))
  494. {
  495. soe_record_ev(EV_AR1+sw*EV_SW_NUM, 0, 0,0,0 ); //重合1动作SOE返回
  496. }
  497. if(soe_check(EV_AR2+sw*EV_SW_NUM))
  498. {
  499. soe_record_ev(EV_AR2+sw*EV_SW_NUM, 0, 0,0,0 ); //重合2动作SOE返回
  500. }
  501. if(soe_check(EV_AR3+sw*EV_SW_NUM))
  502. {
  503. soe_record_ev(EV_AR3+sw*EV_SW_NUM, 0, 0,0,0 ); //重合3动作SOE返回
  504. }
  505. }
  506. }
  507. }
  508. /**************************************************************************
  509. 函数名称: xdl_ar_reset
  510. 函数版本:1.00
  511. 作者: 赵海洋
  512. 创建日期:2021.6.10
  513. 函数功能说明:小电流重合闸复归
  514. 输入参数:
  515. 输出参数:
  516. 返回值:
  517. ***************************************************************************/
  518. static void xdl_ar_reset(DWORD dStep)
  519. {
  520. u32 sw = g_protect.sw;
  521. TRELAY_T *pR=&g_tRelay[sw];
  522. TXDLCH_T *pSW =&pR->tXDLCHZ;
  523. bool bFD1;
  524. // 重合闸动作后,即启动重合闸闭锁时间,在此时间内,有保护动作即闭锁重合闸
  525. // 重合闸出口且重合闸动作次数<整定动作次数,重合闸闭锁
  526. pSW->sta.bFlag.bCHDZ = (pSW->sta.bFlag.bCHCK && (pSW->wAR_ActTimes<1))
  527. || pSW->sta.bFlag.bCHBS;
  528. // 重合闸闭锁延时,从重合闸动作开始统计
  529. RunTR(&pSW->tTXDLCHBSDelay, pSW->sta.bFlag.bCHDZ, dStep);
  530. pSW->sta.bFlag.bBYSD = pSW->tTXDLCHBSDelay.boolTrip;
  531. pSW->sta.bFlag.bCHBS = (pSW->wAR_ActTimes<1) // 未到最后一次重合
  532. && pSW->sta.bFlag.bCHDZ // 一次重合闸动作
  533. && (!pSW->sta.bFlag.bBYSD); // 重合闸闭锁时间未到
  534. // 重合闸出口后,经过重合闸确认时间,无保护动作,即放电,完成该次重合过程
  535. // 如果在确认时间内,重合闭锁时间外,有保护动作,即进入下一计数重合
  536. pSW->sta.bFlag.bCK = ((!pR->uBHDZ.bFlag.bBHT)
  537. && (pSW->sta.bFlag.bCHCK || pSW->sta.bFlag.bCK)); //重合出口及自保持
  538. RunTR(&pSW->tTXDLCHOKTime, pSW->sta.bFlag.bCK, dStep);
  539. bFD1 = pSW->tTXDLCHOKTime.boolTrip;
  540. // 重合闸闭锁时间内,有保护动作,放电
  541. // 重合闸次数已到最大,放电
  542. // 重合成功,重合闸确认时间内无保护动作,放电
  543. pSW->sta.bFlag.bCHFD = (pR->uBHDZ.bFlag.bBHT && pSW->sta.bFlag.bCHBS)
  544. || (pSW->sta.bFlag.bCHCK && (pSW->wAR_ActTimes>=1))
  545. || bFD1;
  546. // 整组复归条件:(1)重合闸放电(2)重合已启动,但充电标志已清零
  547. if(pSW->sta.bFlag.bCHFD
  548. || (!pSW->sta.bFlag.bCDWC && pSW->sta.bFlag.bQDFG))
  549. {
  550. pSW->sta.bFlag.bQDFG = false;
  551. }
  552. }
  553. /**************************************************************************
  554. 函数名称: xdl_ar_check
  555. 函数版本:1.00
  556. 函数版本:1.00
  557. 作者: 赵海洋
  558. 创建日期:2021.06.10
  559. 函数功能说明:重合闸检同期
  560. 0:无检定;1:检一侧有压一侧无压;2:检同期;3:检一侧有压一侧无压+检同期
  561. 输入参数:
  562. 输出参数:
  563. 返回值:
  564. ***************************************************************************/
  565. #if 1
  566. static int xdl_ar_check(DWORD mode)
  567. {
  568. // 0:无检定;1:检一侧有压一侧无压;2:检同期;3:检一侧有压一侧无压+检同期
  569. u32 sw = g_protect.sw;
  570. TSETSW *pSet = &pRunSet->tSwSet[sw];
  571. TRELAY_T *pR=&g_tRelay[sw];
  572. DWORD deltaU1=0,deltaU2=0,deltaU3=0;
  573. bool Volt,Volt1,Volt2,Volt3,angle1,bFEQ=false;
  574. bool bY1 ,bY2 , sp , bLock = false;
  575. int ret = 0;
  576. bY1= ((!g_run_stu.pt1yy) && g_run_stu.pt2yy);
  577. bY2= (g_run_stu.pt1yy && (!g_run_stu.pt2yy));
  578. bLock = bY1 || bY2;
  579. switch (mode)
  580. {
  581. case 0:
  582. ret = 1 ;
  583. break;
  584. case 1:
  585. /* 检一侧有压一侧无压 */
  586. ret = bLock;
  587. break;
  588. case 2:
  589. /* 检同期 */
  590. // 分别取出两侧各线电压压差
  591. deltaU1 = _AbsL(g_ui[PUB_AC_UAB1].fz - g_ui[PUB_AC_UAB2].fz);
  592. deltaU2 = _AbsL(g_ui[PUB_AC_UBC1].fz - g_ui[PUB_AC_UBC2].fz);
  593. deltaU3 = _AbsL(g_ui[PUB_AC_UCA1].fz - g_ui[PUB_AC_UCA2].fz);
  594. // 角差与定值比对
  595. angle1 = SynAngle(g_ui[PUB_AC_UAB1].p, g_ui[PUB_AC_UAB2].p,pSet->dTQ_ANGLE_FZ);
  596. // 频差与定值比对
  597. bFEQ = SynFrequency(g_sw_pub.ac_in[PUB_AC_IN_F1],g_sw_pub.ac_in[PUB_AC_IN_F2],pSet->dTQ_FREQ_FZ);
  598. // 压差与定值比对
  599. Volt1 = LowRelay(deltaU1, pSet->dTQ_U_FZ, pSet->dTQ_U_FZ_fh, false);
  600. Volt2 = LowRelay(deltaU2, pSet->dTQ_U_FZ, pSet->dTQ_U_FZ_fh, false);
  601. Volt3 = LowRelay(deltaU3, pSet->dTQ_U_FZ, pSet->dTQ_U_FZ_fh, false);
  602. Volt = Volt1&&Volt2&&Volt3;
  603. sp = Volt&&angle1&&bFEQ&&(!pR->tSWST.uSWST.bFlag.bDIHW);
  604. ret = sp;
  605. break;
  606. case 3:
  607. /* 检一侧有压一侧无压+检同期 */
  608. // 分别取出两侧各线电压压差
  609. deltaU1 = _AbsL(g_ui[PUB_AC_UAB1].fz - g_ui[PUB_AC_UAB2].fz);
  610. deltaU2 = _AbsL(g_ui[PUB_AC_UBC1].fz - g_ui[PUB_AC_UBC2].fz);
  611. deltaU3 = _AbsL(g_ui[PUB_AC_UCA1].fz - g_ui[PUB_AC_UCA2].fz);
  612. // 角差与定值比对
  613. angle1 = SynAngle(g_ui[PUB_AC_UAB1].p, g_ui[PUB_AC_UAB2].p,pSet->dTQ_ANGLE_FZ);
  614. // 频差与定值比对
  615. bFEQ = SynFrequency(g_sw_pub.ac_in[PUB_AC_IN_F1],g_sw_pub.ac_in[PUB_AC_IN_F2],pSet->dTQ_FREQ_FZ);
  616. // 压差与定值比对
  617. Volt1 = LowRelay(deltaU1, pSet->dTQ_U_FZ, pSet->dTQ_U_FZ_fh, false);
  618. Volt2 = LowRelay(deltaU2, pSet->dTQ_U_FZ, pSet->dTQ_U_FZ_fh, false);
  619. Volt3 = LowRelay(deltaU3, pSet->dTQ_U_FZ, pSet->dTQ_U_FZ_fh, false);
  620. Volt = Volt1&&Volt2&&Volt3;
  621. sp = Volt&&angle1&&bFEQ&&(!pR->tSWST.uSWST.bFlag.bDIHW);
  622. if (bLock)
  623. {
  624. ret =1;
  625. }
  626. else if ((!bLock) && sp)
  627. {
  628. ret =1;
  629. }
  630. else
  631. {
  632. ret = 0;
  633. }
  634. // ret = (!bLock) && sp;
  635. break;
  636. default:
  637. ret = 0 ;
  638. break;
  639. }
  640. return ret;
  641. }
  642. #endif
  643. #endif
  644. int xdl_get_chan_cnt(void)
  645. {
  646. return XDL_RCD_A_NUM;
  647. }
  648. char * xdl_get_chan_name(int chan_no)
  649. {
  650. if(chan_no >= XDL_RCD_A_NUM)
  651. {
  652. return NULL;
  653. }
  654. return g_xdl_rcd_a[chan_no];
  655. }
  656. // 小电流录波点值获取函数
  657. s16 xdl_rcd_a(int chn,int idx)
  658. {
  659. int i;
  660. //s16 v;
  661. switch(chn)
  662. {
  663. case 0:
  664. return g_u0_raw[idx];
  665. case 1:
  666. return g_u0[idx];
  667. case 2:
  668. for(i=0;i<XDL_SEG_NUM;i++)
  669. {
  670. if(g_u0_seg[i].dir)
  671. {
  672. #if 1
  673. if(idx>=g_u0_seg[i].bgn && idx <= g_u0_seg[i].end)
  674. {
  675. return g_u0[idx];
  676. }
  677. #else
  678. if(idx == (g_u0_seg[i].end + g_u0_seg[i].bgn)/2)
  679. {
  680. v = g_u0_seg[i].acc/2;
  681. v = v > 32767 ? 32767 : v;
  682. return v*g_u0_seg[i].dir;
  683. }
  684. else if(idx>=g_u0_seg[i].bgn && idx <= g_u0_seg[i].end)
  685. {
  686. v = g_u0_seg[i].acc/4;
  687. //v = g_u0_seg[i].acc/(g_u0_seg[i].end - g_u0_seg[i].bgn +1);
  688. v = v > 32767 ? 32767 : v;
  689. return v*g_u0_seg[i].dir;
  690. }
  691. #endif
  692. }
  693. }
  694. return 0;
  695. case 3:
  696. for(i=0;i<XDL_SEG_NUM;i++)
  697. {
  698. if(g_u0_seg[i].dir && idx == g_u0_seg[i].max_p)
  699. {
  700. return g_u0_seg[i].max_v*g_u0_seg[i].dir;
  701. }
  702. }
  703. return 0;
  704. case 4:
  705. return g_i0_raw[idx];
  706. case 5:
  707. return g_i0[idx];
  708. case 6:
  709. for(i=0;i<XDL_SEG_NUM;i++)
  710. {
  711. if(g_i0_seg[i].dir)
  712. {
  713. #if 1
  714. if(idx>=g_i0_seg[i].bgn && idx <= g_i0_seg[i].end)
  715. {
  716. return g_i0[idx];
  717. }
  718. #else
  719. if(idx == (g_i0_seg[i].end + g_i0_seg[i].bgn)/2)
  720. {
  721. v = g_i0_seg[i].acc/2;
  722. v = v > 32767 ? 32767 : v;
  723. return v*g_i0_seg[i].dir;
  724. }
  725. else if(idx>=g_i0_seg[i].bgn && idx <= g_i0_seg[i].end)
  726. {
  727. v = g_i0_seg[i].acc/4;
  728. // v = g_i0_seg[i].acc/(g_i0_seg[i].end - g_i0_seg[i].bgn +1);
  729. v = v > 32767 ? 32767 : v;
  730. return v*g_i0_seg[i].dir;
  731. }
  732. #endif
  733. }
  734. }
  735. return 0;
  736. case 7:
  737. for(i=0;i<XDL_SEG_NUM;i++)
  738. {
  739. if(g_i0_seg[i].dir && idx == g_i0_seg[i].max_p)
  740. {
  741. return g_i0_seg[i].max_v*g_i0_seg[i].dir;
  742. }
  743. }
  744. return 0;
  745. }
  746. return 0;
  747. }
  748. // 方向包括-1、0、+1,共3个值
  749. // 方向相反:两个数一正一负,不包含0
  750. // 主要用于检测电压、电流是否反向。
  751. static inline int _xdl_dir_inv(int v0,int v1)
  752. {
  753. if((v0>0 && v1<0) || (v0<0 && v1>0))
  754. {
  755. return 1;
  756. }
  757. return 0;
  758. }
  759. // 方向相同:两个数同时为3个方向值中的一个,包含0
  760. // 主要用在判断区域是否完整。
  761. static inline int _xdl_dir_equ(int v0,int v1)
  762. {
  763. if((v0>0 && v1>0) || (v0<0 && v1<0) || (v0==0 && v1==0))
  764. {
  765. return 1;
  766. }
  767. return 0;
  768. }
  769. // 区域更新,保留最大值最大的几个区域
  770. void _xdl_seg_update(struct segment * seg_a,struct segment * seg,struct segment * u_seg, uint *updateNum)
  771. {
  772. int i,j;
  773. int space_num = 0;
  774. // s32 u_end = u_seg->end + 4; //扩大4个点
  775. if(seg->dir == 0)
  776. {
  777. return;
  778. }
  779. //上下两部分:重点->上半部主要处理以“电压段范围内”索引的电流区域
  780. // 下半部为大于“电压段范围内”索引的电压区域,通常不会用到
  781. // 如果u_end < seg->end,当u_seg->bgn == seg->bgn,则判定为稳态触发,需放入上半部
  782. // if((u_end < seg->end) && (u_seg->bgn != seg->bgn))
  783. // space_num = XDL_SEG_NUM>>1;
  784. // else
  785. // space_num = 0;
  786. for(i=space_num;i<XDL_SEG_NUM;i++)
  787. {
  788. if(seg->max_v > seg_a[i].max_v)
  789. {
  790. if( (*updateNum)++ >= XDL_SEG_NUM) /**< 更新满XDL_SEG_NUM后,不再更新 */
  791. {
  792. return;
  793. }
  794. for(j=XDL_SEG_NUM-1;j>i;j--)
  795. {
  796. seg_a[j] = seg_a[j-1];
  797. }
  798. seg_a[i] = *seg;
  799. return;
  800. }
  801. }
  802. return;
  803. }
  804. // 区域排序,根据时间顺序排序
  805. void _xdl_seg_sort(struct segment * seg_a)
  806. {
  807. u32 i,j,index;
  808. struct segment s;
  809. for(i=0;i<XDL_SEG_NUM-1;i++)
  810. {
  811. index = i;
  812. for(j=i+1;j<XDL_SEG_NUM;j++)
  813. {
  814. // 区域存在,且在前面就调整
  815. if(seg_a[j].dir && seg_a[j].bgn < seg_a[index].bgn)
  816. {
  817. index = j;
  818. }
  819. }
  820. if(index != i)
  821. {
  822. s = seg_a[i];
  823. seg_a[i] = seg_a[index];
  824. seg_a[index] = s;
  825. }
  826. }
  827. return;
  828. }
  829. // 检查区域是否完整,完整的区域指区域首尾和区域外的值的方向不同。
  830. int _xdl_seg_is_complete(s16 *dot,struct segment * seg,s32 zero)
  831. {
  832. if(_xdl_dir_equ(dot[seg->bgn -1]-zero,dot[seg->bgn]-zero))
  833. {
  834. return 0;
  835. }
  836. if(_xdl_dir_equ(dot[seg->end]-zero,dot[seg->end+1]-zero))
  837. {
  838. return 0;
  839. }
  840. return 1;
  841. }
  842. // 如果mode为1,扩大区域范围(0算在范围内),电压使用。
  843. int _xdl_seg_search(s16 *dot,u32 bgn,u32 end,struct segment * seg,int mode)
  844. {
  845. s32 v,dir;
  846. u32 i;
  847. memset(seg,0,sizeof(*seg));
  848. if(mode)
  849. {
  850. seg->bgn = bgn;
  851. }
  852. for(i=bgn;i<=end;i++)
  853. {
  854. // 得到值和方向
  855. v = dot[i];
  856. if(v)
  857. {
  858. dir = v > 0 ? 1 : -1;
  859. }
  860. else
  861. {
  862. dir = 0;
  863. }
  864. v = abs(v);
  865. // 初始化区间
  866. if(seg->dir == 0)
  867. {
  868. if(dir)
  869. {
  870. seg->dir = dir;
  871. seg->acc += v;
  872. seg->max_v = v;
  873. if(mode == 0)
  874. {
  875. seg->bgn = i;
  876. }
  877. seg->end = i;
  878. seg->max_p = i;
  879. }
  880. }
  881. // 当前区间处理
  882. else
  883. {
  884. // 反向,当前区间结束
  885. if(mode)
  886. {
  887. if(dir == -seg->dir)
  888. {
  889. return 1;
  890. }
  891. }
  892. else
  893. {
  894. if(dir != seg->dir)
  895. {
  896. return 1;
  897. }
  898. }
  899. // 同相,扩大区间
  900. seg->acc += v;
  901. seg->end = i;
  902. if(v > seg->max_v)
  903. {
  904. seg->max_v = v;
  905. seg->max_p = i;
  906. }
  907. }
  908. }
  909. seg->dir = 0;
  910. return 0;
  911. }
  912. // 微分增速段搜索,微分0在前面
  913. int _xdl_seg_search_ds_f(s16 *dot,u32 u_set,u32 bgn,u32 end,struct segment * seg)
  914. {
  915. s32 v,dir,dir_old;
  916. u32 i;
  917. s32 next_v;
  918. s32 zct = 0, zct_pre = 0, zct_i = 0;
  919. memset(seg,0,sizeof(*seg));
  920. dir = 0;
  921. dir_old = 0;
  922. seg->bgn = bgn;
  923. for(i=bgn;i<=end;i++)
  924. {
  925. // 得到值和方向
  926. dir_old= dir;
  927. v = dot[i]-dot[i-1];
  928. if(v)
  929. {
  930. dir = v > 0 ? 1 : -1;
  931. }
  932. else
  933. {
  934. dir = 0;
  935. }
  936. v = abs(v);
  937. // 初始化区间
  938. if(seg->dir == 0)
  939. {
  940. if(dir)
  941. {
  942. seg->acc = v;
  943. seg->dir = dir;
  944. seg->end = i;
  945. zct_i = i; //by ygl 电压需要逼近突变点,第一个微分不为0的点
  946. }
  947. zct_pre = (dot[i] > 0 ? 1 : -1);
  948. }
  949. // 当前区间处理
  950. else
  951. {
  952. /* 寻找过零点,为了开始平缓转突变的数据*/
  953. zct = (dot[i] > 0 ? 1 : -1);
  954. if(zct != zct_pre){
  955. zct_i = i;
  956. zct_pre = zct;
  957. }
  958. if((dir == -seg->dir)|| ((dir == 0) && (dir_old==0)))
  959. {
  960. if (i < end)
  961. {
  962. next_v = (dot[i+1]-dot[i]) > 0? 1:-1; //ygl 再往下推算一个点的方向
  963. if(dir == -next_v) //如果和当前方向不一致,则判为瞬变
  964. {
  965. dir = next_v; //保存方向 注意此时:next_v == seg->dir
  966. seg->acc += v;
  967. seg->end = i;
  968. continue; //继续搜索下一个点
  969. }
  970. }
  971. // 退一步,退回本区域内
  972. i--;
  973. seg->max_p = i;
  974. seg->max_v = abs(dot[i]);
  975. // 最大值计算忽略末尾微分为0的点
  976. while(i>seg->bgn)
  977. {
  978. if(dot[i] != dot[i-1])
  979. {
  980. break;
  981. }
  982. i--;
  983. }
  984. break;
  985. }
  986. // 同相,扩大区间
  987. seg->acc += v;
  988. seg->end = i;
  989. }
  990. }
  991. if(seg->dir){
  992. if( abs(dot[seg->max_p] - dot[seg->bgn-1]) < u_set)
  993. {
  994. seg->dir = 0;
  995. }
  996. /* 开始点大于定值,则电压差值必须是开始点的4倍以上。否则清零 */
  997. v = abs(dot[seg->bgn-1]);
  998. if(v > u_set && (v*4 > abs(dot[seg->max_p] - dot[seg->bgn-1])))
  999. {
  1000. seg->dir = 0;
  1001. }
  1002. /*如果方向有效,则调整电流开始点为“过零点”或“第一个微分不为0的点”开始*/
  1003. if(zct_i != 0 && seg->dir != 0){
  1004. seg->bgn = zct_i-1;
  1005. // if(zct == zct_pre) //如果没产生过零点
  1006. // seg->bgn -= 1;
  1007. }
  1008. return 1;
  1009. }
  1010. return 0;
  1011. }
  1012. // 微分增速段搜索,微分0在后面
  1013. int _xdl_seg_search_ds_r(s16 *dot,u32 i_set,u32 bgn,u32 end,struct segment * seg,struct segment * u_seg)
  1014. {
  1015. s32 v,dir,dir_old,b_add;
  1016. u32 i;
  1017. s32 next_v = 0;
  1018. s32 U_mean = 0; //对应零压均值
  1019. memset(seg,0,sizeof(*seg));
  1020. u_seg->acc = 0;
  1021. dir = 0;
  1022. dir_old = 0;
  1023. b_add = 0;
  1024. for(i=bgn;i<=end;i++)
  1025. {
  1026. if(u_seg->acc) // 有首次后,累加
  1027. u_seg->acc += g_u0[i-1];
  1028. // 得到值和方向
  1029. dir_old= dir;
  1030. v = dot[i]-dot[i-1];
  1031. if(v)
  1032. {
  1033. dir = v > 0 ? 1 : -1;
  1034. }
  1035. else
  1036. {
  1037. dir = 0;
  1038. }
  1039. v = abs(v);
  1040. // 初始化区间
  1041. if(seg->dir == 0)
  1042. {
  1043. if(dir)
  1044. {
  1045. seg->acc += v;
  1046. u_seg->acc += g_u0[i-1]; //by ygl 记录首零压值
  1047. seg->dir = dir;
  1048. seg->bgn = i;
  1049. seg->end = i;
  1050. }
  1051. }
  1052. // 当前区间处理
  1053. else
  1054. {
  1055. // 反向,当前区间结束
  1056. // if((dir == -seg->dir)|| (dir && (dir_old==0)))
  1057. if((dir == -seg->dir && seg->dir)|| ((dir == 0) && (dir_old==0)))
  1058. {
  1059. if (i < end)
  1060. {
  1061. next_v = (dot[i+1]-dot[i]) > 0? 1:-1; //ygl 再往下推算一个点的方向
  1062. //1、如果启动方向和当前方向一致,则判为瞬变 例:5、3、3、2、3、3、4、、、、
  1063. //2、连续微分为0的点,则判阶梯波形 例:1、2、3、3、3、3、4、、、、
  1064. if(seg->dir == next_v || ((next_v == 0) && (dir == 0)))
  1065. {
  1066. dir = next_v; //保存方向 注意此时:next_v == seg->dir
  1067. seg->acc += v;
  1068. seg->end = i;
  1069. continue; //继续搜索下一个点
  1070. }
  1071. }
  1072. U_mean = (abs(u_seg->acc)-abs(g_u0[i-1]))/(i-seg->bgn); //ygl 根据当前零流段,计算对应区间零压均值
  1073. if(U_mean)
  1074. u_seg->max_v = abs(g_u0[seg->end] - g_u0[seg->bgn])*10/U_mean; //零压区间变化率
  1075. // 退一步,退回本区域内
  1076. i--;
  1077. seg->max_p = i;
  1078. seg->end = i;
  1079. seg->max_v = abs(dot[i] - dot[seg->bgn-1]);
  1080. // seg->max_v = abs(dot[i]); - ygl
  1081. // 最大值计算忽略末尾微分为0的点
  1082. while(i>seg->bgn)
  1083. {
  1084. if(dot[i] != dot[i-1])
  1085. {
  1086. break;
  1087. }
  1088. i--;
  1089. }
  1090. break;
  1091. //return 1;
  1092. }
  1093. // 同相,扩大区间
  1094. seg->acc += v;
  1095. seg->end = i;
  1096. }
  1097. }
  1098. #if 0
  1099. // 如果区域不是以微分0结尾,延长到下一个微分最大值的位置
  1100. // 否则有可能判不到电压方向
  1101. if(dir_old)
  1102. {
  1103. for(i++;i<=end;i++)
  1104. {
  1105. if(_xdl_dir_equ(dir,dot[i] - dot[i-1]) == 0)
  1106. {
  1107. seg->end = i;
  1108. break;
  1109. }
  1110. }
  1111. }
  1112. #endif
  1113. if(seg->dir)
  1114. {
  1115. #if 1
  1116. // 零流首半波条件:
  1117. // 1、开始点小于定值或电流差值是开始点的4倍以上。其中第二点是考虑在定值尽量小的情况下,
  1118. // 如果有大的零序电流出现,其开始点可能大于定值,但其结束点拥有更大的值,有利于提高
  1119. // 判断的准确性。
  1120. // 2、开始点结束点之差大于定值。使用此条件,导致启动后可能找不到首半波,
  1121. // 但如果不使用此条件,仅使用最大值大于定值条件,则可能导致极小电流差值启动判断,
  1122. // 最终导致判断失误。
  1123. v = abs(dot[seg->bgn-1]);
  1124. if(v == 0)
  1125. {
  1126. v = 1;
  1127. }
  1128. // if(v > i_set && (v*4 > abs(dot[seg->max_p] - dot[seg->bgn-1])))
  1129. // {
  1130. // seg->dir = 0;
  1131. // }
  1132. //如果已经电流区域结束点超载了“电压区域结束点”,则要增加差值
  1133. if(u_seg->end < seg->end){
  1134. i_set *= 2;
  1135. // rt_printf("丢弃1:bgn=%d,end=%d,dot=%d, i_set=%d.\r\n",seg->bgn,seg->end,dot[seg->bgn-1],i_set);
  1136. }
  1137. //零压变化率小于30%”,定值尽量小,则要增加差值
  1138. if(u_seg->max_v < 3){
  1139. i_set *= 6;
  1140. // rt_printf("丢弃2:bgn=%d,end=%d,dot=%d, i_set=%d.\r\n",seg->bgn,seg->end,dot[seg->bgn-1],i_set);
  1141. }
  1142. if( abs(dot[seg->max_p] - dot[seg->bgn-1]) < i_set)
  1143. {
  1144. seg->dir = 0;
  1145. // rt_printf("丢弃3:bgn=%d,end=%d,dot=%d, i_set=%d.\r\n",seg->bgn,seg->end,dot[seg->bgn-1],i_set);
  1146. }
  1147. #else
  1148. if(i_set == 0)
  1149. {
  1150. if((abs(dot[seg->bgn-1])*4 > abs(dot[seg->max_p] - dot[seg->bgn-1])))
  1151. {
  1152. seg->dir = 0;
  1153. }
  1154. }
  1155. else
  1156. {
  1157. if(abs(dot[seg->bgn-1]) > i_set)
  1158. {
  1159. // rt_printf("丢弃:bgn=%d,end=%d,dot=%d.\r\n",seg->bgn,seg->end,dot[seg->bgn-1]);
  1160. seg->dir = 0;
  1161. }
  1162. }
  1163. #endif
  1164. return 1;
  1165. }
  1166. return 0;
  1167. }
  1168. // 微分增速段搜索
  1169. int _xdl_seg_search_ds(s16 *dot,u32 bgn,u32 end,struct segment * seg)
  1170. {
  1171. s32 v,dir,dir_old;
  1172. u32 i;
  1173. memset(seg,0,sizeof(*seg));
  1174. dir = 0;
  1175. dir_old = 0;
  1176. for(i=bgn;i<=end;i++)
  1177. {
  1178. // 得到值和方向
  1179. dir_old= dir;
  1180. v = dot[i]-dot[i-1];
  1181. if(v)
  1182. {
  1183. dir = v > 0 ? 1 : -1;
  1184. }
  1185. else
  1186. {
  1187. dir = 0;
  1188. }
  1189. v = abs(v);
  1190. // 初始化区间
  1191. if(seg->dir == 0)
  1192. {
  1193. if(dir)
  1194. {
  1195. seg->acc += v;
  1196. seg->dir = dir;
  1197. seg->bgn = i;
  1198. seg->end = i;
  1199. }
  1200. }
  1201. // 当前区间处理
  1202. else
  1203. {
  1204. // 反向,当前区间结束
  1205. // if(dir != seg->dir)
  1206. if((dir == -seg->dir)|| (dir && (dir_old==0)))
  1207. {
  1208. i--;
  1209. if(_xdl_dir_inv(dot[i],dot[bgn-1]))
  1210. {
  1211. // seg->max_v = abs(dot[i]);
  1212. seg->max_v = abs(dot[i])*10/(i-seg->bgn+1);
  1213. }
  1214. else
  1215. {
  1216. seg->max_v = abs(dot[i] - dot[seg->bgn-1])*10/(i-seg->bgn+1);
  1217. // seg->max_v = abs(dot[i] - dot[seg->bgn-1]);
  1218. }
  1219. seg->max_p = i;
  1220. // rt_printf("v=%d,p=%d.\r\n",seg->max_v,seg->max_p);
  1221. return 1;
  1222. }
  1223. // 同相,扩大区间
  1224. seg->acc += v;
  1225. seg->end = i;
  1226. }
  1227. }
  1228. seg->dir = 0;
  1229. return 0;
  1230. }
  1231. // 处理波形的微分和零轴移动
  1232. int _xdl_proc_diff_zero(s16 *ui0,s16 *ui0_d,int wave_len,u32 set,struct point *max_d)
  1233. {
  1234. int i,v0,v;
  1235. u32 min_d; // 最小微分值
  1236. struct point max_ui;
  1237. // 求导,并得到最大差分值
  1238. min_d = -1;
  1239. max_d->p = 0;
  1240. max_d->v = 0;
  1241. max_ui.p = 0;
  1242. max_ui.v = abs(ui0[0]);
  1243. for(i=1; i<wave_len;i++)
  1244. {
  1245. // 微分求导
  1246. v0 = ui0[i] -ui0[i-1];
  1247. v = abs(v0);
  1248. // 取得最小微分值
  1249. if(v != 0 && v < min_d)
  1250. {
  1251. min_d = v;
  1252. }
  1253. // 计算微分最大值
  1254. if(v > abs(max_d->v))
  1255. {
  1256. max_d->v = v0;
  1257. max_d->p = i;
  1258. }
  1259. // 计算原值最大值
  1260. if(abs(ui0[i]) > abs(max_ui.v))
  1261. {
  1262. max_ui.v = ui0[i];
  1263. max_ui.p = i;
  1264. }
  1265. }
  1266. max_d->v = max_ui.v;
  1267. v = set == 0 ? (5*min_d) : set;
  1268. if(abs(max_ui.v) < v)
  1269. {
  1270. return -2;
  1271. }
  1272. return 0;
  1273. }
  1274. // 三点线性平滑
  1275. void xdl_f_smooth3 ( short in[], float out[], int N )
  1276. {
  1277. int i;
  1278. if ( N < 3 )
  1279. {
  1280. for ( i = 0; i <= N - 1; i++ )
  1281. {
  1282. out[i] = in[i];
  1283. }
  1284. }
  1285. else
  1286. {
  1287. out[0] = ( 5 * in[0] + 2 * in[1] - in[2]) / 6;
  1288. for ( i = 1; i <= N - 2; i++ )
  1289. {
  1290. out[i] = ( in[i - 1] + in[i] + in[i + 1]) / 3;
  1291. }
  1292. out[N - 1] = ( 5 * in[N - 1] + 2 * in[N - 2] - in[N - 3]) / 6;
  1293. }
  1294. }
  1295. // 三点线性平滑
  1296. void xdl_smooth3 ( short in[], short out[], int N )
  1297. {
  1298. int i;
  1299. if ( N < 3 )
  1300. {
  1301. for ( i = 0; i <= N - 1; i++ )
  1302. {
  1303. out[i] = in[i];
  1304. }
  1305. }
  1306. else
  1307. {
  1308. out[0] = ( 5 * in[0] + 2 * in[1] - in[2]) / 6;
  1309. for ( i = 1; i <= N - 2; i++ )
  1310. {
  1311. out[i] = ( in[i - 1] + in[i] + in[i + 1]) / 3;
  1312. }
  1313. out[N - 1] = ( 5 * in[N - 1] + 2 * in[N - 2] - in[N - 3]) / 6;
  1314. }
  1315. }
  1316. //五点线性平滑
  1317. void xdl_smooth5 ( short in[], short out[], int N )
  1318. {
  1319. int i;
  1320. if ( N < 5 )
  1321. {
  1322. for ( i = 0; i <= N - 1; i++ )
  1323. {
  1324. out[i] = in[i];
  1325. }
  1326. }
  1327. else
  1328. {
  1329. out[0] = ( 3 * in[0] + 2 * in[1] + in[2] - in[4] +3) / 5;
  1330. out[1] = ( 4 * in[0] + 3 * in[1] + 2 * in[2] + in[3] +5) / 10;
  1331. for ( i = 2; i <= N - 3; i++ )
  1332. {
  1333. out[i] = ( in[i - 2] + in[i - 1] + in[i] + in[i + 1] + in[i + 2] +3) / 5;
  1334. }
  1335. out[N - 2] = ( 4 * in[N - 1] + 3 * in[N - 2] + 2 * in[N - 3] + in[N - 4] +5) / 10;
  1336. out[N - 1] = ( 3 * in[N - 1] + 2 * in[N - 2] + in[N - 3] - in[N - 5] +3) / 5;
  1337. }
  1338. }
  1339. // 零序电压启动后,调用此函数查找故障线路
  1340. // 采用暂态零序电流方向法:
  1341. // 1、零序电压求导。
  1342. // 2、暂态零序电流与零序电压导数比较极性。
  1343. // 3、极性相反的选定为故障线路。
  1344. // 4、此判据只在首半波内有效。
  1345. //
  1346. int xdl_proc_u0(int len,u32 u_set)
  1347. {
  1348. int bgn,ret;
  1349. struct segment seg;
  1350. struct point max_d;
  1351. // memset(g_u0_raw,0,sizeof(g_u0_raw));
  1352. memset(g_u0_seg,0,sizeof(g_u0_seg));
  1353. // 零序电压求导
  1354. ret = _xdl_proc_diff_zero(g_u0_raw,0,len,0,&max_d);
  1355. if(ret < 0)
  1356. {
  1357. return ret;
  1358. }
  1359. // 搜索几个电压变化最大的区间
  1360. bgn = 1;
  1361. // while(_xdl_seg_search(g_u0_raw,bgn,len-1,&seg,1))
  1362. while(_xdl_seg_search_ds_f(g_u0,u_set,bgn,len-1,&seg))
  1363. {
  1364. _xdl_seg_update(g_u0_seg,&seg,NULL, NULL);
  1365. #if 1
  1366. bgn = seg.end + 1;
  1367. #else
  1368. // 得到下一个区域的开始点,和上一个区域的零结束点重合
  1369. for(bgn = seg.end;bgn>0;bgn--)
  1370. {
  1371. if(g_u0_raw[bgn] != 0)
  1372. {
  1373. break;
  1374. }
  1375. }
  1376. bgn++;
  1377. #endif
  1378. }
  1379. // 将搜索出的区域按时间排序
  1380. _xdl_seg_sort(g_u0_seg);
  1381. return 0;
  1382. }
  1383. #define seg_extend_dot 8
  1384. int xdl_seg_u0_ok(int len,struct segment * seg, u32 u_set)
  1385. {
  1386. u32 bgn,end,i;
  1387. s16 v_bgn;
  1388. s16 max_v = 0;
  1389. s16 limit_end = seg->end + seg_extend_dot; //扩展首半波至少范围,
  1390. // 扩展首半波范围,电压延后16个点,45度。
  1391. bgn = seg->bgn > 0 ? (seg->bgn -1) : seg->bgn;
  1392. end = seg->end + (seg->end - seg->bgn); //seg_extend_dot -> (seg->end - seg->bgn)
  1393. end = end > limit_end ? end : limit_end;
  1394. if(end >= len)
  1395. {
  1396. end = len;
  1397. }
  1398. // 检查电压幅值,在此区域后,必须有电压大于设定值
  1399. // v_bgn = g_u0[bgn];
  1400. max_v = v_bgn = g_u0[bgn];
  1401. // 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);
  1402. for(i=bgn;i<end;i++)
  1403. {
  1404. if(max_v < g_u0[i])
  1405. max_v = g_u0[i];
  1406. if(abs(g_u0[i] - v_bgn) > u_set)
  1407. {
  1408. // 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);
  1409. return 1;
  1410. }
  1411. }
  1412. return 0;
  1413. }
  1414. int xdl_seg_i0_ok(int len,struct segment * seg, u32 i_set)
  1415. {
  1416. u32 bgn,end,i;
  1417. s16 v_bgn;
  1418. // 扩展首半波范围,电流延后16个点,45度。
  1419. bgn = seg->bgn > 0 ? (seg->bgn -1) : seg->bgn;
  1420. end = seg->end + 8;
  1421. if(end >= len)
  1422. {
  1423. end = len;
  1424. }
  1425. // 检查电压幅值,在此区域后,必须有电压大于设定值
  1426. v_bgn = g_i0[bgn];
  1427. for(i=bgn;i<end;i++)
  1428. {
  1429. if(abs(g_i0[i] - v_bgn) > i_set)
  1430. {
  1431. return 1;
  1432. }
  1433. }
  1434. return 0;
  1435. }
  1436. #define U_SET_COEF 1
  1437. int xdl_proc_i0(uint32_t sw,int len,u32 i_set,u32 u_set,int *ps,int h_sign)
  1438. {
  1439. int i,s,bgn=1,end,ret,dir=0;
  1440. int j =0, hilbert_count =0;
  1441. u32 updateNum =0, zero_set;
  1442. s32 zero_max;
  1443. struct point di_max;
  1444. struct segment seg,u_seg;
  1445. // 全局电流数据清零
  1446. // memset(g_i0_raw,0,sizeof(g_i0_raw));
  1447. memset(g_i0_seg,0,sizeof(g_i0_seg));
  1448. // 零序电流求导,并得到最大差分值
  1449. if(g_i0_arg[sw].scale_is_1A)
  1450. {
  1451. // 零流采样电阻51Ω更换为了300Ω,经过系数转换为码值后,值变大了6倍左右,直接用i_set判断
  1452. zero_set = i_set;
  1453. }
  1454. else
  1455. {
  1456. //没换电阻还是按照原本方式处理
  1457. if (h_sign > 0) //H变换计算成功后,需要判断全段有流值大于4倍定值,防止无流误判
  1458. {
  1459. zero_set = i_set*4;
  1460. }
  1461. else
  1462. {
  1463. zero_set = i_set*2;
  1464. }
  1465. if(zero_set < 18) /**< zero_set判值不能小于18,保证小电流阀值不能过低 */
  1466. zero_set = 18;
  1467. }
  1468. zero_max = _xdl_proc_diff_zero(g_i0,0,len,zero_set,&di_max);
  1469. if(zero_max < 0)
  1470. {
  1471. rt_printf("第%d行, %s无流(zero_max=%d) zero_set=%d.\r\n",__LINE__, g_line_type[0],zero_max,zero_set);
  1472. return -1;
  1473. }
  1474. // 零压触发时,搜索第一个电压突变的区间,精确捕捉过零点
  1475. while(_xdl_seg_search_ds_f(g_u0,u_set,bgn,len-1,&u_seg))
  1476. {
  1477. if(u_seg.dir)
  1478. break;
  1479. bgn = u_seg.end + 1;
  1480. }
  1481. if(pRunSet->bTT_TBLTYPE)
  1482. {
  1483. u_seg.bgn -= 1 ; //电压突变沿作为电流首半波始启点,需再回退一个采样点。
  1484. bgn = u_seg.bgn;
  1485. }
  1486. else
  1487. bgn = u_seg.bgn;
  1488. // 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);
  1489. // 方法2: 电流微分首半波判断
  1490. // 搜索几个电流变化最大的区间
  1491. while(_xdl_seg_search_ds_r(g_i0,i_set,bgn,len-1,&seg,&u_seg))
  1492. {
  1493. bgn = seg.end+1;
  1494. // 区间有效,且区间电压有效才更新
  1495. // 零压定值过大会导致暂态零压设定
  1496. // 值过高进而导致索引零压失败最终
  1497. // 误判或者无法启动小电流接地判断
  1498. if(seg.dir && xdl_seg_u0_ok(len,&seg,u_set))
  1499. {
  1500. // 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);
  1501. _xdl_seg_update(g_i0_seg,&seg,&u_seg,&updateNum);
  1502. }
  1503. }
  1504. // 将搜索出的区域按时间排序
  1505. _xdl_seg_sort(g_i0_seg) ;
  1506. // 寻找电流首半波,条件是最大值最大的区域,是其前面区域最大值的8倍以上
  1507. s = 0;
  1508. if(pRunSet->bTT_GZJD != 1) //选高阻接地方式,不考虑最大区域选择
  1509. {
  1510. for(i=1; i<(XDL_SEG_NUM>>1);i++) //XDL_SEG_NUM ->暂时只使用前5组区域
  1511. {
  1512. if(g_i0_seg[i].max_v > (g_i0_seg[s].max_v <<3))
  1513. {
  1514. s = i;
  1515. }
  1516. }
  1517. }
  1518. // 如果首半波条件成立,寻找零序电压方向。
  1519. if(g_i0_seg[s].dir)
  1520. {
  1521. // 扩展首半波范围
  1522. if(g_i0_seg[s].bgn > 0)
  1523. {
  1524. g_i0_seg[s].bgn--;
  1525. }
  1526. // 检查电压幅值,在此区域后,必须有电压大于设定值
  1527. {
  1528. s16 v,v_bgn,v_max;
  1529. // 延长电压检查区间 ,延长为180度+额外增加8个点
  1530. end = g_i0_seg[s].bgn + ADC_REC_SAMPLE/2+8;
  1531. //极端情况下会进这里,例如零流:为长而平衡的小波,造成区段划分过长,长度超越了半个周波(180度)
  1532. if(g_i0_seg[s].end > end)
  1533. {
  1534. end = g_i0_seg[s].end + 16;
  1535. }
  1536. if(end > len)
  1537. {
  1538. end = len;
  1539. }
  1540. v_max = 0;
  1541. v_bgn = g_u0[g_i0_seg[s].bgn];
  1542. for(i=g_i0_seg[s].bgn;i<=end;i++)
  1543. {
  1544. v = g_u0[i] - v_bgn;
  1545. if(abs(v) > abs(v_max))
  1546. {
  1547. v_max = v;
  1548. }
  1549. if (i == g_i0_seg[s].end) //区间内搜索,采用区域中的最大值计算
  1550. {
  1551. // 区域电压差值必须大于定值
  1552. if(abs(v_max) > u_set*U_SET_COEF)
  1553. {
  1554. // dir = v;
  1555. // 如果电流区间起点的电压的绝对值减终点的电压的绝对值大于定值,反向
  1556. // if(abs(g_u0[g_i0_seg[s].bgn]) < abs(g_u0[i])) // 此算法跨零点时容易失误
  1557. if(abs(g_u0[g_i0_seg[s].bgn]) < (abs(g_u0[g_i0_seg[s].end])+u_set))
  1558. {
  1559. dir = g_u0[g_i0_seg[s].end]; //使用区域结束点,判趋势
  1560. rt_printf("电压正向v_max:bgn=%d,end=%d,u_set=%d,v_bgn=%d,v_end=%d,dir=%d.\r\n",
  1561. 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);
  1562. }
  1563. else
  1564. {
  1565. dir = -g_u0[g_i0_seg[s].end];
  1566. rt_printf("电压反向v_max:bgn=%d,end=%d,u_set=%d,v_bgn=%d,v_end=%d,dir =%d.\r\n",
  1567. 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);
  1568. }
  1569. break;
  1570. }
  1571. }
  1572. else if(i > g_i0_seg[s].end) //延长区间搜索
  1573. {
  1574. // 区域电压差值必须大于定值
  1575. if(abs(v) > u_set*U_SET_COEF)
  1576. {
  1577. dir = v;
  1578. // 如果电流区间起点的电压的绝对值减终点的电压的绝对值大于定值,反向
  1579. // if(abs(g_u0[g_i0_seg[s].bgn]) < abs(g_u0[i])) // 此算法跨零点时容易失误
  1580. if(abs(g_u0[g_i0_seg[s].bgn]) < (abs(g_u0[g_i0_seg[s].end])+u_set))
  1581. {
  1582. dir = v;
  1583. }
  1584. else
  1585. {
  1586. dir = -v;
  1587. rt_printf("电压反向v:bgn=%d,end=%d,u_set=%d,v_bgn=%d,v_end=%d,dir =%d.\r\n",
  1588. 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);
  1589. }
  1590. break;
  1591. }
  1592. }
  1593. }
  1594. if(i == end)
  1595. {
  1596. 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);
  1597. *ps = s;
  1598. ret = -2;
  1599. goto RET;
  1600. }
  1601. else if(i > g_i0_seg[s].end)
  1602. {
  1603. rt_printf("电压查找超范围:bgn=%d,end=%d,uv0=%d,uvd=%d,uv=%d,up=%d,u_set=%d.\r\n",
  1604. 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);
  1605. }
  1606. }
  1607. // 判电压方向
  1608. if(dir)
  1609. {
  1610. ret=_xdl_dir_inv(dir,g_i0_seg[s].dir);
  1611. if(ret == 0 && pRunSet->bTT_HILEBERT) //H 变换
  1612. {
  1613. for(j = (g_i0_seg[s].bgn&0x7f); j < XDL_SAMPLE_LEN ; j+=ADC_REC_SAMPLE )
  1614. {
  1615. hilbert_count++;
  1616. ret = (hilbert_filter(&g_u0_dwt[j], &g_i0_dwt[j], ADC_REC_SAMPLE)>0)? 1:0;
  1617. if(ret)
  1618. {
  1619. rt_printf("hilbert activation,begin at %d,计算:%d 次 \r\n",(g_i0_seg[s].bgn&0x7f),hilbert_count);
  1620. break;
  1621. }
  1622. }
  1623. if(!ret)
  1624. {
  1625. rt_printf("hilbert not find,begin at %d,计算:%d 次 \r\n",(g_i0_seg[s].bgn&0x7f),hilbert_count);
  1626. }
  1627. }
  1628. 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",
  1629. 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);
  1630. *ps = s;
  1631. return ret;
  1632. }
  1633. else
  1634. {
  1635. rt_printf("电压无方向:s=%d,bgn=%d,end=%d,i=%d,u0=%d.\r\n",
  1636. s,g_i0_seg[s].bgn,g_i0_seg[s].end,i,g_u0[i]);
  1637. *ps = s;
  1638. ret = -3;
  1639. goto RET;
  1640. }
  1641. }
  1642. else
  1643. {
  1644. if(h_sign > 0 && pRunSet->bTT_HILEBERT) //H变换,缩小区间再判一次
  1645. {
  1646. for(j = (ADC_REC_SAMPLE<<0) ; j < XDL_SAMPLE_LEN ; j+=ADC_REC_SAMPLE )
  1647. {
  1648. hilbert_count++;
  1649. ret = (hilbert_filter(&g_u0_dwt[j], &g_i0_dwt[j], ADC_REC_SAMPLE)>0)? 1:0;
  1650. if(ret)
  1651. {
  1652. rt_printf("%s hilbert activation,计算:%d 次 \r\n",g_line_type[ret],hilbert_count);
  1653. return ret;
  1654. }
  1655. }
  1656. if(!ret)
  1657. {
  1658. rt_printf("%s hilbert not find,计算:%d 次 \r\n",g_line_type[ret],hilbert_count);
  1659. }
  1660. }
  1661. rt_printf("首半波未找到:s=%d,dir=%d,max=%d.\r\n",s,g_i0_seg[s].dir,g_i0_seg[s].max_v);
  1662. *ps = s;
  1663. ret = -4;
  1664. }
  1665. RET:
  1666. return ret;
  1667. }
  1668. #define COEF 1.0
  1669. int xdl_proc_adc(u32 sw,u32 dt,struct timespec *ts)
  1670. {
  1671. int i,ret,seg;
  1672. int ch0,cha,chb,chc;
  1673. int i0,u0;
  1674. char Hilbert_Sign = 0; //H变换计算成功标志
  1675. float H_ret = 0; //H变换计算返回值
  1676. // 三项电流
  1677. int ui_begin;
  1678. DWORD Ia,Ib,Ic;
  1679. wave_object obj=NULL;
  1680. wt_object wt=NULL;
  1681. char *method = "modwt";
  1682. if(pRunSet->bTT_FILTER == 2)
  1683. {
  1684. // char *name = "haar";
  1685. obj = wave_init(pRunSet->bTT_WAVE);// Initialize the wavelet
  1686. wt = wt_init(obj, method, XDL_SAMPLE_LEN, pRunSet->bTT_WAVE_ORDER);// Initialize the wavelet transform object
  1687. }
  1688. // 得到零序电压采样值
  1689. if(pRunSet->tSwSet[sw].bTT_Power_v2 == 0)
  1690. {
  1691. ch0 = (short)g_sw_pub.ac_cfg_index[PUB_AC_U01];
  1692. cha = (short)g_sw_pub.ac_cfg_index[PUB_AC_UA1];
  1693. chb = (short)g_sw_pub.ac_cfg_index[PUB_AC_UB1];
  1694. chc = (short)g_sw_pub.ac_cfg_index[PUB_AC_UC1];
  1695. }
  1696. else
  1697. {
  1698. ch0 = (short)g_sw_pub.ac_cfg_index[PUB_AC_U02];
  1699. cha = (short)g_sw_pub.ac_cfg_index[PUB_AC_UA2];
  1700. chb = (short)g_sw_pub.ac_cfg_index[PUB_AC_UB2];
  1701. chc = (short)g_sw_pub.ac_cfg_index[PUB_AC_UC2];
  1702. }
  1703. if(ch0 != INDEX_INVALLID)
  1704. {
  1705. ch0=equ_get_ac_channel(g_equ_config_ac[ch0].slot, g_equ_config_ac[ch0].index);
  1706. for(i=0; i<XDL_SAMPLE_LEN;i++)
  1707. {
  1708. g_u0_raw[i] = g_adc_dots_rec[((dt+i)&ADC_REC_DOTS_MASK)][ch0];
  1709. g_u0_dwt[i] = (float)g_u0_raw[i];
  1710. }
  1711. if(pRunSet->bTT_GZJD != 1) //选高阻接地方式,则小波计算不考虑:三点滤波算法
  1712. {
  1713. memset(g_u0_dwt,0,sizeof(g_u0_dwt));
  1714. xdl_f_smooth3(g_u0_raw, g_u0_dwt, XDL_SAMPLE_LEN);
  1715. }
  1716. switch (pRunSet->bTT_FILTER)
  1717. {
  1718. case 0:
  1719. xdl_smooth3(g_u0_raw, g_u0, XDL_SAMPLE_LEN);
  1720. break;
  1721. case 1:
  1722. DWT_Matrix_Transform(g_u0_dwt, g_u0, XDL_SAMPLE_LEN);
  1723. break;
  1724. case 2:
  1725. modwt(wt, g_u0_dwt);// Perform MODWT
  1726. for(i=0; i<XDL_SAMPLE_LEN;i++)
  1727. {
  1728. g_u0[i] = (s16)(wt->output[i]*COEF);
  1729. }
  1730. break;
  1731. default:
  1732. memcpy(g_u0, g_u0_raw, XDL_SAMPLE_LEN);
  1733. break;
  1734. }
  1735. }
  1736. else if( (cha != INDEX_INVALLID)
  1737. && (chb != INDEX_INVALLID)
  1738. && (chc != INDEX_INVALLID))
  1739. {
  1740. cha=equ_get_ac_channel(g_equ_config_ac[cha].slot, g_equ_config_ac[cha].index);
  1741. chb=equ_get_ac_channel(g_equ_config_ac[chb].slot, g_equ_config_ac[chb].index);
  1742. chc=equ_get_ac_channel(g_equ_config_ac[chc].slot, g_equ_config_ac[chc].index);
  1743. for(i=0; i<XDL_SAMPLE_LEN;i++)
  1744. {
  1745. g_u0[i] = g_adc_dots_rec[((dt+i)&ADC_REC_DOTS_MASK)][cha];
  1746. g_u0[i] += g_adc_dots_rec[((dt+i)&ADC_REC_DOTS_MASK)][chb];
  1747. g_u0[i] += g_adc_dots_rec[((dt+i)&ADC_REC_DOTS_MASK)][chc];
  1748. }
  1749. }
  1750. else
  1751. {
  1752. ret = -11;
  1753. goto RET;
  1754. }
  1755. if(sw < g_sw_num)
  1756. {
  1757. TSETSW *pSet = &pRunSet->tSwSet[sw];
  1758. TSDHZ_T *pSW=&g_tRelay[sw].tSDHZ;
  1759. int zero;
  1760. if((BH_ALL_EN(sw) && (pSet->bTT_xdlgj || pSet->bTT_xdltz)) == 0)
  1761. {
  1762. ret = -12;
  1763. goto RET;
  1764. }
  1765. zero = 0;
  1766. if(!pRunSet->bTT_TBLTYPE) //使用电流突变
  1767. {
  1768. if(g_xdl[sw].zero_cnt)
  1769. {
  1770. zero = g_xdl[sw].zero_sum/g_xdl[sw].zero_cnt;
  1771. }
  1772. }
  1773. // 得到零序电流采样值
  1774. ch0 = (short)g_sw[sw].ac_cfg_index[SW_AC_I0];
  1775. if(ch0 != INDEX_INVALLID)
  1776. {
  1777. ch0=equ_get_ac_channel(g_equ_config_ac[ch0].slot, g_equ_config_ac[ch0].index);
  1778. for(i=0; i<XDL_SAMPLE_LEN;i++)
  1779. {
  1780. g_i0_raw[i] = g_adc_dots_rec[((dt+i)&ADC_REC_DOTS_MASK)][ch0] - zero;
  1781. g_i0_dwt[i] = (float)g_i0_raw[i];
  1782. }
  1783. if(pRunSet->bTT_GZJD != 1) //选高阻接地方式,则小波计算不考虑:三点滤波算法
  1784. {
  1785. memset(g_i0_dwt,0,sizeof(g_i0_dwt));
  1786. xdl_f_smooth3(g_i0_raw, g_i0_dwt, XDL_SAMPLE_LEN);
  1787. }
  1788. switch (pRunSet->bTT_FILTER)
  1789. {
  1790. case 0:
  1791. xdl_smooth3(g_i0_raw, g_i0, XDL_SAMPLE_LEN);
  1792. break;
  1793. case 1:
  1794. DWT_Matrix_Transform(g_i0_dwt, g_i0, XDL_SAMPLE_LEN);
  1795. break;
  1796. case 2:
  1797. modwt(wt, g_i0_dwt);// Perform MODWT
  1798. for(i=0; i<XDL_SAMPLE_LEN;i++)
  1799. {
  1800. g_i0[i] = (s16)(wt->output[i]*COEF);
  1801. }
  1802. break;
  1803. default:
  1804. memcpy(g_i0, g_i0_raw, XDL_SAMPLE_LEN);
  1805. break;
  1806. }
  1807. }
  1808. else if( (cha != INDEX_INVALLID)
  1809. && (chb != INDEX_INVALLID)
  1810. && (chc != INDEX_INVALLID))
  1811. {
  1812. cha=equ_get_ac_channel(g_equ_config_ac[cha].slot, g_equ_config_ac[cha].index);
  1813. chb=equ_get_ac_channel(g_equ_config_ac[chb].slot, g_equ_config_ac[chb].index);
  1814. chc=equ_get_ac_channel(g_equ_config_ac[chc].slot, g_equ_config_ac[chc].index);
  1815. for(i=0; i<XDL_SAMPLE_LEN;i++)
  1816. {
  1817. g_i0[i] = g_adc_dots_rec[((dt+i)&ADC_REC_DOTS_MASK)][cha];
  1818. g_i0[i] += g_adc_dots_rec[((dt+i)&ADC_REC_DOTS_MASK)][chb];
  1819. g_i0[i] += g_adc_dots_rec[((dt+i)&ADC_REC_DOTS_MASK)][chc];
  1820. }
  1821. }
  1822. else
  1823. {
  1824. ret = -13;
  1825. goto RET;
  1826. }
  1827. // rt_free(p_uo);
  1828. // rt_free(p_io);
  1829. wave_free(obj);
  1830. wt_free(wt);
  1831. // 检查处理是否及时
  1832. // 启动(1.28-0.3-0.02×6)秒内必须完成小电流数据copy,否则录波缓冲中的数据将被覆盖。
  1833. {
  1834. u32 n;
  1835. n = (u32)(g_adc_dots_count<<3) - dt;
  1836. if(n > ADC_REC_DOTS_CHANNEL)
  1837. {
  1838. rt_printf("处理超时:n=%d,now=%d,old=%d.\r\n",n,(g_adc_dots_count<<3),dt);
  1839. ret = -14;
  1840. goto RET;
  1841. }
  1842. }
  1843. // 零序电流处理
  1844. seg = -1;
  1845. if(pRunSet->bTT_TBLTYPE) //选零压突变,使用H变换计算以增加准确率
  1846. {
  1847. for(i = 0 ; i < XDL_SAMPLE_LEN ; i+=ADC_REC_SAMPLE ) //(ADC_REC_SAMPLE<<1)
  1848. {
  1849. H_ret = hilbert_filter(&g_u0_dwt[i], &g_i0_dwt[i], ADC_REC_SAMPLE);
  1850. if(H_ret > 0)
  1851. {
  1852. Hilbert_Sign++;
  1853. // break;
  1854. }
  1855. // rt_printf("i= %d,status =%d,hilbert_filter ret = %f.\r\n",i,Hilbert_Sign,H_ret);
  1856. }
  1857. 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);
  1858. }
  1859. else // 零序电流处理
  1860. 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);
  1861. //如果电流反向,结果反向。
  1862. if((pRunSet->tSwSet[sw].bTT_Current_Inv || pRunSet->tSwSet[sw].bTT_lxpolar || g_cur_direction_adaptaion) && (ret >= 0))
  1863. {
  1864. ret = !ret;
  1865. }
  1866. // 计算I0、U0
  1867. i0 = 0;
  1868. u0 = 0;
  1869. if(seg >= 0)
  1870. {
  1871. for(i=g_i0_seg[seg].bgn; i<g_i0_seg[seg].end; i++)
  1872. {
  1873. if(abs(g_i0[i]) > abs(i0))
  1874. {
  1875. i0 = g_i0[i];
  1876. }
  1877. if(abs(g_u0[i]) > abs(u0))
  1878. {
  1879. u0 = g_u0[i];
  1880. }
  1881. }
  1882. i0 = i0*(g_ui[UI_SW_INDEX(sw,SW_AC_I0)].e_factor0*1.4142)*Q16_BASE;
  1883. u0 = u0*(g_ui[PUB_AC_U01].e_factor0*1.4142)*Q16_BASE;
  1884. }
  1885. // 计算三项电流
  1886. ui_begin = UI_SW_INDEX_BEGIN(sw);
  1887. 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);
  1888. 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);
  1889. 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);
  1890. // 启动事件
  1891. soe_record_ev(EV_LX_XDL_QD+sw*EV_SW_NUM, 1, i0,u0,ret);
  1892. if(ret>0) //小电流接地:启动[界内动作]
  1893. {
  1894. g_xdl[sw].dz = 1;
  1895. // 提前打开启动继电器,以节省硬件耗用时间
  1896. // 并等待超时释放
  1897. sw_do(sw,SW_DO_BHT,SW_DO_TYPE_SELECT_ON);
  1898. if(pSet->dT_xdl_T) //暂态时间不为0时,才启动间歇性接地逻辑
  1899. {
  1900. // jxjd_set_JDGZ(sw); //间歇接地故障(用于计次) // TODO
  1901. }
  1902. if (!pSet->dT_xdl_GT) // 告警
  1903. {
  1904. // 告警
  1905. if(pSet->bTT_xdlgj)
  1906. {
  1907. soe_record_ev(EV_LX_XDL_GJ+sw*EV_SW_NUM, 1, i0,u0,0 );
  1908. soe_record_ev(EV_ABC_JDTZ+sw*EV_SW_NUM, 1, i0,u0,0 );
  1909. pSW->uSdhz_S.bFlag.bJD = true;
  1910. }
  1911. }
  1912. else
  1913. {
  1914. pSW->uSdhz_S.bFlag.bJD = true;
  1915. g_xdl[sw].gj_us0 = g_xdl[sw].qd_us0;
  1916. g_xdl[sw].dz_i0[0] = i0;
  1917. g_xdl[sw].dz_u0[0] = u0;
  1918. g_xdl[sw].dz_Ia[0] = Ia;
  1919. g_xdl[sw].dz_Ib[0] = Ib;
  1920. g_xdl[sw].dz_Ic[0] = Ic;
  1921. }
  1922. if (!pSet->dT_xdl_T) // 跳闸
  1923. {
  1924. // 跳闸
  1925. if(pSet->bTT_xdltz)
  1926. {
  1927. sw_do(sw,SW_DO_BHT,SW_DO_TYPE_ON);
  1928. soe_record_ev(EV_LX_XDL_TZ+sw*EV_SW_NUM, 1, i0,u0,0 );
  1929. soe_record_ev(EV_ABC_JDTZ+sw*EV_SW_NUM, 1, i0,u0,0 );
  1930. if ((Ia>Ib)&&(Ia>Ic))
  1931. {
  1932. soe_record_ev(EV_A_JDTZ+sw*EV_SW_NUM, 1, Ia,0,0 );
  1933. }
  1934. else if ((Ib>Ia)&&(Ib>Ic))
  1935. {
  1936. soe_record_ev(EV_B_JDTZ+sw*EV_SW_NUM, 1, Ib,0,0 );
  1937. }
  1938. else if ((Ic>Ia)&&(Ic>Ib))
  1939. {
  1940. soe_record_ev(EV_C_JDTZ+sw*EV_SW_NUM, 1, Ic,0,0 );
  1941. }
  1942. pSW->uSdhz_S.bFlag.bJD = true;
  1943. // g_tRelay[sw].tXDLCHZ.sta.bFlag.bXDLDZ = true; //TODO EWen
  1944. //jxjd_sw_reset(sw);
  1945. }
  1946. }
  1947. else
  1948. {
  1949. pSW->uSdhz_S.bFlag.bJD = true;
  1950. g_xdl[sw].dz_us0 = g_xdl[sw].qd_us0;
  1951. g_xdl[sw].dz_i0[1] = i0;
  1952. g_xdl[sw].dz_u0[1] = u0;
  1953. g_xdl[sw].dz_Ia[1] = Ia;
  1954. g_xdl[sw].dz_Ib[1] = Ib;
  1955. g_xdl[sw].dz_Ic[1] = Ic;
  1956. }
  1957. }
  1958. rcd_start_xdl(NULL,sw,g_xdl_rcd_a,XDL_RCD_A_NUM,XDL_SAMPLE_LEN,ts);
  1959. g_xdl[sw].st = XDL_ST_END;
  1960. ret = 0;
  1961. }
  1962. else
  1963. {
  1964. ret = -15;
  1965. }
  1966. RET:
  1967. if(ret != 0)
  1968. {
  1969. soe_record_ev(EV_LX_XDL_QD+sw*EV_SW_NUM, 1, 0,0,ret);
  1970. }
  1971. 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);
  1972. return ret;
  1973. }
  1974. // 零流突变判断定值
  1975. #define REV_F_CT_DEFAULT 20 // CT默认,额定5A
  1976. #define REV_F_CT_1A 10 // CT_1V/1A, 额定1A
  1977. // 小电流突变量启动,放在156us中断中
  1978. void xdl_tbl_qd(int mod)
  1979. {
  1980. s16 v,v0,v1,zero;
  1981. s16 iv,iv0,iv1,ref_i,ref_v=0; //零压触发,电流参考辅助触发
  1982. u32 sw,ui;
  1983. u32 xdl_zt_v;
  1984. int ret;
  1985. TSETSW *pSet = NULL;
  1986. // u32 index = g_adc_dots_index_rec - 1;
  1987. u32 index = ((g_adc_dots_count-1)<<3) + mod;
  1988. for (sw=0; sw<g_sw_num; sw++)
  1989. {
  1990. // 已启动,退出
  1991. if(g_xdl[sw].st)
  1992. {
  1993. continue;
  1994. }
  1995. pSet = &pRunSet->tSwSet[sw];
  1996. //突变量启动前提条件:保护总投入,小电流接地告警或出口投入或录波投入。
  1997. if(BH_ALL_EN(sw) == 0 || (pSet->bTT_xdlgj || pSet->bTT_xdltz)== 0)
  1998. {
  1999. continue;
  2000. }
  2001. // 如果索引通道不存在,不需计算
  2002. if(pRunSet->bTT_TBLTYPE) //高阻接地方式,零序电流较小,可以采用电压突变启动
  2003. {
  2004. ui = PUB_AC_U01;
  2005. xdl_zt_v = pRunSet->dU_xdl_u0_zt[0];
  2006. }
  2007. else
  2008. {
  2009. ui = UI_SW_INDEX(sw,SW_AC_I0);
  2010. xdl_zt_v = pRunSet->tSwSet[sw].dI_xdl_zt;
  2011. }
  2012. if(g_ui[ui].chn_index == CFG_ADC_CHANNEL_ZERO)
  2013. {
  2014. continue;
  2015. }
  2016. // 检查硬件通道是否正常
  2017. if(equ_ac_channel_is_ok(g_ui[ui].chn_index) == 0)
  2018. {
  2019. continue;
  2020. }
  2021. // 得到当前值和一个周期前的值
  2022. v0 = g_adc_dots_rec[(index - ADC_REC_SAMPLE*2) & ADC_REC_DOTS_MASK][g_ui[ui].chn_index];
  2023. v1 = g_adc_dots_rec[(index - ADC_REC_SAMPLE) & ADC_REC_DOTS_MASK][g_ui[ui].chn_index];
  2024. v = g_adc_dots_rec[(index) & ADC_REC_DOTS_MASK][g_ui[ui].chn_index];
  2025. //电流零偏处理,由于在DTU3.0电磁式的设计中,AD一个码值代表6mA,10个码值代表60mA,所以必须修正
  2026. // 电流零偏计算
  2027. g_xdl[sw].zero_sum += v1;
  2028. if(g_xdl[sw].zero_cnt < ADC_REC_SAMPLE)
  2029. {
  2030. g_xdl[sw].zero_cnt++;
  2031. return;
  2032. }
  2033. else
  2034. {
  2035. g_xdl[sw].zero_sum -= v0;
  2036. }
  2037. // 根据zero,修正v值,
  2038. zero = g_xdl[sw].zero_sum/g_xdl[sw].zero_cnt;
  2039. v -= zero;
  2040. // 如果大于定值,启动录波 间隔电流大于无流值
  2041. if((abs(v) > xdl_zt_v))
  2042. {
  2043. // if(sw > 0) /**< sw = 0 默认间隔不判无流值,其它间隔要判 */
  2044. {
  2045. // if(g_sw[sw].m2_max < pRunSet->dIWL_SQR[sw])
  2046. // continue;
  2047. if(pRunSet->bTT_TBLTYPE){ /**< 改为所有间隔都要判零流 */
  2048. ref_i = UI_SW_INDEX(sw,SW_AC_I0);
  2049. iv0 = g_adc_dots_rec[(index - ADC_REC_SAMPLE*2) & ADC_REC_DOTS_MASK][g_ui[ref_i].chn_index];
  2050. iv1 = g_adc_dots_rec[(index - ADC_REC_SAMPLE) & ADC_REC_DOTS_MASK][g_ui[ref_i].chn_index];
  2051. iv = g_adc_dots_rec[(index) & ADC_REC_DOTS_MASK][g_ui[ref_i].chn_index];
  2052. ref_v = abs(iv0 - iv1*2 + iv);
  2053. if(ref_v < g_i0_arg[sw].ref_v_set)
  2054. continue;
  2055. }
  2056. }
  2057. // 置启动标志,不能放在录波启动成功的条件里,避免启动失败后频繁打印
  2058. g_xdl[sw].st = XDL_ST_QT;
  2059. g_xdl[sw].qd_us0 = ustimer_get_origin();
  2060. ret = rcd_start(sw, RECORD_TYPE_XDLJD, xdl_get_LLTB_waves(sw));
  2061. if(ret == 0)
  2062. {
  2063. 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);
  2064. }
  2065. else
  2066. {
  2067. rt_printf("录波启动失败(ret=%d,sw=%d.\r\n)",ret,sw);
  2068. }
  2069. // if(pRunSet->bTT_TBLTYPE){ //零压触发每次只判一间隔,下次进来再判下一间隔单元
  2070. // return;
  2071. // }
  2072. }
  2073. }
  2074. }
  2075. /******************************************************************************
  2076. 函数名称: max3
  2077. 函数版本: 01.01
  2078. 创建作者: 赵海洋
  2079. 创建日期: 2022-03-22
  2080. 函数说明: 三值比较函数
  2081. 参数说明: 无
  2082. 返回值: 无.
  2083. 修改记录:
  2084. */
  2085. #if 0
  2086. static int max3 (int a, int b, int c)
  2087. {
  2088. if (a>=b)
  2089. if (a>=c)
  2090. return a;
  2091. else
  2092. return c;
  2093. else
  2094. if (b>=c)
  2095. return b;
  2096. else
  2097. return c;
  2098. }
  2099. #endif
  2100. #define XDL_FG_INTERVAL (1) //(5) //复归间隔 unit: s
  2101. //小电流跳闸、复归
  2102. static void _xdl_protect_TZ_FG(int sw, DWORD dStep)
  2103. {
  2104. int soeno;
  2105. bool bQD;
  2106. static uint8_t gj_flag[SWITCH_NUM_MAX]={0};
  2107. static uint8_t dz_flag[SWITCH_NUM_MAX]={0};
  2108. // 电流闭锁复归
  2109. 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);
  2110. RunTR(&g_tXDLTime_I[sw], bQD, dStep);
  2111. // 小电流告警延时动作
  2112. if (pRunSet->tSwSet[sw].dT_xdl_GT)
  2113. {
  2114. if((g_xdl[sw].gj_us0) && (ustimer_get_duration(g_xdl[sw].gj_us0) >= (pRunSet->tSwSet[sw].dT_xdl_GT*USTIMER_SEC)))
  2115. {
  2116. if (gj_flag[sw]==0)
  2117. {
  2118. g_xdl[sw].gj_us0 = 0;
  2119. #ifdef RECORD_SW_U0TB
  2120. if(g_tXDLTime_U0TB[sw].boolTrip || g_tXDLTime_I[sw].boolTrip)
  2121. #else
  2122. if(g_tXDLTime_U[pRunSet->tSwSet[sw].bTT_Power_v2].boolTrip || g_tXDLTime_I[sw].boolTrip)
  2123. #endif
  2124. {
  2125. // 告警
  2126. if(pRunSet->tSwSet[sw].bTT_xdlgj)
  2127. {
  2128. // g_xdl[sw].gj_fh_us0 = ustimer_get_origin();
  2129. 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 );
  2130. if(!soe_check(EV_ABC_JDTZ +sw*EV_SW_NUM))
  2131. {
  2132. soe_record_ev(EV_ABC_JDTZ +sw*EV_SW_NUM, 1, g_xdl[sw].dz_i0[0],g_xdl[sw].dz_u0[0],0 );
  2133. }
  2134. }
  2135. gj_flag[sw] = 1;
  2136. }
  2137. }
  2138. }
  2139. else
  2140. {
  2141. gj_flag[sw] = 0;
  2142. }
  2143. }
  2144. // 小电流跳闸延时动作
  2145. if (pRunSet->tSwSet[sw].dT_xdl_T)
  2146. {
  2147. if((g_xdl[sw].dz_us0) && (ustimer_get_duration(g_xdl[sw].dz_us0) >= (pRunSet->tSwSet[sw].dT_xdl_T*USTIMER_SEC)))
  2148. {
  2149. if (dz_flag[sw]==0)
  2150. {
  2151. g_xdl[sw].dz_us0 = 0;
  2152. #ifdef RECORD_SW_U0TB
  2153. if(g_tXDLTime_U0TB[sw].boolTrip || g_tXDLTime_I[sw].boolTrip)
  2154. #else
  2155. if(g_tXDLTime_U[pRunSet->tSwSet[sw].bTT_Power_v2].boolTrip || g_tXDLTime_I[sw].boolTrip)
  2156. #endif
  2157. {
  2158. // 跳闸
  2159. if(pRunSet->tSwSet[sw].bTT_xdltz)
  2160. {
  2161. sw_do(sw,SW_DO_BHT,SW_DO_TYPE_SELECT_ON);
  2162. sw_do(sw,SW_DO_BHT,SW_DO_TYPE_ON);
  2163. // g_xdl[sw].dz_fh_us0 = ustimer_get_origin();
  2164. 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 );
  2165. if(!soe_check(EV_ABC_JDTZ +sw*EV_SW_NUM))
  2166. {
  2167. soe_record_ev(EV_ABC_JDTZ +sw*EV_SW_NUM, 1, g_xdl[sw].dz_i0[1],g_xdl[sw].dz_u0[1],0 );
  2168. }
  2169. 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]))
  2170. {
  2171. soe_record_ev(EV_A_JDTZ+sw*EV_SW_NUM, 1, g_xdl[sw].dz_Ia[1],0,0 );
  2172. }
  2173. 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]))
  2174. {
  2175. soe_record_ev(EV_B_JDTZ+sw*EV_SW_NUM, 1, g_xdl[sw].dz_Ib[1],0,0 );
  2176. }
  2177. 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]))
  2178. {
  2179. soe_record_ev(EV_C_JDTZ+sw*EV_SW_NUM, 1, g_xdl[sw].dz_Ic[1],0,0 );
  2180. }
  2181. // g_tRelay[sw].tXDLCHZ.sta.bFlag.bXDLDZ = true; //TODO
  2182. }
  2183. dz_flag[sw] = 1;
  2184. }
  2185. }
  2186. }
  2187. else
  2188. {
  2189. dz_flag[sw] = 0;
  2190. }
  2191. }
  2192. // 小电流启动重合闸
  2193. #if 0 // TODO
  2194. if((g_xdl[sw].st) && g_tRelay[sw].tXDLCHZ.sta.bFlag.bXDLDZ)
  2195. {
  2196. #ifdef RECORD_SW_U0TB
  2197. if((!g_tXDLTime_U0TB[sw].boolTrip) && (!g_tXDLTime_I[sw].boolTrip))
  2198. #else
  2199. if((!g_tXDLTime_U[pRunSet->tSwSet[sw].bTT_Power_v2].boolTrip) && (!g_tXDLTime_I[sw].boolTrip))
  2200. #endif
  2201. {
  2202. g_tRelay[sw].tXDLCHZ.sta.bFlag.bXDLDZ = false;
  2203. g_tRelay[sw].tXDLCHZ.sta.bFlag.bXDLCH = true;
  2204. }
  2205. }
  2206. #endif
  2207. // 此标志置1时可以确定产生了界内故障
  2208. if(g_xdl[sw].dz)
  2209. {
  2210. if(g_xdl[sw].dz_fh_us0 == 0)
  2211. {
  2212. g_xdl[sw].dz_fh_us0 = ustimer_get_origin();
  2213. }
  2214. else
  2215. {
  2216. #ifdef RECORD_SW_U0TB
  2217. if(g_tXDLTime_U0TB[sw].boolTrip || g_tXDLTime_I[sw].boolTrip)
  2218. #else
  2219. if(g_tXDLTime_U[pRunSet->tSwSet[sw].bTT_Power_v2].boolTrip || g_tXDLTime_I[sw].boolTrip)
  2220. #endif
  2221. {
  2222. // 故障还没消失,更新计时
  2223. g_xdl[sw].dz_fh_us0 = ustimer_get_origin();
  2224. }
  2225. }
  2226. }
  2227. // 小电流信号延迟复归
  2228. if((g_xdl[sw].dz_fh_us0) && (ustimer_get_duration(g_xdl[sw].dz_fh_us0) >= (uint32_t)(tRunPara.fT_LBXH_RST*USTIMER_SEC)))
  2229. {
  2230. g_xdl[sw].dz_fh_us0 = 0;
  2231. // 告警
  2232. soeno = EV_LX_XDL_GJ+sw*EV_SW_NUM;
  2233. if(soe_check(soeno))
  2234. {
  2235. soe_record_ev(soeno, 0, 0,0,0 );
  2236. }
  2237. // 跳闸
  2238. soeno = EV_LX_XDL_TZ+sw*EV_SW_NUM;
  2239. if(soe_check(soeno))
  2240. {
  2241. soe_record_ev(soeno, 0, 0,0,0 );
  2242. }
  2243. // 接地故障总
  2244. soeno = EV_ABC_JDTZ+sw*EV_SW_NUM;
  2245. if(soe_check(soeno))
  2246. {
  2247. soe_record_ev(soeno, 0, 0,0,0 );
  2248. }
  2249. // A相
  2250. soeno = EV_A_JDTZ+sw*EV_SW_NUM;
  2251. if(soe_check(soeno))
  2252. {
  2253. soe_record_ev(soeno, 0, 0,0,0 );
  2254. }
  2255. // B相
  2256. soeno = EV_B_JDTZ+sw*EV_SW_NUM;
  2257. if(soe_check(soeno))
  2258. {
  2259. soe_record_ev(soeno, 0, 0,0,0 );
  2260. }
  2261. // C相
  2262. soeno = EV_C_JDTZ+sw*EV_SW_NUM;
  2263. if(soe_check(soeno))
  2264. {
  2265. soe_record_ev(soeno, 0, 0,0,0 );
  2266. }
  2267. //间歇接地故障消失
  2268. // jxjd_clr_JDGZ(sw);//TODO EWen
  2269. }
  2270. // 两次判断最小间隔为5S.
  2271. if((g_xdl[sw].st) && (ustimer_get_duration(g_xdl[sw].qd_us0) > (XDL_FG_INTERVAL*USTIMER_SEC)))
  2272. {
  2273. // 如果接地启动,必须等小电流零压零流消失后才能复归
  2274. #ifdef RECORD_SW_U0TB
  2275. if(g_tXDLTime_U0TB[sw].boolTrip || g_tXDLTime_I[sw].boolTrip)
  2276. #else
  2277. if(g_tXDLTime_U[pRunSet->tSwSet[sw].bTT_Power_v2].boolTrip || g_tXDLTime_I[sw].boolTrip)
  2278. #endif
  2279. {
  2280. return;
  2281. }
  2282. rt_printf_time("小电流接地复归(sw=%d,st=%d)。\r\n",sw,g_xdl[sw].st);
  2283. g_xdl[sw].st = XDL_ST_IDLE;
  2284. g_xdl[sw].dz = 0;
  2285. g_xdl[sw].zero_sum = 0;
  2286. g_xdl[sw].zero_cnt = 0;
  2287. // g_tRelay[sw].tXDLCHZ.sta.bFlag.bXDLCH = false;
  2288. // 启动
  2289. soeno = EV_LX_XDL_QD+sw*EV_SW_NUM;
  2290. if(soe_check(soeno))
  2291. {
  2292. soe_record_ev(soeno, 0, 0,0,0 );
  2293. }
  2294. //间歇接地故障消失
  2295. // jxjd_clr_JDGZ(sw); //TODO EWen
  2296. }
  2297. }
  2298. #ifndef RECORD_SW_U0TB //保留原有逻辑
  2299. void xdl_protect(DWORD dStep)
  2300. {
  2301. int sw,soeno;
  2302. bool bQD;
  2303. // 小电流零序电压过压逻辑1
  2304. 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);
  2305. RunTR(&g_tXDLTime_U[0], bQD, dStep);
  2306. // 记录事件
  2307. soeno = EV_XDL_U0GJ1;
  2308. if(g_tXDLTime_U[0].boolTrip && pRunSet->dU_xdl_u0[0])
  2309. {
  2310. if(soe_check(soeno)==false)
  2311. {
  2312. DWORD u0;
  2313. u0 = _Mul_Div_U(sqrt_32fix(g_ui[PUB_AC_U01].m2[0]), 256, g_ui[PUB_AC_U01].m2_factor_k);
  2314. soe_record_ev(soeno, 1, u0,0,0);
  2315. // 小电流零压启动录波,但不进行接地判断
  2316. for(sw=0;sw<g_sw_num;sw++)
  2317. {
  2318. if(pRunSet->tSwSet[sw].bTT_Power_v2 == 0)
  2319. {
  2320. rcd_start(sw,RECORD_TYPE_LXDY,RECORD_LEN_TZQD);
  2321. }
  2322. }
  2323. }
  2324. }
  2325. else
  2326. {
  2327. if(soe_check(soeno)==true)
  2328. {
  2329. soe_record_ev( soeno, 0, 0,0,0 );
  2330. }
  2331. }
  2332. // 小电流零序电压过压逻辑2
  2333. 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);
  2334. RunTR(&g_tXDLTime_U[1], bQD, dStep);
  2335. // 记录事件
  2336. soeno = EV_XDL_U0GJ2;
  2337. if(g_tXDLTime_U[1].boolTrip && pRunSet->dU_xdl_u0[1])
  2338. {
  2339. if(soe_check(soeno)==false)
  2340. {
  2341. DWORD u0;
  2342. u0 = _Mul_Div_U(sqrt_32fix(g_ui[PUB_AC_U02].m2[0]), 256, g_ui[PUB_AC_U02].m2_factor_k);
  2343. soe_record_ev(soeno, 1, u0,0,0);
  2344. // 小电流零压启动录波,但不进行接地判断
  2345. for(sw=0;sw<g_sw_num;sw++)
  2346. {
  2347. if(pRunSet->tSwSet[sw].bTT_Power_v2)
  2348. {
  2349. rcd_start(sw,RECORD_TYPE_LXDY,RECORD_LEN_TZQD);
  2350. }
  2351. }
  2352. }
  2353. }
  2354. else
  2355. {
  2356. if(soe_check(soeno)==true)
  2357. {
  2358. soe_record_ev( soeno, 0, 0,0,0 );
  2359. }
  2360. }
  2361. // 小电流启动延时复归
  2362. for(sw=0;sw<g_sw_num;sw++)
  2363. {
  2364. _xdl_protect_TZ_FG(sw, dStep);
  2365. }
  2366. }
  2367. #endif
  2368. #ifdef RECORD_SW_U0TB //专业检测用
  2369. void xdl_protect_sw(int sw, DWORD dStep)
  2370. {
  2371. /* //零压突变录波单独做功能,不再用此函数
  2372. int soeno;
  2373. bool bQD;
  2374. TSETSW *pSet = &pRunSet->tSwSet[sw];
  2375. // 小电流零序电压过压
  2376. bQD = OverRelay(g_ui[PUB_AC_U01].m2[0], pSet->dU_xdl_u0TB, pSet->dU_xdl_u0TB_fh, g_tXDLTime_U0TB[sw].boolTrip);
  2377. RunTR(&g_tXDLTime_U0TB[sw], bQD, dStep);
  2378. // 记录事件
  2379. soeno = EV_XDL_U0TB + sw*EV_SW_NUM;
  2380. if(g_tXDLTime_U0TB[sw].boolTrip && pSet->dU_xdl_u0TB)
  2381. {
  2382. if(soe_check(soeno)==false)
  2383. {
  2384. DWORD u0;
  2385. u0 = _Mul_Div_U(sqrt_32fix(g_ui[PUB_AC_U01].m2[0]), 256, g_ui[PUB_AC_U01].m2_factor_k);
  2386. soe_record_ev(soeno, 1, u0,0,0);
  2387. // 小电流零压启动录波,但不进行接地判断
  2388. rcd_start(sw, RECORD_TYPE_LXDY, RECORD_LEN_TZQD);
  2389. }
  2390. }
  2391. else
  2392. {
  2393. if(soe_check(soeno)==true)
  2394. {
  2395. soe_record_ev( soeno, 0, 0,0,0 );
  2396. }
  2397. }
  2398. */
  2399. _xdl_protect_TZ_FG(sw, dStep);
  2400. }
  2401. #endif
  2402. // 查询小电流接地是否动作过
  2403. int xdl_is_dz(int sw)
  2404. {
  2405. if(g_xdl[sw].dz == 0)
  2406. {
  2407. return 0;
  2408. }
  2409. return 1;
  2410. }
  2411. static u32 xdl_get_LLTB_waves(int sw)
  2412. {
  2413. #if(EN_REC_MORE_WAVES==1)
  2414. float set_xdl_T;
  2415. u32 waves;
  2416. set_xdl_T = pRunSet->tSwSet[sw].dT_xdl_T;
  2417. waves = (u32)(set_xdl_T *1000 /20); // 周波数= X毫秒/20ms
  2418. if(waves > XDL_RECORD_WAVES) {
  2419. waves = XDL_RECORD_WAVES; //最大5秒
  2420. }
  2421. waves += RECORD_LEN_TZQD;
  2422. return waves;
  2423. #else
  2424. return RECORD_LEN_TZQD;
  2425. #endif
  2426. }
  2427. int xdl_proc_file(char *szname,int *ok_num,int * err_num,int *skip_num,int *question_num)
  2428. {
  2429. static char cfg_file[128];
  2430. static char dat_file[128];
  2431. static int ui[32];
  2432. int fd;
  2433. struct stat s;
  2434. char *pdata,*name;
  2435. int ret,seg;
  2436. int A,D,N;
  2437. int i,j,k;
  2438. int d_bytes; //数字开关数占用的字节数
  2439. int flag = 0;
  2440. sprintf(cfg_file, "%s.cfg", szname);
  2441. sprintf(dat_file, "%s.dat", szname);
  2442. //获取文件属性
  2443. if(sys_newstat(cfg_file, &s))
  2444. {
  2445. rt_printf("%s:sys_newstat error\n", __func__);
  2446. return -1;
  2447. }
  2448. pdata = rt_malloc(s.st_size+1);
  2449. if(!pdata)
  2450. {
  2451. return -1;
  2452. }
  2453. pdata[s.st_size] = 0;
  2454. fd = sys_open(cfg_file, O_RDONLY, 0);
  2455. if(fd < 0)
  2456. {
  2457. rt_printf("%s:sys_open(%s) error\r\n", __func__, cfg_file);
  2458. rt_free(pdata);
  2459. return -1;
  2460. }
  2461. ret = sys_read(fd, pdata, s.st_size);
  2462. if(ret != s.st_size)
  2463. {
  2464. rt_printf("%s:sys_read(%d) error\r\n", __func__, ret);
  2465. rt_free(pdata);
  2466. return -1;
  2467. }
  2468. sys_close(fd);
  2469. ret = rcd_get_A_D_N(pdata, &A, &D, &N, ui);
  2470. if(N > XDL_SAMPLE_LEN)
  2471. {
  2472. N = XDL_SAMPLE_LEN;
  2473. }
  2474. if(ret!=0)
  2475. {
  2476. return -1;
  2477. }
  2478. rt_free(pdata);
  2479. //获取文件属性
  2480. if(sys_newstat(dat_file, &s))
  2481. {
  2482. rt_printf("%s:sys_newstat error\n",__func__);
  2483. return -1;
  2484. }
  2485. pdata = rt_malloc(s.st_size);
  2486. if(!pdata)
  2487. {
  2488. return -1;
  2489. }
  2490. fd = sys_open(dat_file, O_RDONLY, 0);
  2491. if(fd < 0)
  2492. {
  2493. rt_printf("%s:sys_open(%s) error\r\n", __func__, dat_file);
  2494. rt_free(pdata);
  2495. return -1;
  2496. }
  2497. ret = sys_read(fd, pdata, s.st_size);
  2498. if(ret != s.st_size)
  2499. {
  2500. rt_printf("%s:sys_read(%d) error\r\n", __func__, ret);
  2501. rt_free(pdata);
  2502. return -1;
  2503. }
  2504. sys_close(fd);
  2505. //查找零序电压
  2506. for(k=0;k<A;k++)
  2507. {
  2508. if(ui[k]==0)
  2509. {
  2510. flag = 1;
  2511. break;
  2512. }
  2513. }
  2514. if(!flag)
  2515. {
  2516. rt_printf("没有找到U0\r\n");
  2517. rt_free(pdata);
  2518. return -1;
  2519. }
  2520. d_bytes = ((D + 15)/16 ) * 2;
  2521. for(i=0; i<XDL_SAMPLE_LEN;i++)
  2522. {
  2523. if(i < N)
  2524. {
  2525. int r = i*(8+2*A+d_bytes)+8+2*k;
  2526. g_u0_raw[i] = (s16)(u8)pdata[r];
  2527. g_u0_raw[i] |= (s16)(u8)pdata[r+1] << 8;
  2528. }
  2529. else
  2530. {
  2531. g_u0_raw[i] = 0;
  2532. }
  2533. }
  2534. // 数字信号不需要滤波
  2535. //xdl_smooth3(g_u0_raw, g_u0, XDL_SAMPLE_LEN);
  2536. // memcpy(g_u0,g_u0_raw,sizeof(g_u0));
  2537. DWT_Matrix_Transform(g_u0_dwt, g_u0, XDL_SAMPLE_LEN);
  2538. // 处理零序电压
  2539. // ret = xdl_proc_u0(N);
  2540. // if(ret < 0)
  2541. // {
  2542. // *err_num = A-1;
  2543. // return ret;
  2544. // }
  2545. name = strrchr(szname,'/');
  2546. name++;
  2547. for(i=0;i <A; i++)
  2548. {
  2549. // 得到零序电流采样值
  2550. for(j=0; j<XDL_SAMPLE_LEN;j++)
  2551. {
  2552. if(j < N)
  2553. {
  2554. if(ui[i]>0)
  2555. {
  2556. int r = j*(8+2*A+d_bytes)+8+2*i;
  2557. g_i0_raw[j] = (s16)(u8)pdata[r];
  2558. g_i0_raw[j] |= (s16)((u8)pdata[r+1] << 8);
  2559. g_i0_dwt[j] = (float)g_i0_raw[j];
  2560. }
  2561. }
  2562. else
  2563. {
  2564. g_i0_raw[j] = 0;
  2565. g_i0_dwt[j] = 0;
  2566. }
  2567. }
  2568. //xdl_smooth3(g_i0_raw, g_i0, XDL_SAMPLE_LEN);
  2569. // memcpy(g_i0,g_i0_raw,sizeof(g_i0));
  2570. DWT_Matrix_Transform(g_i0_dwt, g_i0, XDL_SAMPLE_LEN);
  2571. if(ui[i]>0)
  2572. {
  2573. int ret,jd;
  2574. struct timespec ts;
  2575. TSETSW *pSet = &pRunSet->tSwSet[0];
  2576. memset(&ts,0,sizeof(ts));
  2577. // 零序电流处理
  2578. // ret = xdl_proc_i0(N,0,7,&seg);
  2579. ret = xdl_proc_i0(0,N,pSet->dI_xdl_zt,pRunSet->dU_xdl_u0_zt[0],&seg,0);
  2580. jd = ui[i]-1;
  2581. if(ui[i] >10)
  2582. {
  2583. *question_num = *question_num + 1;
  2584. rt_printf("线路%s_%d:波形异常(ret=%d,jd=%d)!\r\n",name,i,ret,ui[i]-11);
  2585. }
  2586. else if(ret < 0 && jd == 0)
  2587. {
  2588. *skip_num = *skip_num + 1;
  2589. //rt_printf("线路%s_%d:无算法(ret=%d,jd=%d)!\r\n",name,i,ret,jd);
  2590. }
  2591. else if(((ret-1) == jd)|| (ret == jd))
  2592. {
  2593. *ok_num = *ok_num + 1;
  2594. //rt_printf("线路%s_%d:判断正确(ret=%d,jd=%d)!\r\n",name,i,ret,jd);
  2595. }
  2596. else
  2597. {
  2598. *err_num = *err_num + 1;
  2599. rt_printf("线路%s_%d:判断错误(ret=%d,jd=%d)!\r\n",name,i,ret,jd);
  2600. // xdl_info_printf();
  2601. rt_printf("\r\n");
  2602. }
  2603. rcd_start_xdl(name,i,g_xdl_rcd_a,XDL_RCD_A_NUM,XDL_SAMPLE_LEN,&ts);
  2604. }
  2605. }
  2606. rt_free(pdata);
  2607. return 0;
  2608. }
  2609. /**
  2610. * @brief xdl_i0_argument_init_by_scale
  2611. * @details 通过零流采样通道CT类型初始化小电流i0判断参数
  2612. * @param none
  2613. * @return none
  2614. * @author EW
  2615. * @date 2024-01-16
  2616. * @remarks 根据采样电阻的不同,小电流用不同的突变判断定值,目的是兼容未改电阻的采样板
  2617. */
  2618. void xdl_i0_argument_init_by_scale(void)
  2619. {
  2620. int cfg_index;
  2621. uint32_t sw = 0;
  2622. memset(&g_i0_arg, 0,sizeof(g_i0_arg));
  2623. for(sw = 0; sw < g_sw_num; sw++)
  2624. {
  2625. cfg_index = g_sw[sw].ac_cfg_index[SW_AC_I0];
  2626. // TODO 待确认是否更改硬件
  2627. // if(g_equ_config_ac[cfg_index].scale == EQU_SCALE_CT_20A_300)
  2628. // {
  2629. // g_i0_arg[sw].ref_v_set = REV_F_CT_1A;
  2630. // g_i0_arg[sw].scale_is_1A = true;
  2631. // // rt_printf("sw: %d,i0 CT is CT_1V/1A\n", sw+1);
  2632. // }
  2633. // else
  2634. {
  2635. g_i0_arg[sw].ref_v_set = REV_F_CT_DEFAULT;
  2636. g_i0_arg[sw].scale_is_1A = false;
  2637. }
  2638. }
  2639. }
  2640. /*------------------------------ 内部函数 -------------------------------------
  2641. 内部函数以下划线‘_’开头,不需要检查参数的合法性.
  2642. */
  2643. /*------------------------------ 测试函数 -------------------------------------
  2644. 一个实体文件必须带一个本模块的测试函数来进行单元测试,如果的确不方便在本模块中
  2645. 进行单元测试,必须在此注明实际的测试位置(例如在哪个实体文件中使用哪个测试函数).
  2646. */
  2647. int xdl_info_printf(void)
  2648. {
  2649. int i;
  2650. rt_printf("小电流接地信息:\r\n");
  2651. rt_printf("名称\t方向\t开始\t结束\t累加\t最大值\t最大位置\r\n");
  2652. for(i=0;i<XDL_SEG_NUM;i++)
  2653. {
  2654. rt_printf("U0[%d]:\t%d\t%d\t%d\t%d\t%d\t%d\t\r\n",
  2655. i,
  2656. g_u0_seg[i].dir,
  2657. g_u0_seg[i].bgn,
  2658. g_u0_seg[i].end,
  2659. g_u0_seg[i].acc,
  2660. g_u0_seg[i].max_v,
  2661. g_u0_seg[i].max_p);
  2662. rt_printf("I0[%d]:\t%d\t%d\t%d\t%d\t%d\t%d\t\r\n",
  2663. i,
  2664. g_i0_seg[i].dir,
  2665. g_i0_seg[i].bgn,
  2666. g_i0_seg[i].end,
  2667. g_i0_seg[i].acc,
  2668. g_i0_seg[i].max_v,
  2669. g_i0_seg[i].max_p);
  2670. rt_printf("\r\n");
  2671. }
  2672. return 0;
  2673. }
  2674. int xdl_info_fromfile_printf(void)
  2675. {
  2676. int f_cnt=0;
  2677. int i;
  2678. int len;
  2679. int ok_num,err_num,skip_num,question_num,ok_num0,err_num0,skip_num0,question_num0;
  2680. struct dir_file_ext_struct *p_dfes;
  2681. static char buf[128];
  2682. p_dfes = hf_get_dir_file_ext(RCD_XDL_FILE_PATH, &f_cnt, "/tmp/xdl.tmp");
  2683. if(!p_dfes)
  2684. {
  2685. rt_printf("没有录波文件 \r\n");
  2686. rt_file_del("/tmp/xdl.tmp");
  2687. return -1;
  2688. }
  2689. ok_num=0;
  2690. err_num=0;
  2691. skip_num=0;
  2692. question_num = 0;
  2693. for(i=0;i<f_cnt;i=i+2)
  2694. {
  2695. sprintf(buf, "%s%s",RCD_XDL_FILE_PATH, p_dfes[i].file_name);
  2696. len=strlen(buf);
  2697. buf[len-4]=0;
  2698. rt_printf("文件:%s\r\n", p_dfes[i].file_name);
  2699. ok_num0=0;
  2700. err_num0=0;
  2701. skip_num0=0;
  2702. question_num0 =0;
  2703. if(xdl_proc_file(buf,&ok_num0,&err_num0,&skip_num0,&question_num0)==0)
  2704. {
  2705. // xdl_info_printf();
  2706. }
  2707. ok_num += ok_num0;
  2708. err_num += err_num0;
  2709. skip_num += skip_num0;
  2710. question_num += question_num0;
  2711. rt_printf("\r\n");
  2712. }
  2713. i = ok_num+err_num+skip_num+question_num;
  2714. if(i== 0)i=1;
  2715. rt_printf("小电流算法:文件数=%d,线路总数=%d,正确线路=%d,忽略线路=%d,错误线路=%d,问题线路=%d,正确率=%d%%!\r\n",
  2716. f_cnt/2,i,ok_num,skip_num,err_num,question_num,(ok_num+skip_num)*100/(i));
  2717. rt_free(p_dfes);
  2718. rt_file_del("/tmp/xdl.tmp");
  2719. return 0;
  2720. }
  2721. int xdl_proc_highz(int sw) // 根据电流值大小判断接地属性,小于200ma,认为是高阻接地,用其他算法
  2722. {
  2723. int i;
  2724. int v_max,v_min,v;
  2725. u32 i0_300ma,u0_22v,i0_30ma;
  2726. int dotmax,dotmin,dotv;
  2727. int bgn,end;
  2728. int ret=0;
  2729. if(sw<0)return -1;
  2730. u0_22v=pRunSet->dU_xdl_22V[pRunSet->tSwSet[sw].bTT_Power_v2];
  2731. i0_300ma=pRunSet->tSwSet[sw].dI_xdl_300ma;
  2732. i0_30ma=i0_300ma/9;
  2733. v_max = INT_MIN;
  2734. for(i=0; i<XDL_SAMPLE_LEN;i++)
  2735. {
  2736. v=abs(g_i0[i]);
  2737. if(v> v_max )
  2738. {
  2739. v_max =v;
  2740. }
  2741. }
  2742. if(v_max>i0_300ma||v_max<i0_300ma/4) //最大电流大于200ma,认为不是高阻接地,最大值小于66ma,认为无零序电流
  2743. {
  2744. return -1;
  2745. }
  2746. for(i=0; i<XDL_SAMPLE_LEN;i++)
  2747. {
  2748. if(abs(g_u0[i])>u0_22v)break;
  2749. }
  2750. if(i>=XDL_SAMPLE_LEN-ADC_REC_SAMPLE/2)return -1; //电压值太小
  2751. v_max = INT_MIN;
  2752. v_min=INT_MAX;
  2753. bgn=i;
  2754. end=bgn+ADC_REC_SAMPLE/2;
  2755. dotmax=bgn;
  2756. dotmin=bgn;
  2757. for(i=bgn;i<end;i++) //找第一个极值点
  2758. {
  2759. v=g_u0[i];
  2760. if(v> v_max )
  2761. {
  2762. v_max = v;
  2763. dotmax=i;
  2764. }
  2765. if(v< v_min )
  2766. {
  2767. v_min = v;
  2768. dotmin=i;
  2769. }
  2770. }
  2771. if(dotmax>dotmin)
  2772. {
  2773. dotv=dotmin;
  2774. }
  2775. else
  2776. {
  2777. dotv=dotmax;
  2778. }
  2779. bgn=dotv-ADC_REC_SAMPLE/4;
  2780. if(bgn<0)bgn=0;
  2781. end=dotv+ADC_REC_SAMPLE/4;
  2782. v_max = INT_MIN;
  2783. v_min=INT_MAX;
  2784. for(i=bgn;i<end;i++) //找第一个极值点
  2785. {
  2786. v=g_i0[i];
  2787. if(v> v_max )
  2788. {
  2789. v_max = v;
  2790. dotmax=i;
  2791. }
  2792. if(v< v_min )
  2793. {
  2794. v_min = v;
  2795. dotmin=i;
  2796. }
  2797. }
  2798. 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]));
  2799. // 判断电压极值点是否也是电流极值点
  2800. if((abs(g_i0[dotv]-g_i0[dotmax])<=i0_30ma)
  2801. ||(abs(g_i0[dotv]-g_i0[dotmin])<=i0_30ma)
  2802. ||(abs(dotmax-dotv)<=10)
  2803. ||(abs(dotmin-dotv)<=10)
  2804. ) // 极值点相同,为界内
  2805. {
  2806. rt_printf("\r\ngz界内故障\r\n");
  2807. ret=1;
  2808. }
  2809. else
  2810. {
  2811. rt_printf("\r\ngz界外故障\r\n");
  2812. }
  2813. return ret;
  2814. }
  2815. #endif
  2816. /*------------------------------ 文件结束 -------------------------------------
  2817. */