From caba0002159de81b018e2d125ee00559e3636acb Mon Sep 17 00:00:00 2001 From: 1okko <15377594951@189.cn> Date: Wed, 30 Sep 2026 20:16:26 +0800 Subject: [PATCH] ford: support Edge MK2 yaw counter step of 5 --- opendbc_repo/opendbc/car/ford/interface.py | 4 +++ opendbc_repo/opendbc/car/ford/values.py | 1 + opendbc_repo/opendbc/safety/declarations.h | 1 + opendbc_repo/opendbc/safety/modes/ford.h | 30 +++++++++++++++++++-- opendbc_repo/opendbc/safety/safety.h | 4 ++- panda/board/obj/panda_h7.bin.signed | Bin 105612 -> 106444 bytes 6 files changed, 37 insertions(+), 3 deletions(-) diff --git a/opendbc_repo/opendbc/car/ford/interface.py b/opendbc_repo/opendbc/car/ford/interface.py index 76ac5baa7..cbc6be05e 100644 --- a/opendbc_repo/opendbc/car/ford/interface.py +++ b/opendbc_repo/opendbc/car/ford/interface.py @@ -56,6 +56,10 @@ class CarInterface(CarInterfaceBase): ret.safetyConfigs[-1].safetyParam |= FordSafetyFlags.LONG_CONTROL.value ret.openpilotLongitudinalControl = True + if ret.flags & FordFlags.ALT_STEER_ANGLE: + # Edge MK2 has RCM Yaw_Data_FD1 counter increments of 5 instead of 1 + ret.safetyConfigs[-1].safetyParam |= FordSafetyFlags.YAW_COUNTER_STEP_5.value + if ret.flags & FordFlags.CANFD: ret.safetyConfigs[-1].safetyParam |= FordSafetyFlags.CANFD.value diff --git a/opendbc_repo/opendbc/car/ford/values.py b/opendbc_repo/opendbc/car/ford/values.py index 1e48e352f..3b03fb65f 100644 --- a/opendbc_repo/opendbc/car/ford/values.py +++ b/opendbc_repo/opendbc/car/ford/values.py @@ -42,6 +42,7 @@ class CarControllerParams: class FordSafetyFlags(IntFlag): LONG_CONTROL = 1 CANFD = 2 + YAW_COUNTER_STEP_5 = 4 class FordFlags(IntFlag): diff --git a/opendbc_repo/opendbc/safety/declarations.h b/opendbc_repo/opendbc/safety/declarations.h index c19d430e1..912513f8f 100644 --- a/opendbc_repo/opendbc/safety/declarations.h +++ b/opendbc_repo/opendbc/safety/declarations.h @@ -184,6 +184,7 @@ typedef struct { const uint8_t max_counter; // maximum value of the counter. 0 means that the counter check is skipped const bool ignore_quality_flag; // true if quality flag check is skipped const bool ignore_frequency_check; // true if minimum frequency enforcement is skipped + const uint8_t counter_step; // counter increment per message. 0 means 1 } CanMsgCheck; typedef struct { diff --git a/opendbc_repo/opendbc/safety/modes/ford.h b/opendbc_repo/opendbc/safety/modes/ford.h index f2b59db76..f71a0365a 100644 --- a/opendbc_repo/opendbc/safety/modes/ford.h +++ b/opendbc_repo/opendbc/safety/modes/ford.h @@ -288,6 +288,20 @@ static safety_config ford_init(uint16_t param) { {.msg = {{FORD_Steering_Data_FD1, 0, 8, 10U, .ignore_checksum = true, .ignore_counter = true, .ignore_quality_flag = true}, { 0 }, { 0 }}}, }; + static RxCheck ford_rx_checks_yaw_counter_step_5[] = { + {.msg = {{FORD_BrakeSysFeatures, 0, 8, 50U, .max_counter = 15U}, { 0 }, { 0 }}}, + // FORD_EngVehicleSpThrottle2 has a counter that either randomly skips or by 2, likely ECU bug + // Some hybrid models also experience a bug where this checksum mismatches for one or two frames under heavy acceleration with ACC + // It has been confirmed that the Bronco Sport's camera only disallows ACC for bad quality flags, not counters or checksums, so we match that + {.msg = {{FORD_EngVehicleSpThrottle2, 0, 8, 50U, .ignore_checksum = true, .ignore_counter = true}, { 0 }, { 0 }}}, + {.msg = {{FORD_Yaw_Data_FD1, 0, 8, 100U, .max_counter = 255U, .counter_step = 5U}, { 0 }, { 0 }}}, + // These messages have no counter or checksum + {.msg = {{FORD_EngBrakeData, 0, 8, 10U, .ignore_checksum = true, .ignore_counter = true, .ignore_quality_flag = true}, { 0 }, { 0 }}}, + {.msg = {{FORD_EngVehicleSpThrottle, 0, 8, 100U, .ignore_checksum = true, .ignore_counter = true, .ignore_quality_flag = true}, { 0 }, { 0 }}}, + {.msg = {{FORD_DesiredTorqBrk, 0, 8, 50U, .ignore_checksum = true, .ignore_counter = true, .ignore_quality_flag = true}, { 0 }, { 0 }}}, + {.msg = {{FORD_Steering_Data_FD1, 0, 8, 10U, .ignore_checksum = true, .ignore_counter = true, .ignore_quality_flag = true}, { 0 }, { 0 }}}, + }; + #define FORD_COMMON_TX_MSGS \ {FORD_Steering_Data_FD1, 0, 8, .check_relay = false}, \ {FORD_Steering_Data_FD1, 2, 8, .check_relay = false}, \ @@ -315,17 +329,29 @@ static safety_config ford_init(uint16_t param) { }; const uint16_t FORD_PARAM_CANFD = 2; + const uint16_t FORD_PARAM_YAW_COUNTER_STEP_5 = 4; const bool ford_canfd = GET_FLAG(param, FORD_PARAM_CANFD); + const bool ford_yaw_counter_step_5 = GET_FLAG(param, FORD_PARAM_YAW_COUNTER_STEP_5); safety_config ret; if (ford_canfd) { - ret = BUILD_SAFETY_CFG(ford_rx_checks, FORD_CANFD_STOCK_TX_MSGS); + if (ford_yaw_counter_step_5) { + ret = BUILD_SAFETY_CFG(ford_rx_checks_yaw_counter_step_5, FORD_CANFD_STOCK_TX_MSGS); + } else { + ret = BUILD_SAFETY_CFG(ford_rx_checks, FORD_CANFD_STOCK_TX_MSGS); + } #ifdef ALLOW_DEBUG const uint16_t FORD_PARAM_LONGITUDINAL = 1; if (GET_FLAG(param, FORD_PARAM_LONGITUDINAL)) { - ret = BUILD_SAFETY_CFG(ford_rx_checks, FORD_CANFD_LONG_TX_MSGS); + if (ford_yaw_counter_step_5) { + ret = BUILD_SAFETY_CFG(ford_rx_checks_yaw_counter_step_5, FORD_CANFD_LONG_TX_MSGS); + } else { + ret = BUILD_SAFETY_CFG(ford_rx_checks, FORD_CANFD_LONG_TX_MSGS); + } } #endif + } else if (ford_yaw_counter_step_5) { + ret = BUILD_SAFETY_CFG(ford_rx_checks_yaw_counter_step_5, FORD_LONG_TX_MSGS); } else { ret = BUILD_SAFETY_CFG(ford_rx_checks, FORD_LONG_TX_MSGS); } diff --git a/opendbc_repo/opendbc/safety/safety.h b/opendbc_repo/opendbc/safety/safety.h index a278a756e..256480127 100644 --- a/opendbc_repo/opendbc/safety/safety.h +++ b/opendbc_repo/opendbc/safety/safety.h @@ -152,7 +152,9 @@ static void update_addr_timestamp(RxCheck addr_list[], int index) { static void update_counter(RxCheck addr_list[], int index, uint8_t counter) { if (index != -1) { - uint8_t expected_counter = (addr_list[index].status.last_counter + 1U) % (addr_list[index].msg[addr_list[index].status.index].max_counter + 1U); + const CanMsgCheck *msg_check = &addr_list[index].msg[addr_list[index].status.index]; + const uint8_t counter_step = msg_check->counter_step != 0U ? msg_check->counter_step : 1U; + uint8_t expected_counter = (addr_list[index].status.last_counter + counter_step) % (msg_check->max_counter + 1U); addr_list[index].status.wrong_counters += (expected_counter == counter) ? -1 : 1; addr_list[index].status.wrong_counters = SAFETY_CLAMP(addr_list[index].status.wrong_counters, 0, MAX_WRONG_COUNTERS); addr_list[index].status.last_counter = counter; diff --git a/panda/board/obj/panda_h7.bin.signed b/panda/board/obj/panda_h7.bin.signed index 008caa0b25a888b1c61fd58718ef8078feabc3eb..d2e41eb0eaf063d3a29a16e0676a64a94ad1c586 100644 GIT binary patch delta 8963 zcmaJ`2UrzH*WTF+m!*qz3+x3!iUmRJ1rRR^2zEuIV(*Ft3l^{&V~LLa)2OjWW5f$8 zDuOk}7L8p|(Wr<~11juNF8uH8g8C)j^WW!r@0mI0%$YN1%AVa>o}s;(rA?0BM!58I zyg<(X8{9vB)jEt_UG{3o3;sY8pgGVM=m2yEVu4;je_${$5*QCm0cHZp4f$kY5enY{ ztAP!`R$wQvANU1G2UurkKz{|U0k?n$z*FEQ@E1@BNPrR0G~xwwz!q=>Y6Hw^9cX=^ zArJ(#Y?RCk?NR6mL;>A^KEMEAC@>nB2qXftfO)_YUi9$Lo`Vsh9{ap!m2e*n=uk;>g~#OH8V^i zYn$Kt#w9#tCRbp~B}8ypIrLl8J|u@eGhM}0Cu@IX#K~-+W6lLEsTrEjEvmH5o;G2& z3Ur(}`w}m_g;usZCtmXA#OPmnA=-`;8-Tydo)Z)5!N0nkc$IDvDot6da=O(#uV#bG zyzn}Z6Tc7O#Hm+!0UfTl*foq4m{=90o9J!zrh1nnZ*?4F&VjG8dH#G8uLCc7lwtayzmLM4rl-newB`}T&ZOv zL!YIV?};DHw;GG#_p*+#VGj0%);J*LDs{7UB5UaxYiEcbSO+021{)h2f?8c$XR?n5 z+eTTlCT*_q!qxVic*?NC){?lY#t|H_mjhdXgTN_6Y7J-J`)je)b+lw6AP0C0lmiW} z)Au#!qoYaoL-Bvd-rtRNl#p)PMXN`AuGyaV9~GeYj3=H!Qxh`VT&Hdhde^S7Rz1Ul zzcwp-CTGqiB}C-VIS$D%V{~ZErnZ%%8zcQ3-H0ch>DZiAjyN`g!ZSxtHb|`#&jxAk z9A(0KrL&#uXotb}jO#SX+0Ar0ireW{=lZPSMdx-&kIm(IM z=x*O&vXMUWoyXJ%`b}fxu3s3rL|y%($UQpTzrC_{UccrAr#HM1f0iEc@2yRXA=sVN zrhyZ0noqqO994!Ff7bB4!E;SbW}0+0U(j6Q#!PTJK7<>}_vK8uzG;{p?scJiEKK&xr^QWwVkUnK z_8_O|ghKVI? zPGVZI88d8dmCM^-D)Z52zLsN+(z%mtIyrHQVMSYw3As&IhfdM5(V&Gz*in8^K0O=O zokzHwI(*PLsS@mgFl;nH=)j3d`3AQLH;vsojCCjQ6R-uiK)XaPsbAaZC}zGELdqql zO!KIXVJ0^n#B!;I(MkN6r&a3PP!|kkk=Mv)6S-9;$_)>q8xo)SCz*@i-_URk&tP8v zbYe%hcsIbRD<4I$pv^tmoM65i#eAr#OhpYYzzlG~GH`lB17jPpp$~~oAntWa1N7yR zYbgsrL1?+;aDv%RjKyQt5sy|{*tHKEZDhA0MB6w+S?fo-d$Sln?5>>2b9?x)+D|=- z8Tqm2Vn+OWb*gbct9o@Xi|}TCuP_VdO_1uv^K{h z&t!{GXy#Vh3TX$&K8r=Io zAXW2csQ4yQSzM6=$5@QIfxrRi==8wurfDc1q>h88Sa3IaK~@E82F?LI&a>zsXgm@sN3h=H}nv(V1fGP4?H0Lpqqy;HeZP1Y~VKa9r344IZB^^ z=v#_AwML${nTp~#AVgsqUX46U%$bdB)Q{8D(YGwDZu5c(@Xc+ya?GAipVup%XM(;` zO05iyTqEBI>S0(TpS52jUku7m<^0=5S!HQOGg0$l*I<_{OZ^)8yglbT>gF0b%d>eG z&aZhSojbNxaucYTidu1Jk<+ByJP3n&q&AH4(yNa~jm|$(5dGu*qMkvf8jpkV*a8jl zjn~`W_SEQFLFyb?i#Zqv z4_&)F&Ex!CqiRBxxuPj5O}q<^qhi1sTr*LCgPjtuv$SrwnU*LI)eL5Rq$+?4I zcjCn5ckzfr{dhFeggbQ1I5+EWAWBHrDq%9C>&Cfjw?hdF>%_Rg>J#C^xMhCTA=O4v zd1O6plt(I+FWZ}EoRhEGFOV-QTd`^b>>2OVVofIJ0TI|LlM_kQE%~Y@A%Yn)i876( za!7!tfN18+H-p$du4=5E&|Nuh(%{7mzL4&#x>X?*9_qNxq?k8A^N2ex8?hu{9k5PIdZg^BloPKE#oh4k>kYm zvSaSi*{2JMZG)`I;UsXm9LJGnN6LyTv}JA{e;oNf%pr?-#1>Qr+J)=hJey?dyD!UB zcNG?3aj1*#p@*>2J$9o=x;2@OvfgHES#BML*6; zbjYb7%?{^rp3m}#XJMYY0qES>i%nS(DcYQo>;RrTC&uEOffH|xK{EP)UY_IO!Q|)D zSjvcmu}h_tSK0Kdv@szw`T@0>tFGnv2fQ%#0oBh9R8F*|sBWS2<~CL)RFlqYav&nY;5XXDZQkQaK+;KYN8oapk1K2Kgs z!sytP=42Y(nW9tAah%LJm9M8P_1#lZ<5PAl!X|QJg|y$;Hlum2NkfyHGqKCFf;j&- zH#1+i2&s@bnmjLxjH6fPO(A`0`}x~!pBqi}TAirB@X^||LV85s&5t5kwB>@@pZD9T z3v}RuK(gGhdO>Sq{i>{>DugUof3Pr)kRvo`aTg_jdC_8JyNwsr!CBn%Y_t6$mNEbn zKP+BG{-nN3;%qmUwneHrG8`F9hYCretCtM*>QiQ+%Z3M{zC!9&X2r_ptQ=Kl!^#3H zcciXM`zwzp_SVC;9ny%l>|v&MOII#sIl1A`(uZnBcM6`&h8k04-h5Ft>{+HK7EI1$ z$QO#gn?^kTt;dP1D>e$}W8GRpZ{>;+M0o_}T%e^Z**lPuYOP##S;<{l4{ZFWE*M69 zA4rsKJ?!VnUpc8{7Kc_hAj9aR)tmgCE4ICBoMd8dr}xosd)LsyDA`n)=wjb=6@2tY zNvPmGmY8dGtjyDGYZfVUqPk5R(b>MUjhv;$O*`yzqcqLPwQ#`#utRW1n7qv+$Qy6^ zTi!JBFiNB7E$aJqW9{1Y$}r&Qk81}Kr!QgJ+4@VEsMGo+vVg8$zo0thskLDkv7*Tv z45S4O+Ne`90Jf50XW&K~H6u{#blRqe%J`Y-|BgN#vALbPfR#kVaA@;sUQuOf$JZ$b zcIEu7Dda8H{GcP|)a!@(=0)WpZ?t^5WH9vl!GhQnWsP8u@Aa=W+!Of|9ly=fd|!@w zuHUAcwhbl)v~1f@TaU`8a@X7gN!cH5%xpe5(DB=A^EH~$W!pWJ-OBQj+lF*7-I%v* z0P0%UiJ246vgM`Dtl|BSTB5u$ec>sC*X}~T$zDp~@`T-u$YQ#6w?9h@FYj)OQ>b!x zT~=}1Q?JIjN+GkuC^D+ATQMH)V0PcLlD#$Vsf~Z*o;a)1rRU{QJrnT0h4nUMZ_Vm_ zkS6U5WJV6{t5w&$WWBtmDerHUbw}PJ+>v()rBb?lN8S#aN?-1C5By91x5lk1jo;ENR*dC);ed}~>k zUX)*STr9ut_%Lr3rtl)5E?vWqM_dSY=t;wF+j`{(^Q=5JbVee8QPiaq)9Mo~-H@hh zDVL?>33}11R2owEQ|ccChbCryp(IwXAf(tLj8hoh0@$osBmmi@)%cbLUz- z$^QAt2l)H<>7So?fQOSDzvK>FO79MA%K>3{MHaZnyx{Pd^2g#We~|rN!AVY%TGH{y zbfhU=b2FwVzZH+Nn}DCi*ojz2G&}t%^;nk{G`l~WqSuw-VJwgz`A{F>n{RG0^fi>WbK>Z+GyAM$_giEseg827*k&h6Wb8g7l2a= zC%y;$0GOa0{~I3J7+wvOeSpD0B~X7JCt87z!|i#t2Va>%wP!-SU&)fxqP#?^RVqnI zGQs!X5~&@i0B>5!1JQGOr6ie^3_vNN zY`rXK`gc|z?8VabvN&*uEB`|jS+TUetT%Z5%kD(tpm;QTW5gX$PGkp$Bwa4$#3HGW zTr9*f4l%T!4U!&9n3$EGLQ zAZ6(!G)40wiI%bUX_e=T#IB?%2hOsxv?6iPUpG~~y{cYKDOpk6t|&6K>D2c^{m|Q` zXYvBFT95WDFwwLgeEjo+3@>samWX~dFccUEu+ND1kDGG6@Y#ub$K}oeZ@pplg}g+n~u3Z4m?t2;{H}uz0OxAs(Ejpe{rQ?6H+>~*+T8E+w< zs?TqqDr2)zx~XPfHKg8k=aautWr`?#xjHT7PrgiFHSZvJ)eGS2VpXpVc-8$c{r2Cy zsS8!#%fE_kHSd(lGsHf4Op-g{h0eMv2#oB*cWxH=QJ_4i4=4*<_Dgk=UgzP_hk7UQ z*^^-x>gJ%sKv^))zrr0Qs0iu-%7L=c4+FIaW%ivx7eXKXCUXlRs13$;biii*5(GNX zdZ27J1vIDw8V|WAs0sM>KwqGp8)!J{?w~1X*IDeRa-VMz_x z#0XAdbY}_m%js>CIAQFBiPI-E4DT4$D}pnu%!v`Ao8THXe=)MMNyY*;Ul&q4s1+5DZv}gBvw9QMwUmi~O4H7kOLA2d~Ga?4bWCx8v9p zPeOxtYheUKU@lt4fzb_&IJ8n2=@W2+N-d;N_WNu z@%OkQ9j(d_*x-T~_!eM(znp_x?p26Nm0vpWy@1YD^3`bHb|vZ5bRHRTVZnFvt#G=rIiU@)+VH zCxj8$E#USU4OW<;z7+LD)!$-x~l zZyL;%2UbfxUhr*(%>~p4uOWCZLtzs%AN4{VJ|H6b;A-F&s|qF>>^}TnAe=9~Ra=im zT;F@5Z7^=l(T>^j1+1~QY=Ooo+kuLyT;%^SMu=dh;R<`G#^#|@=2*3{3*aY$XW9X$ z3n+7p86(HKH7EU%$On^8WJ7izGK>|^duZ7?)rQ~DZU`B2YpirEm7Laa7Ki=^+dahu6@lKg`{Wr$(X;lrCg?vf9tj%a0gvgmKFaY?sx z5nFS9cv`qO?wG%r_{krr&_S|Nf=B z{k?;~1wLBWcl)#20aooMHqFbleEvaPD79KbLc0#}YjJDLciS!YJ+j*SvY*eAmwA(3 M*NMN}GoQTwKh`i9hX4Qo delta 8694 zcmaJ`2V7Lg*1vO?a_Le;1eCihDoq6uMG*zW1z7=&74uZ=Sc51&I~W@=iAIN5@@$FN zV}T8d1;K7&iCyd^Du|y67~$Tfe*f7E>i6FJef#_U_spDg=FFKh{m$h=vu}=?rCViaP+@``?p_{4r4c^U;6XH8=wM^fI2|co);_tJD@q>2B?8{Kzkqv=mzv^&!-E0 zkkA1GfT6$$U@R~R_yR}+SY?YrR{-Av6xaxC1$F@YfE?fmPyn0(eg%F9ZUYYh=JYer z65uWHH(+R=&I=7ln0DX=E5IIT0ki@%fDh0S2nIrd2p}4W0|o+#05kmQZxndrfhoWY z;Ge)kU_&<`{a=n8}Zy@9>}lO;6L!JwZvrbmNL1f~Hqfw@3B zunhPbSOcsFHUry%J-|UAuOmNLIF1A}dV0B)Id9cJ|j*J!`>PVi$5RWj41t@zT>C!KOdlDRP#Xek5!!r%Q~2LVIYogxpF%$C7LVDqFD>1w8$YW5Q&&gLVVXPtv9b5tKnG-{ha z31qg))tuP*A}?e^tH^^BFSg}GlMB4i&z=)sfMz>#Vu}|hCb#0mrF5gvV9HAULN{BK zIK23k7fL&E;&%a@=zob9P~keuZ2y!zGp&i%;`Sx;L4~Gqrg3&x?9Tcn2WT}(DFv!H zse~)sz?6ZaOLU9XDl_IG%678;lW3{hW(u0|txa?otLPTAN`YD+CC?;|S^m^sv&*b; zP2u1^35UIH5F()0+S-zqtb+em+KW!J)7h|k@?qz4FHQ{6pR==43NW?|ws3_PTmWw% zME|;33%;%5up-U`JBEpXVL&o42iSduwsrgxP1)l(7XLO*eyl^fJGt`tSLhHYEr0n6 z-Qwg7QJzye{)3x`MzAVV51Z){%DE)dzEmFRm6=4;OXN{b{L!+KlxQaBO!)9{A87D$ zo}G4gVR$=ht*Q28ZlJba>P2&!cV*R-HSYkEEuA$i1U;R3f?Pxk9TTl;(OP&4|K0=i zyB6-I!c|^yr?*?QW77H-J)s)rGLvNKb6iFcazbzCT45Z7y!#M+l6w?s%e>%qoaYjG zgjece#Vb|p4S7LlO&O;;4a#`*w^nP-j4{;d$Fv?|G;z#hIWOyIq!=Zn9t$bp3YkA* zEUWmwxYohgJZpX!(JhfznY=Cmdy#q!lRj5pwHIMx>{rxa$=006Yc3spJ1|yy7?|<>RWtgGve$Q zNup??pNXuM9L!X0uMd#vpVG8szjUe}rtJV>6MP!-ccDUI*%b2wFs1 zGZUPe59h}6!#EReSO9v5E3NRj1e144X_w%=%;fW6PZB~+yLgar+NO(!#OV8WaWq;V z*;NY#{m!l%2+!5g@NQ9UOX@7eI_X7S3;aK;^Tz+%R$+f^jw;|Hxx|3KdIn_Cff0vjU z^IMU()`#k@kPjyQ(P(0hzGYl{;=TMhbMd=tD(+PJnfPUPmyZEi|lC z(BaeL-#Hljq8S%p4rIa3+SfESp#y9BPYEf+L#z(a)=I6aSpdo-Y9*Ir%yw`Bwxs5~ z(1!*N7|L3^a=;j37If4&*x!BDmc`h0pmB9RInbBo$_7?3@_f*8MkWuAY4)(NX?QS; z@aFX3NK59;LB$K(A+E4YeTURH4nf*sXczL9jvqRdnK(H#)v^GZPaEun{NHK!VZ)jB zx?!n|SSI>1(gmA8$b`fjjC3Dv%uOEtCCmLeJO$H#)aUm|WB=)Uj)*mmu9YJvTfX?6 z7ajtrsD9*UrUBQmG}6JNW?C*lY8C*&jZt4(UPQ7;$&O4KNnX-|Bn`PqpCyHnYU(jM z!q^~=n-|3zJ$g2AqR&Q8A^r7nV|0WR(2ZktuB^%1_KgpJ)?s`k=QS$^==;OQjlEda zV_Scie&Y@hOBSf&>%6e^I=w%BxdnHF7i47n(@7Klu$_Zk3UCoX+2IpU+Lj|(3Y;*q z^lg(*6ANae$4y@7Oe-ecw9G^*11P>puTI_>`(eE1nP>%VgOt+{5xZ8t7WBD(t$f;X zt$aSHB8T$}(^(gqiRPl}UG`{~LM!cB`K%-7GwJ$TxlqITc8UF|@=zK*F~v2#BNR0_K6ilza$^sGsqP4i7=l~t|Dz>tqTRwLiEyk z#kmyvX|F-!f}=g$C~zDA9TD4yinu{bal~ox8)%%=sMOsdZ64c_xi}C$y2L#x;rv?Y z?4inBQB_u_-2unRXut|wb5VeUF=>}d1hrl4rVooxJCrb}>Zf*US`E5PTiKL6Kdq@7Y1uSy_pAcW^Nf5M zlTaD>oJe#x<;$v+Xjtel-MMMF$4>vuxPSz}b`&rSmFAU|o4|S>t*CwU+EG(4`O~c_amFI7&Wm?wRZ6PyOmMY&|KrwH zhb*&b+KdGI4R!Il?8=OBfhcHwsa{$`Z_k)d-p~Q5YI2{>OkLK7MW-CKmI4<6W{&Tf z!L@%~9&M`K6rXv>AcfLaGlR(IbimB^NudM_;}CNFY3 z=(a|7yLP0KOV?cYMkxQLZ)b&)bn25f%9+^>zsn0ITt>60pcRMiyC6JU(q<7$>NtBf zIZe0E-cDB0=zs39`Lfzg=c5fUy1bCq{nG=Lx0=(@Xk-Hl7Xi%iTG01*>6kgm#D-p) z6KGRwP@|XN8X<^~(L)QnC%*N>8U(F2AU%u$sRodI%2 zIpKQ z6%D->Yq|1=bmKg={qcI&i zr^1ACVx3Ul&|^%LXW8>qQ{UZ%rk+<(2DZk!sPD_}V%Mmesia0V^T7hncgi)+Z_@Q} znm*s&&cEJVdrN+(3Dkze@AY)+{LN$-4O@`r{H&gIIaI=Fo|OOa zS#-d{iEN=gwlLn(JckpnO~whdm^v)-G|melmv&#|%3my|Ll!B6`5{7AUQAal>SSD` zOzvS{q`xleXzT%hiZ8d?y{I$QnyE$YxxZ~p>!n9Dc!`djqKlWfeVAO_8M<>xC-R;C z-jYyaQ&v-MTv6C~4$-TZB@zs=`OD*s$B!2wt9m{>tu(~)uBOL}Gn{x9fQeVjS7JAr zu_DoKQ*{`QiW8%70O?&X8R)$gW7{OwSgMQQfr#_)z#40owqWVF8e5hYSUQ@H&ivfC zyRhpEs~@L;Ft!&d)zUket+A2nt1|C5p2$#h5!9F}^X8+f{?Cr zKlKwQjR%9OHU4A*)vVp<*Rp==+m7>0EbO)3+O2QfTh>X<>P^%MW&H(jZJlIU&wH-0 zFjKR%2`yQ>OzC2!&e?Q<&7wNa%JRBBrf;1zhc-|*GMTz%`FyGtpS8~DFkC9j8cCc# z24s8a$AHk0>*kTK>AiJJnilIZ>&Ibpzp!3Uy3_d^)W)rvO#|53xxrT1x|KXVx$(ZS zrOfpIjWa#)Lr=vSXVhG!1*OjWc$8tW++p4kfdZ;Yd-_U1UN0axo_qMVAUJXy={!jPKEBf2k z+_v)ggzshZq6ZdJwFzbJ?m}UDBy5~jY}^W_H@0~iXC^yNe50@1=Ek>XZBQmYYs+@@ zoOqfIFQsSoUACJMG*jQ+K!P5V?P4=!`Fd1FOPV~%TGqdcR|ql6@lH#}}!Iv<|JH6fj?n-P26 zv@E7VUXa)z`PU0M)zY%My-2sMcgblw!V3jVhh3yOPh6s%_@v0BmPe^PqO&Sz%;5&< zDT1Z{X>WIeJ@V1MbbA&G<~A!;p3jA6(P=+Ztj~IW|0Y7()2#>8c5AEn2eoiy3B(kl<;n0AF>Lss~5Ho*RIN3$H2$!Da@Uhl|3wd0$|M7IB|2nz<@s={`d&Jdj z0cQGl4{7X?B&(lf-&gR5lcX@3cSMZ~_1zqAxdv;`l)mN_7F?C|vtgYq1i7ehd(Kb0tO?Q9r5#VR)AUHKnNcP6Ftk*M z%pMZHSJIoMPAXFMQO%^ zRWtgw$c;w<%TsLsroN{P4 zRDUKcosA#%IhfJXuZdWXuQGBT5huo1Nq4I!##KsJYh`J-p-MVokR@M(L7FTRe7ZGA zk)Q&+=_!vy&G<$pSs6wkHLC^#O7(ePF2n`rwkm0U4LV<%Wc;6ClvPRDHABD~Rdc(O zoZ0!oqz%!xK{@eXlQ%?5($#8CtdvH{Rnov}#(ywHkv~{yl_N`C>5peykc0H-*>>qG zs%6Qqmg)R!p<7OsG)9)CUiF+hI6=_LQgH*PCZa6e0_8!kH3XnitC~tFvUZ*PI@l^l zlC&88{(U+hXHAl<HK>mD2heRzAPsY^B(rbmqWWRr5=wIO@%HMQ@*?=O8UHDrOrM znc93h<6OIl;_6c+0fnKHH036$(9!uHb|~D+!dD>v7l9{0DZoDS73Z6BHm5Kc-*Qh4 z;5q2;ox5q;oPF8GJTU&)mu@qpdeBps90z>!8!uQkN|x^9M02Et7rekU*sqDV;SbCv zh4q1#480~^(T|GN5IXv@V^}h@o9c7PRwVLscHL8cm$!1FDUuh#YtkFGM&Y^Wb1qX7 zCSy7`spl_NOq7C08ch^$R@66qVxozcOc!5ubasJ+RlGeB~Ve*rYjSv4`(N2E$pS<1BYx1HWReVmS z>bs7nF_7@|)Quqh)Vrl5z4-CR21k9PxHJd6rk-fxd4tzv@bYp+FaOcUiZj3ZPu{Gh zii2~Y*A&zyUWmfe-@NyTq@Tr|%BHcsjCkRbEsOYz68H+54a(xy0&d8=LxHtC3Ia9gHt<;o zE=ArQ^dZKc1~dbFFVG~=)}Te89-y_5vk#*p@-0D6z+Q9Ee}LM9nxJ9}D6D9P@s1Xp zKM$@fLx!Ue83%1uFEI^u0KMR_-)MK1-DG%(mBTKY0rAH4yOo)eYQ6%Kgx zgwP6L*84)PT81OnRwaMk4bOVb@RAD3ef+jGptkqyxfYB1AjEou6*=KA#?9OfU+X9T7HF(mTqu6}_fBvw=iqz=W z!WM|ockp$RBA*G_8ejmx{N4<|)~x1*QzMo9Ya6@_1NM#bM3k@m3GE)Q$X`OX5K(;u zy2Axe*ZYwmFc8;dz#31Oz&Y?)1*1SWev4*|Xsn=7p4ce=b}F7S7vn52U6I$JOZ!0Q zE-GQ=!>0Uyatv&1D0M?2!`FCPrq@dxZ30`uHw%n}l^8L!qQtGu(QO#(<1oI_n(fYL zEwDqtyE-(#HAe(9(fc&8)G^EglDTHQa2%2=sfr6}rqBZN@I9Wn^b+YN`AhIV$n}d$ zS43;&-UQEUOc9%JG*Qw~T==hnAD2!!w2{wrdNro;hJ&neB!nMWA0I$mJ5@%ZYgKp% zR$~x&@xtq1^gYlHytm-(h(KdNUn8Fbnt5RT4=9(uqpebIvxu9526`yt2OU<<2e^&l zaTn-_^gYmVI52R{@K)3a6BS!(2ocOxT+TqnRs`%bADV0}13wtNbCJC84D~P{m@SOt zBSC>AtsU_?vS%2fPP}~nNY82HafkKATga^R}b@Pmf zdGX_(P1(L=^uto|Y4G=na(!Us7n-*< OmZbEG-S^I)IQ%b+=^~u~