From 63b70321015d822ccfd930bf98bdde79db77bbb8 Mon Sep 17 00:00:00 2001 From: pagmatt Date: Sun, 19 May 2024 17:52:27 +0200 Subject: [PATCH] antenna: Add CircularApertureAntennaModel --- doc/installation/source/macos.rst | 3 +- doc/models/Makefile | 1 + src/antenna/CMakeLists.txt | 59 ++++- src/antenna/doc/source/antenna-design.rst | 40 ++- .../figures/circular-antenna-pattern.png | Bin 0 -> 66011 bytes .../model/circular-aperture-antenna-model.cc | 193 ++++++++++++++ .../model/circular-aperture-antenna-model.h | 140 ++++++++++ ...rcular-aperture-antenna-reference-points.m | 105 ++++++++ .../test/test-circular-aperture-antenna.cc | 242 ++++++++++++++++++ 9 files changed, 780 insertions(+), 3 deletions(-) create mode 100644 src/antenna/doc/source/figures/circular-antenna-pattern.png create mode 100644 src/antenna/model/circular-aperture-antenna-model.cc create mode 100644 src/antenna/model/circular-aperture-antenna-model.h create mode 100644 src/antenna/test/gen-test-circular-aperture-antenna-reference-points.m create mode 100644 src/antenna/test/test-circular-aperture-antenna.cc diff --git a/doc/installation/source/macos.rst b/doc/installation/source/macos.rst index cde771541..77732307e 100644 --- a/doc/installation/source/macos.rst +++ b/doc/installation/source/macos.rst @@ -123,7 +123,8 @@ For MacPorts packages we show the most recent package version available as of ea | Emulation with virtual | | | | machines | Not available for macOS | Not available for macOS | +-----------------------------+----------------------------------+--------------------------+ - | Support for openflow | ``boost`` | ``boost`` | + | Support for openflow, | ``boost`` | ``boost`` | + | CircularApertureAntennaModel| | | +-----------------------------+----------------------------------+--------------------------+ Caveats and troubleshooting diff --git a/doc/models/Makefile b/doc/models/Makefile index 07c94fe19..110b625b6 100644 --- a/doc/models/Makefile +++ b/doc/models/Makefile @@ -111,6 +111,7 @@ SOURCEFIGS = \ figures/testbed.dia \ figures/emulated-channel.dia \ $(SRC)/antenna/doc/source/figures/antenna-coordinate-system.dia \ + $(SRC)/antenna/doc/source/figures/circular-antenna-pattern.png \ $(SRC)/applications/doc/http-embedded-object-size.png \ $(SRC)/applications/doc/http-main-object-size.png \ $(SRC)/applications/doc/http-num-of-embedded-objects.png \ diff --git a/src/antenna/CMakeLists.txt b/src/antenna/CMakeLists.txt index 5c38a8cf4..86c442b4e 100644 --- a/src/antenna/CMakeLists.txt +++ b/src/antenna/CMakeLists.txt @@ -1,6 +1,61 @@ +# Check for dependencies and add sources accordingly +check_cxx_source_compiles( + "#include + int main() + { + return std::cyl_bessel_j(1, 1); + }" + HAVE_STD_BESSEL_FUNC +) + +set(circular_aperture_antenna_sources) +set(circular_aperture_antenna_headers) +set(circular_aperture_antenna_test_sources) + +if(${HAVE_STD_BESSEL_FUNC}) + set(circular_aperture_antenna_sources + model/circular-aperture-antenna-model.cc + ) + set(circular_aperture_antenna_headers + model/circular-aperture-antenna-model.h + ) + set(circular_aperture_antenna_test_sources + test/test-circular-aperture-antenna.cc + ) + message(STATUS "Standard library Bessel function has been found") +else() + check_include_files( + "boost/math/special_functions/bessel.hpp" + HAVE_BOOST_BESSEL_FUNC + LANGUAGE + CXX + ) + if(${HAVE_BOOST_BESSEL_FUNC}) + set(circular_aperture_antenna_sources + model/circular-aperture-antenna-model.cc + ) + set(circular_aperture_antenna_headers + model/circular-aperture-antenna-model.h + ) + set(circular_aperture_antenna_test_sources + test/test-circular-aperture-antenna.cc + ) + add_definitions(-DNEED_AND_HAVE_BOOST_BESSEL_FUNC) + message(STATUS "Boost Bessel function has been found.") + else() + message( + STATUS + "Boost Bessel function is required for CircularApertureAntennaModel" + " on platforms using Clang libc++ such as macOS." + " You may need to clean up the CMake cache after installing it to pass this check." + ) + endif() +endif() + build_lib( LIBNAME antenna SOURCE_FILES + ${circular_aperture_antenna_sources} model/angles.cc model/antenna-model.cc model/cosine-antenna-model.cc @@ -10,6 +65,7 @@ build_lib( model/three-gpp-antenna-model.cc model/uniform-planar-array.cc HEADER_FILES + ${circular_aperture_antenna_headers} model/angles.h model/antenna-model.h model/cosine-antenna-model.h @@ -20,10 +76,11 @@ build_lib( model/uniform-planar-array.h LIBRARIES_TO_LINK ${libcore} TEST_SOURCES + ${circular_aperture_antenna_test_sources} test/test-angles.cc + test/test-cosine-antenna.cc test/test-degrees-radians.cc test/test-isotropic-antenna.cc - test/test-cosine-antenna.cc test/test-parabolic-antenna.cc test/test-uniform-planar-array.cc ) diff --git a/src/antenna/doc/source/antenna-design.rst b/src/antenna/doc/source/antenna-design.rst index 7454d7414..1581d9296 100644 --- a/src/antenna/doc/source/antenna-design.rst +++ b/src/antenna/doc/source/antenna-design.rst @@ -113,7 +113,9 @@ pattern. ParabolicAntennaModel +++++++++++++++++++++ -This model is based on the parabolic approximation of the main lobe radiation pattern. It is often used in the context of cellular system to model the radiation pattern of a cell sector, see for instance [R4-092042a]_ and [Calcev]_. The antenna gain in dB is determined as: +This model is based on the parabolic approximation of the main lobe radiation pattern. It is often +used in the context of cellular system to model the radiation pattern of a cell sector, see for +instance [R4-092042a]_ and [Calcev]_. The antenna gain in dB is determined as: .. math:: @@ -192,6 +194,40 @@ are configured through the attribute "PolSlantAngle"; while the antenna elements the second polarization have the polarization slant angle minus 90 degrees, as described in [38901]_ (i.e., :math:`{\zeta}`). +CircularApertureAntennaModel +++++++++++++++++++++++++++++ + +The class CircularApertureAntennaModel implements the radiation pattern described in [38811]_. +Specifically, the latter represents parabolic antennas, i.e., antennas which are typically used +for achieving long range communications such as earth-to-satellite links. +The default boresight orientation is parallel to the positive z-axis, and it can be tuned by +using the AntennaInclination and AntennaAzimuth parameters. +This implementation provides an exact characterization of the antenna field pattern, by leveraging +the standard library Bessel functions implementation introduced with C++17. +Accordingly, the antenna gain :math:`G` at an angle :math:`\theta` from the boresight main beam +is evaluated as: + +.. math:: + G \cdot 4\left | \frac{J_{1}\left ( k\cdot a\cdot sin\theta \right )}{k\cdot a\cdot sin\theta} \right + |^{2}\;\;\;\;\; for\; 0<\left | \theta \right |\leq 90^{\circ} \\ + G \cdot 1\;\;\;\;\;\;\;\;\;\;\;\;\;\;\;\;\;\;\;\;\;\;\;\;\;\;\;\;\; for\; \theta=0 + +where :math:`J_{1}()` is the Bessel function of the first kind and first order, and :math:`a` is +the radius of the antenna's circular aperture. +The parameter :math:`k` is equal to :math:`k=\frac{2\pi f}{x}`, where :math:`f` is the carrier +frequency, and :math:`c` is the speed of light in vacuum. +The parameters :math:`G` (in logarithmic scale), :math:`a` and :math:`f` can be configured by using +the attributes "AntennaMaxGainDb", "AntennaCircularApertureRadius" and "OperatingFrequency", respectively. +This type of antennas features a symmetric radiation pattern, meaning that a single angle, measured +from the boresight direction, is sufficient to characterize the radiation strength along a given direction. + +.. _fig-circular-antenna-pattern: + +.. figure:: figures/circular-antenna-pattern.png + :align: center + + Circular aperture antenna radiation pattern with :math:`G =` 38.5 dB and :math:`a =` 10 :math:`\frac{c}{f}.` + .. [Balanis] C.A. Balanis, "Antenna Theory - Analysis and Design", Wiley, 2nd Ed. @@ -208,6 +244,8 @@ as described in [38901]_ (i.e., :math:`{\zeta}`). .. [38901] 3GPP. 2018. TR 38.901, Study on channel model for frequencies from 0.5 to 100 GHz, V15.0.0. (2018-06). +.. [38811] 3GPP. 2018. TR 38.811, Study on New Radio (NR) to support non-terrestrial networks, V15.4.0. (2020-09). + .. [Mailloux] Robert J. Mailloux, "Phased Array Antenna Handbook", Artech House, 2nd Ed. .. [3GPP_TR36897] 3GPP. 2015. TR 36.897. Study on elevation beamforming / Full-Dimension (FD) diff --git a/src/antenna/doc/source/figures/circular-antenna-pattern.png b/src/antenna/doc/source/figures/circular-antenna-pattern.png new file mode 100644 index 0000000000000000000000000000000000000000..2a41bf361da45de7109fca7714b096f333f31152 GIT binary patch literal 66011 zcmeFZ_aoK+`#)}FpB!YJjB|`+mrly&I5I;Tln!M)on&Rp9v!ntRz-4Bk}}HPqDX}! z*|Y3%j;w>@@O@}J8_)Oa^!Wq6KO~QOjO%{guYKLFdz6Wh4u~1TOhrWn($htnQc=-C zsHkW>80mq3san)92L7UUH`PH<<+bup0~d7mn#VP%s0!m)HZSf1u9;kQ&$?4l?QYol zL*3-^*p`ZF`=K6E^OTR}966}dQpb0gMSXjk?a1-!){ak30q2cUEO1raTo0eHu{;#D z7F**LcQEWwDk`(9`qL zSm|x{?$v4Ht+%N9WdCgQAYm!s9_hL4`$LEP{#l(17$gSPWE7Vy52&vWy|8b)SDx3l zbwy4Q!3YZn@BD~AW&;2E%g&E1zCECC{|sDRqSvPVeiImRM-!*G|Bw9O-www_ zUieR14D_lKoA5nzo{{66!(JKNW6$s#v-#eoLszqv7dsEVF#eZ&>lo}s!(@U-cF)=J8*~d%ORAh2k~r zMR@XHdgYPIhY!opM{5WO?~RVz_>TU#wxQcsHusfY{*a(N`<_7{-wFDCL*?5{;IWO1 znNkC*PW=P758?{^Bm5esr83e3t}83~&5FLo-#C(nPfji=zkFlyIsK0nF0p7g@Jb80 znK=%RJ6Dk*r82@0qXD;&{-VX+r6G?}mYi_TBlG8~A)nqI5n>HA`a$G7+C8AK7jwH& zg@f}IUtp#Fj>f0p172?)oxbV1dU!V9-rR0m`*JCYF5l+|iHHf>XiUgiwX4_rui*U# zwaQpwl8_tas{Ln%tk&lS_Et|GVvNW$ubj8Olu|j}IH&r9M%&i=Xx@bJ2lzVzlNH3g za~Lixb~L{7)4Sa`=#p&uthLp@g8VcS%^Pdyy?jRiKR{ITSiH@A0#`7MDw1S(LpCf8 z+$ED_f2<&0F$8-NnjD9}((hg4H=oRO5s6#qZ431B^i4Z@yjfUAUhN;$QQyGz zO38CK#R+-)tBllnl{wc>4&4!ldX5&gz{8a-Ki!S&Qp{3_*mEu4nKeRM$x zww^wkUU@&n@!Q4jX!^bTA&h`D-U>&%zV)scylbXLvKpFFd(kSI<_hS^y5ps$4FqGdh+^#%*^NpzODd&Kh+r1=l#hPv5Qs40<_hHhSgqe)A zP{qsi;>E2JsomwVO^2Q${zg=qOPRVNVI9BT#%9cuv1r?5M_jYAoUruiyM%vO`-RDS ztdL=F(#4l2kI;nT@MdYfH1&Fhx?v0c<|1lAKVUwV{Rp6(fycT---3+_= zUA+fAyXUz0VzM&=?*v?D7us5gFwzi0fBoU! z+Lm0%u}?R31TR|I)WrpD+N>Tvc7N!^w(a@$?hEIT`yqXrxC1BlLv#W8-w|33+yTL| zJJblxb}V(9)5vMi=RY*!&zt40c8{LUgrad&3&FZ7l_9Jwzv$AA%p)^@k;sOlBVJ6A z^w;GeuCE#{yR2^jTmZBN*oe8WoDQLi#zg%pZkQ`|#^xZ#bUuibn z^Q#cW(%ySNGR!?1Lm%8PzQCDkd-R(Eg?-f~n*ES{%-Xa$-*gN3Bt|Fi)WR-=E_|em z={#8j^X)d}ug^e(u@|u>@Oa1%6};%6K1R)LFY5SYU;nr`G;Hy zo_`q3pt=!`q|NwnK;;{Mze+VN^&SxCwO>t711G7sB_2(+R_} z%QtF`4dEl*5^HIg(f^3)ZvxJJ{`LaaTi{-`Qc@~9*mlGn@|U%mLqQ9%81O+Hm16xQ zob?;K0jUJU7e{_KyO$#BK3=P>Iax?rAx1@h%A5?5;;?25rIWz9e$1|IU_k7qqbQKBS%P#- zrdaAyi;-2P4nc~X9}TvxlK~&xxf`{RB98ghO8-}2j=Iij&1uF%#UNBjt-1BxS$}zX z-7_|c6YXM<1^2@&qI6rmfW)CZ?xG zd7br#xE36*?GRJP1p@ER`tA-dynlOQOzkAh3_eny;8Q1h>DTb=KlVqizD|&qtJ!m`k`S|f<#z7PBC&fp@mf*n;JP}wbMae$p{4XFD-5zQ$Do2vSdO45yE1^s zQY7Wx-ixlowd3r?`7aVc?lQ>*YowBl>*vJJx#8RuL>*3ld@1nwpKK+2w2}BOM&e%SA z(|;cmWnG}(gwP3gneW{Hk+m!!beUp!m_bWbH_Rm<`T5 zwv=6>Q!pAZg8KWpKvY=jmm!^h;i3wo8!Il@LVQ2uFG58hNxvzHWGcpv-V{<%21urC zlxYLKfEVwCwU*=+CQ~+wy08~YrAB(j`NGV%GaA#e5`U61I~sPf$mo3dV|GxPTPQYD zh@$TV7_vXW3-JuSzgjyijhQkb3omZjL}Kn_;6$@2M=11>7*hviLp~v>nT!h|09oJn zROElSosA{Hee|EuZaAH>)jG%ZJ?a1D7A_+La@eXTg^pf+uu z19XL=rY|#KN3{t`Dt<}E*U@!LdT+}y;}Ht{o;Ql zdCQY>f<6v#T(IYfzY<<-#@88%>B|*w)}yFl8z)Hv;pcEBOMtX;Q1k^`8t~3d4&-(8 zWAZhYzunupHD-Gq?K8&ER#8jrXZR=oN0$9R!YXozZLs6crtGM0GB#73qTG?|m-t*s zNQq+X#p&nD_J4i(PRy1fuAjsHU2!Sfc%ev4t-_%97X6RTCz zrbHD-O)8a*EgDH<<0ccdk~~- zpOFC+^Cbxgqo-BGsv{AcbK?O#c>#gb^RhaCw*IV=z}^Gq=Z4?BjFlBx{n&il@vdn4 z#L2kb{@$xIr(6wQ0p<+7@+1PXUdaBRRRIN6HBM0fz&)E6N4VOeB7qjhvxgtvIuBpF zOsF0k%$(&r15nndVsAO>hLQZW?vqvT#I8GIqb?CeghCu10|^Y%L0oXI091IPPS6u#91$I{7{wTo@6gz&zA?rcasLSXUD>t+_9s+vWvVCCxg*zDcg%y z6>|QgEN4G(8mo-fY?BFXua?Jd8CUq>%f*4dz{Tu_2)0Tcp#b>TCV`9p=e_t`E3(7) zs=pesylt0y|I`O5m$d}a=SsnIWYUrCPdzi2x0N$gS5c*&ZIj*A8HZqN`O}y4w%#%f z<_C^6R}ekV){xh}oSqH_KIpVOec6fxAwj~J0Ddvp9j_tQ6_FNWsuYMCxic#pvy2P-~S6RQdD@2HWdjVl=heJ;p8 z%J(0a!s9Cmy8~Ze>X{z$BY5iE;Aa9Z%9uhYYe;yOufGPo520lU%>YOcI=_CFIIX2) zwjD@xvU1yAzT?*wF!@>;&sLLhX!J?(otlZ6+BK}-x+l@ACeAI>e?vJ<&>V#A?T%F+ zbAeG@%(LNtD6nI7Q*NQCT=V1&Lf~n>jno%{FW1JCnmU=yyaO>8e`%?FX%v1ip zOexc*!gV+&MM+v{@NZgWy~kqIu&`2oAHr8t98L9y6`@}HM3VJPvFtls{eC{kMdJ(`qyt=R0XqwL26~YsLhnn6WVZ%ec>(j8D;1H49cq( z-<|y042e@s^34xt_;sVZXfwfAhcu@`VhZWhBhKN#l2%h@@>E0lGQ+1RM(gIrbG{6j z88Jm1psggXLMa{uTfxAe68?K>?5X%ZHYjA%LULiSJ&8`SXb)(=T+1iU4g9e`E%)y& z0M){NFjXEp*cB{|q;(!$7^luVSC_=AmYI7%L5X6lHX9{2Po-D6bvQaPaS9uU1|DsR z?H5IiNV3Z4{OwsF&a>827JM1m&mQi}4$6`LJi~yR_>ZFf-e|cRpE{e%4F`kPeD$cY zux0RTeg{Atydo0~gTveHhW^4KhEhl)GAwAED(|0E9voQ8eNOY{UamjH=J!s`NzLbY zb@YkP>b3Ry@U_~&QFUd+hwRvse`?TgJRLpdN}_g-iyXBQ2o|kbbqGnu!dB{Z66KcN z)BpCp`WLLG*m@q80A~Zh=WE1Z;(gjjm|4$9{U>Dly)ko?OC^Z9?PBDp`sNiuq*nL5 zV9&m2%%95lyU3(5w{B%S9}2c@&-m;@euv@Mh^8+;O;hRz^d#~+bl=>%PERTEYsm3X zZL6yVUHtYvl(6-KKjX*WN-VCDV35ylEmpubZJX`ap8_h-Qk>`DS$XDl&$zbQE;`#% z%x8=q&Spc&_DA1^p%?O>FVqwFLyRbQsrv|8frWzi}u$SXv|s&0v~RE!Dc-*Dd2&kzKKmj zH~T{ET9bInJ&P!5lj9y7RY>qy)$CdNvU4yUxNd@r1jM#lkt1`q8$Je0=_%>6(B$4l zi2x`h{;0^olu4;9B%-xVVu!y zNv>KNV*^U}GrZ%M3?q00kXo1cJOskyCyg%L*_h{~V8`}q!l74^b9v((n>d_BXLb83 z+e;Hph{vh&U@)b7<%H@|WMsij}A)@G+0 z;LZGpBmGsE4N>qlJ&zeax}&ohB9LWw!DT1Dhs*4s4RINF!3$A@*Cn8p2QIn6u2FAN zB`B2c4|w8&>H2~csK+#>n1eg#cijPO^M1J!5goXOV52k3O*fG~OYCr911yZ$bv7jKyjc6SnpeU_o9E9(Vmf8m)nF2# z>soEj?WGZhl(bbM24l`4Kw3XmH97nbI}MW>N5BO!$k>zE9t8}esyDT@*z)B)2miuY zX^6ERJkNtsHJ@tuJv+!;7HGhFg@2_aUvF)sg<-p#Wn+f*_{`)19!k9ZMhqvNZV%bY zijf)@{DRztr8X0|UsaBP76rJmV2ll4Q?wnx8#)j1AWGT8Th}7uE#x-#eFkE2|27^_ z&kYlSm8AVktpD3@G$d+k&D>@I)76|}BqOvL%b(Sm0tLGZlI5?chnHqWmhXX|JfJ9} zcEILy(PSLggf~0!D>fpvp4_m`kddb#1}C{e5?8?bSQrTC1x_?`cTcR1RwR8cR(Bo8*qj%LO6qXHQxqMC?)-aL`hdF#xe}&@H7JU20Cj zwn*PWYkZ0k9X3u4s1OYfDiO3_G0+ZE$<0hw!%{~D05yD~N42NZi{{upraY+>ZN4@K zNY~*?5{+~El)fX3dVwFqtJ{8Ao3=)o4Uj;NwU?sTwoy54ypQdlitI{`AZX~0F|%E! zU~Rbd;LUvg!E}l%j#PS$HWw6?4kjO3QjAQTO5j5Qz653F%~%7N@@hJ4=4BG}+=6Nj z=cYP=(rOdX8XZz>p~#N3il?=+&x5v?fM!$tsY%gK5*v~9<$YBB@lp^dpFbdk!Ve6P z8bwoWUye-N_NGiF^yW1%6-w_w0-^Ka<`x$ls+4_A1Ac)I2rf)+@NecGt#Bq!CCJr0 z)kKZyvl&o&nKmF$`6}WtOwirnIMEqIO4&kv#WQW%uIo-f#E3Gp_4s&DG|bY{EWM3| zl5hz_5cb0-+vuj0J`luv4`ZqA3B^zM+d1M!tU`jJeM~lyEs{RJ>koi;Fc=YMu_a*=k_W!P{X<@%H}& z=UK>1P>MLT*+H3yh`I?kr6V*r1aE=sf+lu4P~L@Gc~Uf{fVAYycdnJ{ueCWc-h84n za3)imRwI3b@P034E^fYnx6?uu#a#8a>(Wq z8^zlBtVS5QpIzt-CHV_Q2k)utbr({<(&dqUmY5QiE2a-@u9E~Le(C{#p~YVRg6kDE zCaXPMFq)dUtE(Y~brk`oX1Cu_+-xWWz>LOa9KDRNeyOvK-Dv=N7vl3Q*i4JSpj3Ti z*wC>^Ogyr^v5|U$YTYsZ5z7x}$Kx#V+9&-%h*;tJRY%SrFB#Qwq zcyZQg>SfUGT_x1k&|RWh=)HyN2pZv1ybNV20vk-NoMuAIty8OQtkZ)*Vkw&9m&L7#Zv)6v>XNy-@jo>e+{q9qv;uGtT@6W(^VPHaWih+Ch?~74DTw; z-Df>g2?iCqjRWz`)TE&YfYPfbAG9t-G>U4^N!|i)1@2|}-!U+~6b_owGbuU))Op)D zG+xe%zb!A-obtVlq)qS#{*Ngj3ccWRxo#NsM$HzX+jn88CuxtO-OHc> z#wZBWVO(3Hs!HKNX7-=;&%(p5F3sz^+l6oSqQJf<5Re~R%9!}S_5G0fb_a(Np=3MNV#8CJtZ!xY%V}1Yg^PmxPmDa+WyIJiP$M$J z9I5v55rFUn05|_9k1>TnaNy`O6YFJ$h(!w7w;wL=&=9Wz@BmR?gn@KI0c9q}m?08&*2!-4xEATq9qHEW3?wg?JUj*E^!95iT*IH{*bhm?fv7OMuxv;v)wgd8AX zqv)X>_j4@|r!u_MY+vEV&bmt*$IweFPb%D?Wcy-eAo0P7-Ds$wwVql+S7+~%$xGHt z@gh{qx0eTe>HK+^UewBn0R}6lf(h1fe|DAOxGwyDjdfO@N$`cbMgIbs!hZX66nggu zN`TsA9LzH(z|3@SMKO(@a~?Y0R@qySdP(*pj?e0KlCJI4!Di*eCz~G~voGQ#`gt`RQcYw*b(NQH#p2Gzn0xIeOvP2gc z#LX_`zP6S=H@ZM@P6VdI9G-Wcx^TtX_hG*&1^Lpl!a?=y_vb8YnhI99!93}!45%(x zZc9cDkjN`n-N(a&8~e|}HTn|wO$vi5^7Px+fEMRm#e3=%hy$m&J-YfaDJ}OX}4{S*MGnLs!pDimdJxCd?8wktB7x{aM`t4g=-GEyrFZtj7DhVEy0+61tr?FC@g zpp1wmYoQ3yL^e8xM)kq+_YO-O>zyBEfhLQ32m|=qXswl6=BUE!G$YLK%5?| zl+O<8X-&)JpxbtcOOpA37}M`x*2@r0Q%w1kozQP?Eh!AWmQkfIe<$obD1@#G3Au0&unXm->Wsn= zw>*7tfuIccW;e{*%&>9wdZ)dEm5rJV&nEfaZ&FbQ#a=XLI`CSF8vrf+=%{o?(~E{D zWYLP=L!~^=ZVlHuwHB2lnt@tN0H9|VhB~rNwda6x3lY0g}c0LIj4wPZN zA{yDdB352cB?#@07-~~=lll$0tzm=H*oj$x;gzp#7TY?BnhM3++w&%VV8fqG58_0@hCN*dBMF;x(di){R*Fo1?uT z2e}u}KDaV8?4Dw^=|G%0Vu=qVghdQISSe(=4Gb%Ohb3Xo;h^@f59jzz+=L1rjDwZR zp%H{@3!I%m95xT&hPL=fra(n2QQ3@}QCJ+I=NEft;}tOg7fdX8%8Qy8_gF!6&vV0m z+bawgF!3?tOsY1k%or?r<<$H9l@qDgV8D~1lP^-7Sozk=5k)pMw(vebv6_bLDA5T$l!M$FlY>5bP4^Q@So|E$4BiPf6Hkmxy0Zb&yVibs z_nr26Zp2(kOkOS;@tb0>ibL+Hep;<@=vRs=uzRcMOnjn!bY>NQPL9vEui`SH<%q7# z&rgGK!L}eKv}|UhiU%nSY_MmS^jpBJEHNsvzjgzn<+NwRy4Xw)zsoa6MAJ=$sk4w= zeTbiLJi5q|TZ_ zsUR0mb>arGOu0rzpe}B(lu*`MJ;C3MnRg~`W!n>Onf~mjLxGa#`KgX&@KH{|3MH5r zeLNDwp$$u{d!1k)lwS8*wlEGB`^kaoYwKL`Y0 z-^WmZR=K}Zet)rDcIC!UyJ~z}g{xvq|V47$)@lN5($sc zQ`A3ue>Dr~*@MHrZ{N@fG3{@a&eqkVwc=*fmA^UnP$S_iP@*MP2r5mBFrnX^20sHj z20cUPfP#YRG0uwlU9m4)0wkrQdjlrWxEmMv; z@5)M18f`7PpYpL_uTxWcRXZ#=T;9`H`P+w4di(0C%7iuRh~xU#_H4+L0gZOjMpT_Z zhD9dvpM_3I9YfH-kh;1Ts(Z{Ntg(^`ay55f5Q^%2+R~c_+O`jJ4iy46^8bYCZH=@N z%2>9?Y_S+5d>?ywB2ej#vKzFx0m;%dOwJwcg*cH+Z}(fW8dM5HGttC6qXn17gDH z_0Ure0M`CK=LykQKvU@Lu1!<|@_}KGrMiS5EenpyH*Eg>O~Cg7&Q_j@yb)0wq}7*6lnfB}XVYhnf_j@y@Y4=2k3$;S`a-ooa#TRv&cDj;xGl5o#E z4Rk{+pwJ%ZUfd3RnJNW|KM=itq0Q1eWaXkC@>Fb861r}6qM3%;Qxfpl-O-p-pk~mg zH^UrmewWk4GC&zjv%O7bQ}33j*%^ZQp5X*jYd3J0J|q-nA{!d;nDn~?9gkQBWM)6b z?%)XwoTrOe25StvfkCWqp6H@~JnPmNmv=tP)=lVZB+Zapqy>vozV95nrH>N`kIEXPP4K#pb4KBusf-tJ^LPx_VSXT-Wb6O3o17 zw_*Op2;%ZFqKlW~pgDHbfZ%E`BU65Sayy4b^`E)D-Oki3480h>78RYta+0gCIj|{r+PU3=9Es#Ol2t$`Dkcbif$rlXBhhL zim=ehKnjA~wZnqA5c(IGY8+`cqLaWV(twK}<7N=4EzY)-^IeQrq*waYDN#Kjf%#!- z#33b?6KaZnXB%FSENcPoWlFVr7#JAfi@JlZuHIv^28vcr_Au^+((i4Bth_GwIH`X@`jx7h_??$F&OgV>Jox z?@}tRYMc~-Y${L{Y$Y;A7b{YaFK9OeRena7oPYVeki5UvN+IDlDV&mGQs``e*3LTj zlov^ISZ*}2wGAzm&2yAq8ee*NoTnmyugw+vV_LGEE#M*bRvhZ)ea()wjMabu)PkJZ zv2YFSM==N@Lqu;jL8!*OZg!y2Cg#luHZvCnUKq>N z4sSk|hJ&T{77^&9B~4CYX!EN%F#+2DPAF#zxm`NatU4Qes5r=L%x`Ch`99?#)x2)| z86`|x>9H$S3TP$g&%z?EO9JT<;z8wZp*!%opS~;}v052(MhtNSeYBx^d`zq3!N16pDFzM=(VI{)W;mE8W2^6r zXT5sDi+E|Ec5Jnu@0M*1i&gxT(+ncvSR&h732M8yIr&}Y(U|jRsftu22gTjqBJ}n$ zMqxVDuD_81JLK;~@0(xZ#Hx>HvHlpnhF%ENZopJ{Vv+E)3G28k?Ha$p(C`(rvR~)g zUo9u)`@b3tAP#<=svq_fQyIOZNuBAiJY~bDWv7h2iaAt zwUfr%BP{?pxQZC5>^f2>LCBuiejgWDk6PZ+s&6}R(NZ*VLp4z%8q&D2%s10x)^t-Arvv+A@ew4*(?WKd&trs$>W^Hlfnb)TYt}S<= zU&u9>4@dt+yK!zn40qsejNvA`$Bgp><}VM*ZN8Ly2OIzpvAze0Tj%jr4%z+B?TGr! zHS9ZZz2TaGk7E;tY^uxgq}kf7+49sH(jehq#GyDVGn@LR>sN%K)%HgBAnt)qSjH87 zW1*vK@yew14CV3aLqU^*L^B;B#c|aj#7o~fi@3a&;&%IC|7E|~q1xpMxys?SrTjcU zE90%Ih}tiM9~Y+x;rpa{-llZRkspxGlpYTfSGY(jtlpA$dsIv6YOC1(Tr03UP+=_m ziv3*8L|g5`{73RFi~Q7rZ4;ffy(GG~3$>hgJoZXoq75XytSQGIV!O9^?_PUXmj9N= z=yhWLU7mV{4T%40WyWToM8^89VfsSpm=gJG(>R@m08sX*c&GA=`Z93P!ahB`R>s-m zfq2flL#rjFC*W{vFwS+k!mL8Zq6D6*5REQ-k9sa)2H#AqRQk*#>-!TIIKzZGCs;v; z3VU07(-oVqX)-EOD!dks&m`7Ym8GF{QGIz|N=1m7$r?2=7ION69*hTEX1wAv3(7oc zJj2QN{7AGK%5`eHdg87QKhmgf@pkX0T1_8Lu5|9!9ZY7wV+OR8l#ba}t~}$&vUMs zZdzku$tKn)6p=^@rFvWMhk{OjV5+T6+v77L#Gx-5w+D2kKrTLUvP%j8ui+0dFK^{K z(L%EYF5E>l#TKz#kHpAIT=U$pGt#>b7Hj&_`7_eOlzY;&ne{AQEtNYUsO~qWM0{d~ zn%^yIU2HEjIO#>`8P8LGenL>GXl@l%whv+yGssi8js4h`V&qr-w#*&y2&cZ9!Ul-0 zA(YWHp-$YRi#z{hZ5A}j$`Ia|P}yGLzd0tGSa5A&T=z|{yJ)tXHZljT;d|r`y7`&e z{acqeGo&CU`5hRCeyZl1d%>{GEVa%X+VJo!480vwfS`pLBR|7h;FNhE`Zu#na`s5s z-Vz^p@FGRrw=ZDXQ=O1kQ|fm=aCInmu&s8&nD%YzzTq-2(i7btk2g*)yp6^y0dyv5 zb5q?@vF+`n9}c4+xgC~+a66gMe(|2?o6eT19D}Ma0ivM?arbW_ugkF`)`o9-dWQG{ z?KletH@+&y2o4j4Z8!2kpwVBx!iD_FhD%-|Op}PNO9OC)%RANl-`)?ymQ#n9xB?|i>@%-1EZ#L^a-}JqCVSL~1 z4L>ogrfgV~!nKxkn(@K?Sn8Ftk`Ss1O;V%U8|@fOp8{3;jmnl7FCaxQwL{!xEM|Fk z>%;3Y-o_1_0)d*nBm?0Y8CtxeSaKs^Bym+uBKKvjPlq> z=wCi*Rw?&>Xy)JOpdp8tEpqdXo}sDz?Qv-j#iUjX?mS&OnV3YCbo z1v36rdmuM5uZ#@wh3+(ozA-Y9XDOV{9Bs)CLVXhLN^U>nCJHUlZiKZYa|G=LFh|N? z3jojywz!4Ie#80Bo;!5|L_ce#jS>!12qAkSO1hw$Fd9_XOqdVCcX>9 zzh>zgs(d)oW$`aZv-ka@dm}#8L(DwKmT9Rei;BZt)bJnx$asXXj|}kF3vqf0XdBrFXmfqc?c>vfzR@kk!edC0U%4{2o}#etL~Ow8lET(1{Bd+;;MfdS61V zA+*VeC)En*WIVjA{0M0M{|K$0qh0aS3$`CKBY27?r4*f#k%8c8){nZ#ff`u2dKW+JrpX?8i-FBsP~E= z?&*)1Vril{)PRJ!{cL|)R$DEECTKE^x=ZgLGmfb+y4_LducHfIBH*2&B~c1Dfe3eS zw_AU35dT;SZSDJzo{wt6Kxc*P*4di<;H+m4AFJNW{>kABP{p=`<1`N~9n z#^bsk(g1MzhaO!=r+)eDBIF^oS^ai!uT?;vGV!B}M@lVO$$K_)O@>SKM=|px0A6s7 zPkoiJ3C=`Hiyve{$;*Z($Z5)czOh5n&E@b`FllY}c#_rw&a!sC(LM5VNZOLi?B=3C z(=2Aq34U#euvS|H3LfwRpHu78oI1k>WCbV~uKi;d3}({BdvqV!bCQu6R1>$7*txFq zGYv?Mrk4i}i1>QC%Oe2zcmHkga6%T1URuyTHSssh@5MDd8f86o_zE`@(F>dpdmc`y z1$IQpkLmL0J=HM7PKqo=Yn-LyJ`TUo0wkHv3}Vmqtn}jhcNaLS!{SivS=w5LhS&0R z+egY`QB2hBLt3F!OBe6D)ybQv85Vx_x%sn51c|{ImzUPg+2Q9i?o{)KAM7ez`yRTc zMx)!A;zXAeM=Aq9eG*5(OlKK^j7;|>Cnrd^W)ms3Y{{rTZGiF|73se#br_y{#6h6FY6!n@JJ9Vzl0VZ{?|`{H4%=_B3YHUpL@n!8JFWUV>@eJ_CO=(x_yk@UPuCT=b72Y4F zBoe(gK3t!#%X*l6gJc(tG5SFkfL*$+qsJ(Ec8X?6?-Svc5-lWw`cBWJk5xixDmvZ? z2i0uh0x9Xmh@oHwMo*Q+^sGln5?|z9cF;J3$&vKwPg9!r6;TJUju!#^a=Dw?FA>ZQ zS~Hqz2}{iaFPufIQ8$@Qm0gbl2}xTBX`!rbc7~IF)cVRjKiP>GR(T48zLiuk5|J1a zx2Lxr@#efJ(Yny0N;Lk&pG&-)9J|fQjHU|k3~99Gor*L z{7z>du*vUkp*1){=+u+Y;Tklf;wKeT^lelN=>+ZNZ8FA1KkFK8zGyek zc$Wfrbk7hlhXicXR$u%Es@c0R7O0|K#iDuX=ZvjXJTr-~t+u4&=M2Z0#pm4B+1Ui* z0Qk1Ka)V!{?=F9CPI`3hK}8OW4CBWFq$V4l$CbVn9HIYah4<0_0+1*4;3JO095W^f z_k=ylJz5`jWVn)2L|vdx?Qs^Ic(}U#JYQ1qqW-8L-zgtCJ|rB+6S?oKY%g%bkHTw5wRDn884N# z)5#DrC@4lpify5s&bZu-jTHSTdtD3=^H zTL5A3-){X55ga0lccZBWjV-MkTM-j<>j~$ZA^{qr8|x;E0USTnCtWi8KBr;4MTABd z5Y!zI(r!3#|9xZYNK^ZpqkXP<=(@lZg_wXl{ku0mi)lhm=6Kvo3 z5uhXPCV{(O%TN&>8T@<$3*(UiB91f6i89HX??qzvK@h?+yZStU0)0i+;KLhILh2!L zOtLbN;9<3s9GZ1*HpA0OMhoRNrp@B}B>h8DUJ_24WS6fT-Se*>{pC=sc2hW%Y5#Fk z&8azwgyl3Uf#H~EoNqK|Sod+HpW@)YD>Ocn0Nrn<3vc$902)iUB`kjqou0;7vHHfE z{Q%r}Z^GMC0k1WBft{iziA)}Cp9)t^UXG{>1R9|mN9!wW=4C}89=!+hi-z|UI?X5< zWnFKotI)#I%(h$_eoULF88*I77!P)Pw2{a0e{EZ45?bTHjYFY~%_r;q8U2Tio!ReC ziV7(C66^WinV&Ufx(QaMg(xrOFp14wtw}`JRh@(17j#S5o&I>*_7H=fJm99|??PKy z0A)io9ph0th`Tt8ngyq@qu=%^fmR2cF(k_d6tm%6Iu$#gG3O7kwS=ypYX+b;--G2}PUb?3LBJPmo+wrd=nuHldc50PdH!6&B$#oevK9VX z_Lb4IlZ;{lJ9I5f9v9Pq$nfaj3etO7kx(d60Q98j6}=XMqR{bV!uH@bH6zpKP#ySK z8o9|oldfv^XuZOEQ&#Z0sVA33_P#UrTV7rv$t@!C4ex50YIfJFvtV#Qb zeBSQ)+A7RfE6UF({<^OXQ!w2e%;b!yuEYTsv~oef^JfPSWYpr%<3`$IkPlD6vp8zN zkEQe)t=;~Sbfm)t+vt==@D~gh1E8BQH6z#ajkcP)@F}nKJ7!^o@EhxSpXr+J1sO7% zTRIFXHdW;@9DiB`DmTHRv`4ejmrJc0@#sZfK_j=SG-fqmC$3>Sa(ZMpw<&B;qm0qPkz+csZAS<*4V3I z@m||XFRa8w1k&zhLjypWx2}YTR~T%Cn0I7U>@@i3TQbfaDJe9loBnJOMsyz+sL75a zCy>E_OZ{HhOSQrk1eNwLw#*LTdJRm!k6B~5vGvAJJYV)}&pef)S?=srd3RS|)-;Nn z=BlFKUJ>rG>@f^Df&Z!>h67|Ol#?YHE_yU|KZIMTh_>hf2kw5{=Yzn^(j)c5`Sio} z-iJ{E)KfETrk~hXaAGXhWP~0H%eqk+Xk`K%KJz2$$i|MIC9cc{-Nt4zd=DFuaH!rh zRrd*oCa5?cLr!>^R2f0ZCT68cl)fcqJiE>t;TdW-5lOFpsl(p75*c={6zE}#I)%QA zkvD})tDz6|q@dmu9uqG<`pA89UIa4cLv_w0t9_Q!RmDp$NsUacW*o%`<`gLJ-?gCH znHl_9{>+4Ifu-Z2pH$VLeO5I~2}go()jqS_rFI5QNSw*{_yaf?V*AR}b7IeZ$Hyl@GkUEfKqT~3P6tl4 z?yTn~azXH2K4-eb^P5O5=Su2|!O_%r^DN8Wq-ac@tzbBvZHfn7zy__O*^H0=Gohu8 z08LiuhLqIItzwqmGyHA9S+W4q6FA{_=Rml+DeNcs!Td5~dNxxv} zi~+i_dVT&T$DRhY04iZ}E6qXi(YPkhMrDpo7Y_*ixgz$cpnIR=V;7uVT*zl(uQ@L# zm+r-r83rB0+H=t{12w0@2sY~XYuDdM;k|^mbKBa0w&d3VC|YWf7>wWGNA-Xk{ndmM z-JS;tK)b*g*G~IAk+veO+TeFOYi;S;S_Nr$FHrd(@t}WUz`?j?akC{5e zZ@-@LGc!gf+8HLj^E`|TWWs21=aLClv*gH;OH1_?o{LAc6+!_tg<**E3&p00V6S~0 zvYrDGNZJ~?G5$8Her^s>W?-|pm$avoONw+|RnBLbN!}Z8qEDq`;-9x%*(9iYbOPNi z-fr`+_=K-?lbwmlgHqf7Eaa3{rjR3u<@xU|6Xd_g$(!ol>Qh@pb(Py-kqfPQS=Y?n zz{<*#VV={rS|7Luywk7IrCQ|)w?kdyo|)B61}=u$JsgVw_XSo7YQZlElBCZ@zN<56stxvjZTzn;G#lh7-eCBnEpjxW9*I@t>J=AU= zz5zS#$d}qiFfhHKSRw0tbS=`aS7=DpPy6*ZTpZ0YZjjEPXMR=B;O6q27#~n!2U^cs z0tsbZi!NIkauwVDZ&Uusub_1hqT9@Tk^MX^FI*>MoH#9MU?jhXWucOT1+3J-t2{du z_PjfqetXXG%~rhU*gmJdH?IRD|Lsl+lWX8}(HIk}Pn_o%hL*;)7?+P^Zc#NAou>xV zdUDRT6*yPRs695$dB9s}7rv3OCop5Pkr*_#)oQUwiU$WY-M;>9W{EC#D;5(mM9jrc z=GT-ibun4u+P_UbYY8xxd77SU*}nQQSHPX6brg!3zxO~8_4P}S{eanlLo1!;> zDQMV@qQm@0P)*70`tY%mv!b|jjns_M{5WvG>@ow&e*Pr+xLHu>qNm=T<->*YW6PeQ zwfv$LJX>!Zn zc%wL@HVge2^Yu)O|BtBm4y3yM|HsW^o(S0-%FIep;m$FVl@S$@&dEtm=ABtM)-jWj zE#%l_?~oPQS)Ht7mAzF~=Q#W>-S5x$_rLLay{_weUeECu#vvlBY(77>@jNxGI)GOc z1jfFB*23p-o-%X+^@atSIkD0BhT!odrQac?daac%)E**^E}SD=EcIE!BGM*dXthc+ zZ;0!fu|kKsw|1(OuEX90k!^S`W3lUb!q=yJKZTJGlrZ-<-cw?VJ9T=J;zcN|$RWGS z^5t8`@Fgs27IB7c8qBDjZ4rD_eQW`-mXQCE&*`}zDDXeuR`%?BkL-Rc!TbI{d`J9u zx>EXTS9T<2z@K$hpC{>PHKBK|qk?VEFEVT~mY%J?@WSAYo=`)e_ni0@je(D?v#Qce zef{dOS1;wETaU*5a(_I_RvTv3j$*3L#`AbZs&)!7T6g(?8~~+a7M1BEkq=pC7Fbrz zbYBoeRs3?_!{UmJHe*XGeEaFvf0FKsG-T8-4>c&SQ`wCCMo#4RjsHhZRP%3Z7pnV^ z(Y&U%e1-Sj(HtO)c>OmI-Z2!%@`fb^xpT%^HJ|{6q!MLmk*3|+=j;vF%P)R z6I%?Y^WQlA|9#P7!U0icD`CuL1xs#}{O^&+K{q%+kgBberjd!J*0{-Sq80*;{gH}yzoXE?AbxNDzo;!@`#gBQEd3a0|kS$1G>C@9Bd}cCI zWS}0y5GAK1rl*Bfz3RGXzLMOXz+s*t$K-O8lKu)K=>{-}qW<7hm&ada(`* zk*+2URhCD)1Q{~-N4UhlHvJzViiZ0ipuup8Zh3qm3_{aQfAws!@c4V^xOIRG)i+}y zerny_%(8UeBPI!yS=OW|BkOHz0r^WUa+E&C+L;uM_7fWDp|+fF(U;N zFm9d&*i-jams5p+4)wt=lbJmRnm4Boo4IqW;&4El}1 zM@?`(777ak5iUf`{U`~F%$JmFMfS&~^X6lZ!_s^yW*KyKLGOHh2$|~UC#pjzP><$X z$#z-u8U71H1mv4@(2wp}Tp)D|0T+P?1BVOSChM^afKhU^DN^+$qt)uI8L%;NZX{bl z#fr@#9VN`^b4oCDr*k16rMJ$xk6mT*dgQ0l0N_0_u^7f2LwOc0P3~7A*Bi(kQI-HD zcA7V7`*Ar6I>PBr8tbiP?yAO96Q;Es2uGV?Z`PRP;fZls_8A>S#k0_rI+t2@q@|__ z?YfbpHgmi|5+d=+p6aW=A`(;(cBqlyi)aFQ&8%_u#UQ0;B|-HMP!|2_cUWJ-@n7Ty z^i(0-FY3>+tw!uj@AU)K7~TRTe!hc*w%JHJV^qaa=^yuz67?VNTyIHp_;f93(Z}Br zr${!62<5QQ0|?S**XofO*0ZA`)O@tyB@{R5Vl(e6tf-2aaZ>Q>UpJ)TzW`!oj>h}` zw?iL=Jdf}4xDqw|QZd$;tgw(+Ulsy8JIq{rql!DTA+GkS&Hv7!(D068ooCX~?M2zT z1N*8v@YI07u4b344Pph0`nkNRh074C=kTG6XQ?&JeuN`$oO{i9-7;8Gl(!*pB&|c} z9RGY(saK?9*+#0Y#_#0;rX%DBsqrzSpT63bI3+3!!WII~A~9%We;&XYQvRI-nMY`Y z(QVNms=l*t2+>3^AH8g^@jGzp4{1Mm1#ZHis>;N`au!;UG1W}7{%Jp?wYK+ofOctKR$%L9W)S1`>VGC)-6+wE*!uYBg4WVKDN6J>0pTya#*U^%Q#P zs=(B5y;mWEr#;#w0|g;}^_@^5Hp*BL-x?uqMp`E&$~;KG#ij57e<|CSChw9#iu$i> z?3=qUU2bYgUyq{wrkeRj({{{S=ZEORD}Mi9y4L;;x2)(vaE#|-*V5$wyBPU^G@d}) zx^3ptw+;(cOM=sma;|p|f@=i5mZr)r-&^fpx0WKnS^(HRSc_|5E$+~n(p!r>D;DN77QWQaU=my^ zR3^oJPGlzn45tF+7Q|XzLT=D3Z8KiJ6J^X6U<1(de!lp}TN84AC%73fw{mD~nGNB9 znwRw(jcN|F(m4t^76A0Ip#Q$CA1UZ2){tngrP(7N_|oS=%v!br@dJKgAbK8tH3Ug$ zBx?hvFrLoN}%>Ua}zA(t2`T4Ck zUGF^hqTErZ6OCl?VSkEFBZ;&9c!+!j#kqKhcA=V}(9q4+iC~&qAV_Tz;AfYrAYm^Z zbDOs6t+7(WnmU&F?RQdtQJlnFT?B_0LH{Q8egwYcScR^beO)re7Hm{jx=n%2{2Zf^ zt}wrkXdf^T_2ti~CsV2gUbHQ9C7ov_I_XiM0o&8lcN&H+pGtEy`X&>WpcxUuFBz!J zXjLzaL?l$Rv}}JxdY4-%>R5gQabrL7KNbN^e-iq`y4BVvCOJ2*=Cy>aB+r&EfdcsR z`tPSH0XUO1H|U>;tT<36o@0E?idzy(;gy#2fap*;q|@iC;gT8cP&Q*Ltye)a<9285 zHMggiXad`eg?N}IHRrs2$sLJ&RKHOeswI8e4PKfOizys*#U^)1U12}v(SR}F2ecm# zPFw?G86nrv`71?wgfxDmLX?y!@6Z4#EM#gwYVVMICMqfI=Y>S%@&HW#z2>hAH!nV> zt8f9G&dYA1KSm(d@lg*X%+QVM124`|yDvFAXDkM{Ud`I5$29*hVe8)n;AFuMy_-kh z4(dN%ivK&xumf$w$MmmQDe~BM*sKHY7{+j(Kj%YD(^F4%f%7(FrrM-7T`lNax%Ai+ zs=k1OkWevq{n`h};X4Fe$##jR#TP~CN8fTJq3<*l?%wU?^LSX4_X$joZ;*DqeSc{q z73Aca{MDY-86#K4$*IQ#z*lHs>0Yf<0C$F-5o;{1`TgRIuR4L7*?qC+?_aB5+}6v| zB#Qv;dYjgGD$G5*k5m=z!L6RCJ@!eMQn$|`;sUALuLbffWBi)=(=X#jr^43Mlpa9P z`&K{i_lDJR^@FxP-vP&IuN2@Mv3`mKGSQ4gtNDwovjtL@4Amxo*Uz{#`C~W(O@?`Tf(YH=y9|0atBK5?%W`ixeE&4AA+p(-;4cQj ztb(VHa=q=KVq53*onJpagw5WY#jX8=CHEpN{p3L#>Jd9s_o?{sAM@pv^mDZ)WQ&e zt3~oHPgVXO3-P{l*Pc1Bs6*M$ElV4v)YhAA-uPszaQAcwD{&6W#=4GUX$w}he@IO= zU{3Yg{aYGlm9d&NSzj&gJcmf=3geVpOd=ap!c1DYi=^Z(z|JVzqb^aPRsPy$!T-;m zr@jd7Xi?t-HjZ`WU+m?=TGW#|W|u48fQ%5kA63h>POXXi@+}O~lAcs-3OiBisp?Fk zwIWwR7QdYa^*b}hO0J}l(q^HnSJ6Ok277DF@il%NH%z)7G7De!C%%e2eSK9^+&{kF z9WMHlJfH^`kQLxRtkoVzdbC~ZJn^HJyiqjGC)YA%b-eA-Afs}U-pKD)X2{iu@cCK4 zWk71t?Kv&;saVUGAkKhNA$0v`oR)5f{^)u(c8QgK*JaQ*AWe;Fy`KT;5;tIMT{^5G z2F|4U=fL_>FE(oe;(V|MsX^rWXD>J7Gj~ z-052`aj0t}X{9R>m=(K-dZ6>lbis#i!MCjFwQ{*ZIK)S;_QVC-Za$RXH>8EmIR8Rx zC=H8%iN=0niLa2U4-jJ$DulN*gWPnCXB4I{q!9){FoZ~OFY#B`@eT`xt4_WsM(k)| zotCdBx)QHab|r*CnvVI)4bz7nd%LPPt-PdMVu{pJUAd-#5$>JIm!tKC=MHBH--j3& zVsIz@JOpEAFP;d2bGlx9;IKXfojOQSWA)dF*s~)JX*(5s#iF`mn&0^e|^v(&KM(%XcfrOO5VxV(ky1yaPq@$FXH+34a^YnjncyTC3CKR{oS;i*GAu! zZ^c%EhU10`zP?&&wLLd77Kl00a|33EH^^xx@TU61+B575*11ea;j0*J-#aDdXSjt!zvmX{E1ls@atn zr_r#f+h5%2v}GI513wDe7zEH;H7xNLR?I2{#670TyIZQAAH970Q~5f+dn@k@Oc+tE zX;WD{l%4U>zp3c}3yKk{Df;3F^of4xAn*y@&)l*Qr}zfiA+E6aKJ^2b7g-5CYz`=7 zEoPTn@Fhmi5WH)CChlNPth4vHo*cQAZL&h<04q;bTVv0EyQW>%qLT|)7Z9R|qbTZD z`x{#^pyfr+{bw{BV2=h~Mv(A&vpe(iQqZ02XO3*AkEboC@1NvLXOQE)`}QNIzZeZZ ziaQT7jy8%gr0}&jcC?V&%&*y6pIt(G(z4ZOsgK6-uAH4Zr1m^h*kRF1G!1ym5}244 zq%8jD3smQ4`rV<@q&UDj=p6U`O})jTT+ZdPPAi1mrS6MG4ra6zk4TkFI48JRIm?eA{Lb=$TaJz7kz8-adppwQ97SyV&KUog3d3Fu~93S|l zDqOOx+#GG~%cs$Z%&WCJz^n*ZLcJKpES0ECeNNi+SS~)U>`>b3ZM;9zbYNHKy11uQ z_A;LYL)>gX$|B9J4?MR#B1r381$=#E%q?7i8VxlEM4yJap5Y$Cgj!6k-o&Qo98`&t zrq_h6no${Pc$R5j1BqCmu^2bAYJ2MB61|-hs3kD&0Z=+r!g_)8L$!-eO!S*c=Jgtf zS(#EX_&IOoH!&R&bPiY67;s*lHNUd0xZ6P{_ z=1Sf6xg9Ir{nMGuP*;|p4*=44dr@`<#Li#AAH+wAXZmvOoh|Z204<1$y;aPFmYtHu z*ax`B$V}ivgTf`KK!F zDbl0HcE@1!IPv!uiY5vm;i3O{SX<`Iv1hKvPK_v3He&y zJO^^yUtRiK>%yy$yK161eno19#Pm2^f=Ew&Kq9O)q#JA&0iSCjvZi_p6nK-(};$`(69hBNlV(Gc_H%*j%(19PKMClmF#CJ;Odk|3QcOp?-?=QffH&)D zP*ftw^$qZ7nt*C?GkCI(6-P$GR@^gX%hL-n|CF8XkY;ppfTg&gw}q#PQ3Lh%#HQ@{ zPwTqP&ni>2W;08IU)F!?g1PbYHe{$e6{RtHPID)q?V_!5F)gKrS$XhYe(VYGKyX}T z)G05WQd5iL+rX8I8gHJzHr~UK=t?C$bZ?Ap3M)Izs#27hG#s4qgj)LAHUGAc<{|!j z9<*rQ4JoTSQA&x?-1SP2M=fW-5NF&h`C7h!^IZwv5!{z0vD8*BXhrk$M>nyuD8tJ9 zwg$jFe;)GuLXxqc#bi=QkcQpHFD*?69$b!W#lX0Wn(lPoWaO=bNGs%YW`&g2dnbkZ zKErTGi%hUudOUxGB2oaIl=*^To@K4ETCfD%6|J`wVje#LLpDQp>}6&1)eZR%K1BO2 zE<**}2KSDst|DYmm`+N%#lDrMuvG*|b;Z14kI$tl>AYdJMCdH|)t4A*RG+*arwXC>GM`pDH`;sa~PYkW4k zVo^^7)ex?P@6N@E0wmo@uBfq>Fh0ru44;}b)iN?DmA-L0x$7#`9{^KG6FouXiLl&$+{ z?q_#No17?8KhC+~343-$BMBg@o)}vs8t=UQ>O|Y(q_=hsIW0lGQ`f@egP08F*RI!z zp*N0JZq)_k_jSqX`}lK9U;eu}d8@DOX4Hevr92uO119W4_w`BkK8Vo^x;JHsBy-Q6 zpSgACR+>fWT3{5{A8BIT!_04F8fhZVed2-^5e_3_Nf%0GYgoP}!A$ZC}SLs7UnBy_!7#(C`Wok3BC%Zcag){l?Z z*q{ZP@NP*NrYLn?#-O{%4ea)6L)-K8#xj4WYv%WLC-!{XW^;i!UbJ%AIjAV%)n@z0 z-&4*HG>2m#o6s%?S>-?N&zE59U=QFT^Ou+ce)Wv!pA1mWCTvRawD`kIas8t1!3}$# z9d$DTD?@7)zh#7gd-~z0+CPgCnd|a5$Ch~k`UD=2uWKo5XgzQxS@>KNhlGWP#BS87hMX=Lx(lNwK1NANz}DCa*oIHvn6+De?reO$H16xeML74$wHR1>gw~_STf8F( z$B9NtAt=Wbpqms(nSMrj)%epnsQP~)^Mi;<9A5nwH_gaT zIQlL=xVH59a?pq>t-BN^$MIL{oZR54St7#xmm%HL&Ol0f zb7)gY%V`=MEpCjKOxtn{N_uK=F~km4vEw^#CU6))|5S8 zkjHez0M%VUh>FgQmHxV(vJp_Qppa$}Fw%9oBC@hlQIboL#Oh1gK?FB4jBkL=QOp#e z)6<~Wd!KjZ{M;&0H8F*cgEIaa=Le*f=%!rl4eppdmxi0vQBr=OGj2+m$Fg5fv#;^& za&gPHvnE%=#{vYuO1(}aHCjKnD|*AU*tG8R!{Hb`MvawUv6ncY&;E5t5ZO}x-tn=* zS6+Gn!V+lOWP2hJkT4ygn6!p|>I$`T; znmdV%<}%NF&1Z$LZY%ZRVuSkf^-^x;oMn;+}s2!!rGC_%H9-mLyyrdr z<~iY84!5O3*l@=jp-vMZsr}m+(+B=>tCYZId{{J5HqKYGeW;AIerH@cD#~jIv8`sP zj^-Dc`ywjdt!9-yRgWZD&vrHF?Y}Jc@g;x|=9}8(B5FJ+jDJ*fy<45`b?*9smZq-C zi26TKsm25YVaFkstWY*6a-$Uun*2Ig4>K2stjOMCHAir1 z9>A_&)sK?XbqX3;;8_1=vzIicB~ib&2wH$rMC_Dn{cf=e-~oGw!xwV7q$Yn2Tx9Ua=}fL@1u4EjZP)3#Y% zz15h&s?R>v&Gykgzzh`qr~cQm(pFlXZ^)oi!4v6W|0sxnvtuaFc~M?}=An{NGbZKn zSjvakGgWRe5oh&;8r1~!*m76dpo-Lvx)c`dPjqm6*={M4@_~-478zpu82Aw2A7^4- z|Iy#~mMFgn<-U`|b}1w=iK?rpI^7sI6C7V7)*X8_?2lV2%tRqZfdlF|+{G2u2f&c? z+12nNa@lOYdnPG*WWJA!I=N0nFy>gT^_Lzo+ZYyf;FjV86jJWa=92jm0pNlEjLWlS7nSN46lz14WMusGjk&HrHF3;MKvS5*h)nnsZi2z^ zP!#ZEt;+BMp}BASxsvduo9$9%z?wJr#V%&ctgd+gv%5NBZ_)wUW^^|o_HG_4=$(dh zpyWahX@r_y_OGSET0YCld8UMX+$zmbNfSQ{J8A@B$z9ilNki9#S$D%_l?{f5v|=Zbnps%Pl4Yi!du#9%)cd9R~411{c{&1Td2k3WI&@Z1d@PyyF8G zPZkBgg$CS}?Ic)Vu?}PjSAOuXM>EH*vgB9R}ybZCI@X z?Nyp*7tmOleNAR?yEC8XRed>e|Db5QkJQt9IA|$&+EQusUblTJr+ZfO6@&-P$~}j3 zHi$w~tgN{GP!KbB){W)L^1%Hrv@%5RY_mbR&8;aNnA4GYtykkAJXU?B3=d7z&yEJW zFZ~cd2y96M2{Gfvod%KegLTHe>2tV0EB=D(vJCO%zPe&|f%h2lNY(6!;`p$TAzu+- zuu`oBL!gT)e3e0nxh)5_b__}{=>}a3VvKBZMxqK3#7Co5+qU%^h}FsHovy97&3A}} ztiDQ(YE3!TsUsNLBVyE@Du)TKicLCDGQ70=Ar@bz0AUPRM0(q|?dR>2VsKN}j~hmB z1ff}h2`Fr-x(HayOv?MC{e{MVcx-1h6)!!g+uoB&tda+kj5yOa|9~@jA~*?AGX>l~ z68aef4UpA2dx#`-tRG_Lkd-@e)YC~L!cp=xKdyXhlNXpZGDk5B z6)iY+ak+_YNxOUb>?b*WgHKTn@&3)=-CA7VAS-VPKDLyHi$VVapuh<+d+$si4&fQo zkFv#e#h5UZkgCB6*=`(?%S3e3E{u4Fc{YC)+iqF>$nwJ04nK+;|)tpf7o5S)biVcF2#~UeL3aX&Vn~j{s>;jeu7rI#v zMao!K#x1akC)HMulhkXN@Y<`jIO#$%B5vq>SQxEIX6q%|QJ9_73ziH9fa|C(+u_|_ z{!y3k7O9_PDbAy`vNyJo1jL9ZakKZq^*m`_aC~ZcHJKGHSN+ZWJW!pyuBsQuna;fr z0G-?X|K46x#VHK$kV;4Ps8{)DTKx7HfKEgGoy3uwGcY5NmuDmleH;7M?LT$QPKaDg z3iM)eYyrbG{PWer3)S3A<*!G`Y*~5J`?Ot#TgsuFP^w90V%dmC3a~j6z)mYykYzH~ z4urJ-$$s2t)L68g-Xx|4t|h6-_}HY# zSQgBDm^BX7AJ8*%=2G4_6BuRgD151>#njAqF+)xotUiqUyL*p(xY}*HYvw1(fZ88C zwC@(+RSbZ|zYj_0%@oXO+)P8WGG&N|8kUiJP_TPZJQ(j)?lO`?an%$4&W45Li#M82 zzd`G^QR7Y*Z*hy;#?P^d8mRp6oOri{n-oY#*k#D9okgNm}9TRA?rF3qrWM^!0LFIo;Uml&0O{K5E& z;o*A6)ttn$Y|sYyxuN_-v0uq_ggNZxg#D__id&`)8R`!FyXxtQwd(#vy>BUUymqzf z7v?o&#>GWZo{adE*^m$__lIt2Q4+i~{BZ^B)!$=P+rT1#uK_vO)t+9jb$U)fvmX>UA^+}LSU4XZEL?Mr_h3l$|WXW z+cY+)a*PZurTLi0=$YejSOq2Rx^x0pg&Nb|YvjlHp?{8j*V&+#U)ULU#!|E+Zkeg^ zXwY@8h<9>A3$8e5KdYzgT^kerFEF(I6z$@ew2MSbXo;`nlIympFClcZqERN%?bl0D zC^h<%7o|MnLv*X|YYp^g*`de2pDyclsmI@lM|&nn1|jNH3u_b3YmY4Zh|~1Otz`f; zo_&)ls0O-a@%%{cQx#3*Y|_>|Z_bpcd}uI;jr+szjISw=_6DEkUtuBVqPC_NYn>*3 z47{ACo34oK@&vw4+S@+)-rs6vn##h)SHP}tI}eTuWj4DEPqih0Xk5BP((PcyGfzo2 zK)OMvS^bRpwOU=B@DNpkAXAV+4drXt&a?7?(mi6w+PU?DXQ)gZ)%I#znZ80$Z_uVJ z?{1ULN{0xS05V~;gcItE%fRAO9vL!76v1Kt7c8~j%R$2-yPJw#@q+AnBxfHpB8(p) zii(&`0xhLtOW5EF9~jha`Q70vv;khky>t4(BVIHWzfQl2NWA>f<^XNq?ad1tUwJ2J zjo;zNHzFXbWA;7aw$}J4gnao&{IMPFWUT5_7r+yx>W%aW#@w$Q1bqbgWOS>hu=9&= zSLDusr~-b9o5^*G;X4nQ zAepxmD3}#6kZ69pKz|P3*1H-Jlzp&ErdgD;pYLiK3YqarGotGXB3S7k$q&V|GF98H zt5jPZr=)@l^&)$(i3#HPvKuZ#A2#e?sYiYg2I*fetI}7{$uN0Ke+nQ1F_a0Dc51)8 zRPt<=_(4K|uflNEZ%F$9)X^w-Zy_mgq?s94S^?D<;|9ri=1<9*ZR-xAsh`gv<^0Wy z-^(Jaj)=e74*hM$Sox}GWq66j+B*azhVj{&=XaTT_(F5?tOl@X)|=OnEq`3(XuwP! z!U2`~$Gf9xTXDI#2a-~oJ`aS&lG4*WKE4p+dZOIbw6#R{vhSu5un-oIfxz63R&YC> zx}Pp904uBXgP|U^^)prw_alVfvwt~1oqh}9I_#OVV5by>zHNZg+G{wbXp(5$MFXBQ zWK#68EC9R4z}(pRQN*nQ)t|#{5MpGk5T{MT&4Q=XY;#$%HcFVk1ZfabKj?_kg7i5; zHAqtfacHhGFFQ2#j?AcuU%Sd~rHeh)++DBe66WG^`DvsX%WO3(<&<)|v zSD*F0aqp=NV4)zWdo84->`<{BR_+9@i5YWpC+LU>;oc1m3`ITfUqhK|f@}1#R{KTJ zVH|53-5h^!qw!+WW5&4w-q$i1C<5$=mRojU_f-`5&9CEz>a(tN1`V8{Gk?}44*{si z!060cU2#2`w#%q=E5;4e+nCm?nFB$9S)gXhreoIt@uZRU{guwNCUmtsD}5|p2_Iz_ zX8;%a>|%ct3paZ9@1~rlI72uPh71*|6@!Od7u${J71S&> z(nkX~qratcLM1e3KlC{Ji6T!%H{v+BL-o6Uu?^MgYw-g}jL=+i+tti3nFiU%PgI6E zJzGfGTU>I72@`d?uv@IqE51jLo;%o<<(b1Z*Xh22lxdTSz0XcveFN@g$C2w(C3G#D z1>BRA!~b?_ZU3`4r@nk&+5dsD6t9{=YYY=m>#57Lr--`4cN2X?2{t@Ft%m~bnGC-* zwk$bRi^2*8vM@{J1_dTglDFw{KzB(8R4^1+;r%4zD){bYyI9*M8DK7Fr1l^zO5@#HETLqatC z_Wkp%%19U1$qs#TR}ThJG6F+V<(+5HL`&Y+3;t0hdHviw_OWPKx(^sYHJe4ixQT}W z2!=w;Blc>__|Ij85v2JJ_C|ac%jjuJF^*?G_To_t5LkOq3&<2Jo28(+qoo#14pere z{4YeRt0bs$bZI*{pe1>SxO8SyEo|wnW=5LNVUh)-^++1UmIrN$sKza(fRjma5#Av` zB1WS3-eg&5Cx;N(OtJzKegKXDw^*$hJf_@g7X|nM3 zg~A*^2{7IOib5mJ?)1Y#WD8l}mYJH@pd6@c%tLe?$Y&UB9xgkl`u}}!;#jgoH>Eod zUvxF#b?YCvhrRukb!_536l&Z*LKR>4By9`~2ToajSKp&4D%2}k=x=LKzhq)nNRUv|o!QV2`0sST85Id37@@w_ zT6`E}g0tU}4ZV8RGVVj){so<4s0Ri>?24=MK8irKG%be=MPJ@(w4wp$Pi8X$Y#_g* zVqI0TuT^xmFsOR*W%%9o!{!;m%*8`G;x4uz^=`}bH7ET9gNP7|KR>Yh7Q8Hg#fNf| zRa{)vn7rqRx%0nQ0NSKT(KaKg5c9k|w;g@avD_8hc`<-O3*s-2j9nn^G9Zs|cUnox z+%m!n=&>^k4ZiB=*~M27f$mY7H(s!K$N?@FEn!vX(2{_bkdXrtLqnWEV7SZLE?rm} zSXU&Y{$8gi4YhEMbi{-j!8GXpDm$|H3DBS17dw~V`?-gPZ!}$u9n+mvxxsJR6udBa~5Wx<& zpuPK-XjW|_coVxTqqK6&LpIh$EQpWh&NsoI=B!DD%$_{9G$Fvijn`b9hd5ZL-W52} zAW;(@DnF%fIAIyS=VygHc?=H$)YZ%QUapL{BK(A~%gpN1XV$1EpKcz5OO6K6>Ea`$ zK}4F1p%RdVBxdf!4a*Mwsnhe>tpsgAt*nGt&}7e`gtlQ}=<{evC{2(t`F!`qH|kW0 z*#{+O27uwUkmDr|^Ces~Q_Dl8(0n|9keC@n2dByM^o-rJFxjtDId6 zpLIzRWuvnKPDzoK!=MnDi_O{=8mH~$d z$i$;%4D{Tr&95}C91j{*x(@vwd%f+OQXx}l15A|!v)$6;1OK=QAQ3nL{2UW2<@Wa~ z3lr4+FE8?j=Od!SAk&X5=f++#Zm=k+hEfskE9Z5oy|gD}0g&G*b#P)OixT^I*3g6- z`z}(XzUDyqF zUy4_x=d0gx*0TD9X{=BXVyW~pfCHAohd|JB+)1*c>I@qim&E)lBY`T2awBX4EI=5V zKC%g;BVemqq3o#p5{3zchG_V z^4}SdbA2F#DDT#Q?C+3R=GP zKuV0JwuZ7Wk$Sw@lHuB& ztw&Cq%)I^Mo`ECPasx>h((@B{hH)0J4Ad-cuvooL)Zo3<;#|_571R3$4u1gTdqA%% zonsup$T-&py_ko)?jrP}ms$UNY>rnUyF9yW7Rl56m(&($R924cv2VC5w?N>LUlG!M zP0Nm<=GN6v#Kg86KM-A&rVR*l3e9v`1Ua_2I@{X&Jm`wQ>Rnr_l|Mf{SDz09-;TYB z(9@B-i2^gA^;o*OB(E7Q>niB@;YbM&tNB;-ere)Lb3Q8mURM>iy?rJe(lki1pMtlQ z^o>pDb#4q3EzTv4YZFZ~?*F=8>vI;W=yF4==lZDEw7f{-x@Lw)0T|x?$NMaXR%fde@T`Vi9!?vTWMR6mT4eYUk!c&~Ajl;TTu|POzH2pR~TGhM1 z-habByZ70dPI0Ann(n`-4}e%bZ6<+lcrBzaa4`pFs*liN>6-~`x&;~lYOiG=w1icD z9iGwb{#t#Q1*!2%m7E1l^(Niu)Lih;mF(A!1^%!v9im zaoIgq8qhtWG6hNuVDEp{Q_u;VHdBb-7d6j#&7(NaPK3uJ#w;0f$qtW<_}rayeb z%w?Q`A?jMSXhb|$#&2gkI|}n!Q(4~B+~gLdMrnql(e+#070MFAwHc?H^m^fAy(zqXTi*d!oxUhPje8}Ir#W{VTDWQDGF|QpPk&U2?&I}4(fe-fw#K6 z0IK8u3AD+hp#mK`KM;W%CvVkmjfVcQsPX&1R)-x>0F3d4b-9+H3k2XfCz>hto!8R4 zmv_7BvoiNZ0(b9e%=)tJt=9>!w%CV@wkeX}H;Xekf!{&h?S0p-$Hk^mUozFnYcH}P z{46yUaezSd9|^wv7NRktt;{(e3~js9+#dr3uT0yo^5YnEx4>=Jm^oupzLjShq~S z{(Y!>8H-XGHG_gP8)vNhkhCkB3`%Z5JzHCM9jc&A{3=QhmUv}B0Am2sg>_c)6Hv7N z)QV=Q*(+9u@vdpx@B7g=87UMTl-)SSE=NV*#~HdLhI=`j({vxshf+=qj);-|7ydKR z=G0h6^juG_jmcN7Es8sFIqm^%us&#bNHcX31X~HC7=w7dH9kKMR6`fEH|N&J5E2DI zA)_)at@Ucq8CBCBo!21$cYBnQzbLPJN{~O@`feGo(P9pf#4g; zeVQ3Z7c>wzhcb#8iBRHCrd?Q3`>c*e$3M-Xk;Dy4s@cIpK5h5}6y|GCQ+_N7Z zs>@&whfIvsbkrK{Yay&jYKMbM1lA&FR#HDIu67yHyeqF@bNP;%Ai?&)j7M|0xUC*Ww- zY9(U-*stBD^HOg8q9s0MZum?k-rf39d@c<`P8SEXGSaJ2m+!P`K`OvkOk047?1L)< z){pwX=F1gsrslG|7w~g~rNyOv1_?yI@w-V877RzPFs^0otL? z+p1o9FZSFdm-3$yQI3_pLh%i1f$|QjWd3R)3SSDk(-7)aG|e(N*rCdIppmHhm1ds9GX1vN zRm_iQlw!wPgOPP{cnvL{1f_$$blR}jMgMb9$g@rKzx=I~g_N~x4kF?IT;HziI>!17 z<%zvmqlWa9QgYe6qJ2}N!wRl&89H6~3rb9&6y@S?EIq?b`yq9eRu_>TIa>FkWoS<# zKS*PBSkFz)@VntcH`_fDaoDk;<_)Sn|4p?C{pD{UQ>Q2aGtPqbN!6f^Ot6)j7>twpHK^jVii%1w5 zq&f-wHu+nTaSTpv@4q{Vo?<%L6g&v9batoaVuLP2V~_c!>l^v(t4QSgjZ$l)qYek$z67T#1@p z@Nl$nv2FT;tNVDJ11cU4b+XbixgutcxS09u`F-Ianpfb+BHt^}zYd_Bm~9^N&J9|l zC43csG29j{vZ8pn$>KK_1`%re2)nx!NO3#x-CGoudIAkzS#5yU9b67NLb5h1z?A?< zA#6TS7{-0hPC%~^k^<9?g~fPtqA4-NtE>4mO;fx_j&^T?Iwy4v2;su0*O0wqz)g3H zQRSaEH*2b%YUV#mzQBcNnM6wcx553{>mJTs-TW{^8yXRM&>+AHmHHaB{_R@g_P@^8 zF^lA?vgg{i&goAkrhkTfHCvikDph*x`G8uhy)XwL?J#s}ybV-Pcruy+((c{-?-L+< zOKHVajyXooX1&uqkK3M$x_UtaSB4(48SXm=0L`GxVMR8b=M~YBRcFH($MJ(;4NxXG zM&LP}jiaD!tK#6X9FhZ$DMO*i_nZ0^POF-(o#pc2H$~^RM^wQrw4!Q6Y->JrG@Ue- z(s0xTW(2D5DcxWHl)mLi+KAv!5?!dC2l^1Z5J z4QU4r8}@k%jexte{`4z2mTLM(A~Cd9Z8sTPq%$c%Ge{>UM7j7GT{2hS&bky=6VN>e zhM>yRvA;LA?M%R`jk+p$Lk8*f{mJE{tHNu)Er4M>1)eNu!Yw<8_-I z$JV{SajNfiIPHj^x;HpqL8#GDgx*&ov-ac7t@vX3fdk&wo?|yOZ>i9{nqi)pV z{r|3W&_LgGHGAY$=>2sDJK#O}i7``(H>&;yVRT02pmWc)*ky&$Q*7zMn?>#E?t?K;?_$8APF7IG&74fYrpAg?O>&~@nkzJA#?3_MpRAK9{#(*i_G|T4MW2v8js#bS{Uu?~ z&nW^~-^T^(9Nn8;tu~~1Uxk>evl@>~uney*4b{xVlsMA{#)HC;wl_*m?KD+(8=|wr z8bzK7(^!lX2npOKD}Rz6GLFy?1bm6WLpbONr@WkA`{HP znQ0i;_khpGPDB!d!O~82nyWoJ&ug5gst%iE={04)ixad64;c~SCP-yv-WvgWuOS>h zr_8(kxer@wRiUh)D^;>942C(sM@t)x;Ph@cmgC`~yg5c%a8s3L(w?ztKChLm5au!B z$gxAUc$!XF19f4IEME4(+jy9@$i79e>pX{j?hwlRf59|vV(_kPdUIbB$ybIWkpdA_$ym~P8MfOo5yS?4-#5DPKab}qn9jLh0*NU zq7-B20{9U*x660DaccZTLhjgi%ogT;@@Ib5{2+Zww^sgsX_ugPKjwGp?8Ad*c;@Z# zlW{W%(-jq~xax|x`z7m{c`|zGS!ay7GPgVOq?}Lo*BUA|ud8a#N~RBu3DH9_A-Zck zjh%US2#b5y=G6|^)B$- zkP2t9FhWOE>`S3^R;q;aN4x~G(pKy*1*@hWtdh6=;98m&BzW&X@DP9Z!a;GoEcKns zy6?kJwtor;RhT$SWlVnK5>AdCEW*hl>;&cc?Vg#9LEV%~-`YQ>?|_cTmfJSL&ur(4 zAF*Y|FNu`U^m5kmMrK;ZhYLp&C(Dk10Ic(|cf~aEMlsVmetR$FxKGBjp3BO0P1%HC zd3kjaH+6F8N&NQJ=;YX{Xz}xG8OEq(Ux-xVTyN|t@%7}V_WHrSo}h+L`@yRE2Ik_U zku4PhxsG>w5nrPglRJpBzcPuAaw$3Q?wFxU;@I;ib%|;s_c8}Vlgmrt4K+?_YJNh$ zXIv}~RVo47=F$ z7|QM&9kH)%RuOf_fid!%CX9U~N;BgzfmmHJ$wV{mBmMSpL?#GvP(pYP_b)Rs}4S-gNJNp6z=L#+DT6xX;%vl(2*L zAoD<_N`q}9!EIE_X;!nZ>cGeFnC-LvjDW7w;K0FW0g3d75MEf?$+q^uW{+KP|mg=@gOx|VPldSHvT%R$96xCL;v*th!?RIz69?q zO_P@v<1HtzAp9Lnp(bZCw?Dj6z28e~A=>Ztkk;$=vjBwCP9_0@e27B&1F^G>RrVyPm8f$eE?4`lC%}1YVOav*QCN^An)&N=5U`LSa{??o8Ox zTmc7z5Na<)iq|6jv9{9bJ93NyOQqDT0MO&=(o>ilFVNk$Nh<-fr@CkWGR1V!1Jxp( zaEPS#Y|j5j-dn~+-M;&xAT10jH8hgaIFu+Mpdbr*sKQBQO#M-5r8} zNR4#2BHbPP9^ldEzs@>m@AdMW!wWvd%x~_v@_Us=?0c{u+uwdQ5tvB%lxve#)dQF7 zMkfzWJysXGbO~S8DJ3KnF{W+e zEK4wQ^k6)C5gFr4=x4vC88S`;7jci$#S1px)&byb_#s>Ehuwrp!Z|QX?l~#Gj?;wY zz-*vD#kLZty6>i^)e>b>cV7T;*cMZzLcW%VN}i1F*D?Xh-GlSoc0m_}W#8zB_)FN} zw0YOod@TBpKb{%`@zX4dKp{fDF^d} zT2w*H3>1YnZ6I&G__cF7yW`HfwbqI=#y&*H|15W5|Uy3$gsiz#Ok|VpLMar<*ws>05Lcm3pXJ|lR z%cH(fm9iw&fbm7%nJztaB$iyG3%~qN`vWRkZXOtRwyNNbHx3m1QgxFHZ>8k0PN~vw zn|%CAA~ z)-#)}K_D8&h-fh-OR2Z`K7pRrb7lh|am{EHhfbpuHxLu=5&Frhxb;0Xq64-BHDSqC zQWQFb$w4$M!*CD7HFd;PX_8Z#b;tSlfyo$8w8uHf>uXGUeEX9M-EG)pT))CI-XDCx znyNLZ;qX+h2APf=0ZVxa9uItN89k)ylGHP=c+?;)K=Z-_bzZ+H(MNEnSgX)h^o+SI=B)IKN0hTG&yMk?rNMEd$-1?P z2yx#5S{-L7eEcCcWiO`+NZ0@>FMIwzW=2w$Agrj!U1=-v1-fMf1*a}I3Ni(rmlWDy z*3FW6ugixyny_9LiozEP{Et9OHh`MCTHi+`9|Xmiz5`?%04Ae!RO)(pP>sC1dIO*( ztvds~zSwhKaJWq&=Juzu&sIa2l9|Lqy_er0txoe?PjC4KV;9SW#N~u&SGYY|#Q@dp z=YTaaHU=^F)RlQ_5;PqWnk0YT0Xy&T%O|YJ=Xg8bEFXCC=%sZ?w$K$84pU>1A_XnB z`8NsX2kTJY)b~g)TZ^a11{Rf}9!(LWDi^{lW%lgt@&6qz)y&}d2q9CPu z?6U$O5$RB|c|xL9^i=^<)_u;Sf(o$C-dH~9qn#Wkr1gpCt;uocyH&!gGm6yG5wR3a zZF4;?47h&)U4Aiz%a<5#5r->TSa%1lsxrpC+{yz}0B%s@mSQ%9uzZsN0Mnsr2%X+f zXu1U{?g7X#!oQ~rR4LRMTa7KP?h zcMN89hpUH>)7>1dWQ|?AzzmCmIZ!W;X{sIGI~6BiNW=}GTs@!P6tjWYZ>Se7OOy+- z0U>nrO^Q;h!zN!g*+fNK9JXqMYESt(1N8n7%SZ_UxqC1JvBB`#Z`E7sJiudB+n#U- z(*wSrJeIn(!1eRVPJmODZtdWXUl5z7q;Zo4r!5vqt@@1^kdb@=e&Sxm(+_5fS|l~{ z)l?oj^a+q2-<;ev&GWBFSN37*Mv*V))@d%*2oL&GeGm~b_~HTKVFv($`!fj&8MZ4CDBUWV2`f}z#% zx@~z}s8h062qpsT&D>NeY;!Inpe4e*&E?lNV^a1uzBv#)pmd4nk@KbRW%XM2B{V3I zJYd=3z;C;P{{c*YdW`;~>}E--GH7myf3eP|79bxO;E^8`?0-tbQVF!3z%bQgqlu@r zCdu=nm@~?X$kdU`F(WSAn{!_!_s76oouh?sWE?rtdas&c#mlue^7{Ei>dbeIf|>pR z;eN;cFuUv|N8Dg^JAqrp&)kF2{wCT6~ScLQ|i?n1T{B?o?~I7K#S zWWe3129r+)h*Ek^s#4~qEPxH6Vh}{boZ(0fHBmp{%8m%-`qvk{a;DUQ85|bS>Dhxx zLwk`SNug`e`%rt@AA;1|--VmB{RLg)XHW@dw^`u zo2kvn@IHb~u1bJPo|Z4Qdxui-3Y( zgI7O!uOD`3fi(3|10_Z?6=JG!H<^}-c&|7YAJc>x9RybC>gDVEaPV^N>mP<-o)#GJ z017>%V;21`y_%;=vwR6%Xh&&4fa>-R&Vmi7&N*g7q?K5YfcAC8ILjIb2~wQMF4XwC*qJrr%;SN)0EZv9@PKgb zgL$u5X&oDJe=0XztH2=V4oo|Bk;(*2{1}`N+EzgF^8orR`T+WzjZ^(-DWjFSCr#Z= z1Q2p<5R_0Ug^M+J54BhW&cSs*h;y{H`b4?_-AZQOLdQG_ZxJV${ z;SXu#=*oy8LP4*9u#{+Y0qME7ffOxP2UExk)82rlv;6@cC3942m!m|td_T%611oup zM>~8grzBP40UrlZTR$bP;nwXk&szD<>aqOdn)GVR2Cbowwno_XGp1WmcdHqq}`VG3H89$hy`5urmc{LQR zqVY)+w+)J*$?B-a7EA)tMB_FKJdW}O@rL6NXI(m+3Sj?iMcn;C1gJ9*9_hi~1VS+F zx?IrwocWX-IT>XHXg@RGm7hd0L$Gi=qQE^ePUfJ2VxH`h_73qnMJb&LMKduZ&5sUr z$SFzx%u^ir*62^%ZaQ@{f)#Q@r-TJ2754h^b1=QNOZUBRObj}~PBJDB54MVhB^BGx4 z)igIJkQT$?OuS&SfXr82Cgd9EE(&uMBKScsw8{S>)iN`@Lp6Ko@;{jXGPIb&1>q0b z)nfKcnhYdmf2@8I}T#hG8~C1%D(TgU?)xc3jREeB&idfC60^~LlT zS|&N9*&JY@V20)|Ux9>x(2Z;N>Nf$3_a$&C{qXI676b>^PdyE!mks){L8mroUXcpY z*$utjL869yU4TgmDda~4#;te7m~(w25jUtG!Tc6zM~0qff5Ni7-k0l7JMu?m+7J&m zkxG0|mGc#x7c|QMgx7q}8@^X3x!uuM=OEQhdJCi{SdiDtM`GnilzjLoono@U#Ul%A zictrPQ)!0dGYKbv6h?U@KLHBf^z#Hk{ z{qzZZR%FS1`K1}WwqfX-hh=w{-lxf7^o!*I{%VnldHu&mx_jvj@ym&8Ku8fhf)Tvk zIs{!O*wB`YIFM+>s#3(Mqo81;euHIj0pn*&p^?coz^&+&IS%xtlrBzkeB=Yq{zk=|OAH+EB7*L%Tz= z;3?ltadaH8%}igA&gDp382XnXIK?M!tmj~1#1LMw_Yq+^J#kNQRfZ9iDAPSQkSxx& zVo3usEVEW2Gths~7EBpw?W50N)Rr4EAAXDZ;R>V&lzQF*eZ*VtDoKuKN!B0m-#Jj2 zi!&IT$FY;OkA+9N4^Z}Ewe{X6FsMuIYLR#WGFk&LxYd&M(A#Z1V)hul%kgaedhqDgze%zyEZxBZF17VChc|dM*yejasHK=Mz&Ol(^?02`>{7nOQS}@Z zLb}X&NwIh2rk61{%oOqNd3ep^zbvVyP(_g4zt<4Wa#QBIKRnWpgt4{0wDk6l{^urQ z?=QsOkthdLf?7~iK26Sdsgd;QMDH8@exRu!sKa7~THo&v#NNV*E6bDwx%-hl3=SZU zoXX9i9wLgpjQ3qSW#ZFm^alD;9pJn+kQKzQ`6!uqEhq?)HgoUqF)75{Zn`mBu#V%E zhJ!hULhI}hh9A(BN$+^gfe;32nh0t=mtsUMAC()-8Q|K&QWmqrR|igpt?3f6uCa*Z?+JM{|_#vmyp=;kHmH#FV^{XLYJ(ma<+ zc>&9%ho@Zuvf<}j#uBXaR6!g|ax%F_(e}^a)Se;rzwM0{c42ALAJ`2A>4yBAtJT-o0JCu>@LYAOyV!Hx{eu|zGL%7{41kA#2; zN?b0_Sj6!r*k2%B9ha7dM^;7&WS})Nmt@JIcpVhDACrVDgi>Z~{U++}>hguRF5`f7 zkevEG?!z)xun2YTy38?q0U|_0#1wbXSDlxhxX7!2)Y^|$sb4)VQaNACrl>*%I0!nd z+-AG&fC1C3ik{Rfd8s`Hlt6Sj4@Fb}FUCS&$zU81`VY|7Vkb4nXjmSB9u4dTQ64-? z$`B4PyDrYQOn~yYiZPzG{?nvOk%XWxr+XE4y<=bTB2&Et0bPnrU)>+4M4s#(?epj? zrZt=#aOic1lUkam^nzZ|2Q5;e21Vpier5OXw175ip-76`TjUVjc5^a(FK6AA5s;9P z4p~eG+3YAE`my1oE6DCFN#bw(v!Z=pL45?3xj*RjCo7T>ub0Z$74OXw9CO|arO&Tf$KklqOyaT$6U_rGh zVsdbf06{SU+vzu`V+HsjaSrG(6iU($c+%z*YsfWd_eAEfIz%Sy)F(WpySbSZ;$i_6 z^tguQKnswUUDdxN#hAa&w;igZkI%2lj7G-t0SXQD-<+^JU6|0;@0iOMW2Cy1N>W*v zPDEM`(miE>zv=!YQ5y|atRUrz8u0_D3q)+^dl*{d0#ARjrYRPD068|EbjBan^}E*r z#o>O9(`*&}d3~$=&OaV5ASMI}M+v>^VJePyfe$AS)Ss4vwH{6fH^{$R^8f%C!&iMc zF@pfZES8c&(!l`wsW(?!2@L?iG8kBVaWf7J1Ep1>c*7hA{h;?-q{Iloqp6Z8k;KqE^Spc9 zf54nbHWqRg>v&3jxFJ89F_t=z4bM|$H`uz=;nZ*dz<$4T1p-$jEsjDFWs4fDchr0$ zOA;g+(1)H5O6DwTf!;)Xu~b*d`0~06+l#05b{*^zXOv0wO>#Lx`gc*y+)nn`jGE4mk}}681^5f3D8g zyGL^Ix)rdS>oowZbXQhpQ3?a&8uq@N5eZ1Pj|3eiF#%0zgX>Wgl>m)LP**1L^#l}` zw-yRy5}-F{;JWo2@@ zW!5x{#;H)f=zuas8MM-M!4eMOUrL^!eC8%I#-o(@mObueQw{)76o-Sf85><+cSR&I zs-E`T#Z&qaoecW)HfYcKoVdyt!@Cjhg>VT;9y3!#dRLHNfiyCqD=wSF3Z8;#Z>JW- zzfs0LP+CAc@Y5TK|FRqnF9-xWb%W?fo#e)>Qbqh4YJ=mGSr;Dat~DPDyXRgkOf zb`_BZl6KwMEFC!bHB(RTjN}tTK9uzrC@~yuw60%g`J;egX|m6F)E));Cu}#LP5()| zB|k{{0M@&Og91yO13+dPKrinZGX&{(m+~G(+kpIEIi_Wocaep7A7ayHSpHZ}P%t{i znQBAR8_JK{8vm|9w$T!2RT2!}^Zv++OHiPMI<71^+?zr)axkyqrb|HC;Gozi*nhYH zQFU0m15#zN zodKjHQ56#^gQ(B&wPz@0&;KN~hBpD&!$kQ{0bl?B2LCLmbN zc*P{yiw>)GW-z3es3kq%c}jGxOMOpt;_&|wlxZ}WqQrz>B7+baF@pWS%E@9xjipIdNzpVE@R!1+vW2aT{R1ft zLv=yr`~?Ax8Km|g(DQx)zfS5V(1zW({@Fwp_+;hLs0^kN&_{u%%jW@V zE%{50-Ung3n`QvvdZzRhCK+js?gN_KVF$x*tVy3`aWeIT>17_rGB|h1An=e#$R@?e zo)*69m8hpN4C0$^xBM}~%-gX;Cpu-SV{{WT^giM<7)aqHm?6&^}!BZfl zU-&Lwg+4z(9SBwdWVk&NAx&z2q=L*pi6=0o75O6(UPRuUA0gpi>mUOs{Ba|rRn=|( zyS_&*{n{yD_3vLdIHCQi@K%h)Y(1+$UL@o4o3pE22GYRG_;aRY{Y^j{L4p1n;>#f{ z{eUc=g^03s3;)8Cr{jUj1{2`@y-St~kouPv$Ig!{uO?Iqr>c~Ay1yw*rCR_8Qq9Qn zi$1VTvdO%SxoqVyUTLFbToH*}BNd)Hz?GE+OW`j^cxFHsk;LK-=S!fQtw=MBwh9dR zyCCrSJ5I&8QIS#|WG?x)ha$HKK$8*(7-kkp1^UF-Ua0JFm6BucB%w^W_RajdZqqI5 zo&Hv8HxP9fmjJ{pLk%R+xr7e=yo~-pP8qzDJOxE2XS;l8h9h^B>ur#P< zSEZR`gr~#fu)*63h#?yW`oPo#37|ljrMq#+0^tQ!gaI2w*f0TT5((*De9@JyoJN^H zhWY#*9FBSGUk^YLNDq4?m*IV+*Jl`tUVQ!STOH!YN3&3dH zGlpb_s>P034hz!VsYfv-d@p53R)H z+4QSzGx~%u9LF~15{?4FeE5iv!4r4QOuln-9c3ATnnlpDyzUoWuzz$Y6O@q}G>M=} z{P_^Jr=`Pqf`A7CF@~zY1;{7l#+V#2Bx)%XUA9s3Xw3KXHdB-_NB)sBO=!*_82v`1 z{VNWE4$LoieS`TpP1oBNQCx(>k3caZIZVAcq2B82W49&ug;Y1~|`#1;k=PvmQlY zHYV#=fNaWxZJ^MLbD>??AjODIePasb9&emdUGO*RzKUaGEZMYR?Ya~ESm4z%Nd7ui zSdy$P#tFmaice_nyW6M9z&EsM5jExfBZpd3(T={i{X`!$#Ye7vnUE}g#DF{LkLf#J z4QbXy$^M-mg&h~M=wSc-S3r6FF!cwg`Mz5Mcm~YWPZL07jB4?k&L6EM;=(WGW%^n) zqZ?s3{}~?>v=#?;iB#JISfm8FO`LC-!(0ceR5F471+wMX3fG$DfQ*niTPCQUHSPsF zroBqq6Xwud4N-ll3(}TFb_-5*WjLVSJ&&^jbB=1G z{Uu0=Qx*h(NAz8k7oeo~@TvD>bj;>p&c7SqLo_hkEL`#}z|=5C&C@ZskIY9Yi%6@u zM`yDjf%_$kK_zGi3JdbfTNU2KxK|K`qlc=)85k;$?cN7s2VMR35GGG6rg>lCuME)t z%HM@RkF5s2sK>!rfEHGL_cwB&bwF3)R)9O{Dgyn9Qqe)!K&Vi}3IK)I3h2uQlG!T& z_pS{_Z@~{w1YziZ7|GXLO4hfw((s*B)ce&O8Yn0}2uX|P!u%RyZM96r9R-)F0=^1x zFyLKxK>KJ>Ly#oI%}C`>FKNvUJ|x=-=(W*l*ng}sRHQytB*JV!fZw2=Ru^WF|I}89 z9*nExw**6#k1g>%Zvu^@ zO@Z+K*7x#k%-dPO8wKVTfOEM$sa-D$gl)aHH{N0j`>Z25qr-I|qfjN0z%hPdZx+(z zGEP5^!7q<^Y)o(&4Ka1JJ9;{5pwMiHl*MSGl*9Oq;=GIXyxt?UL`C0Bt1BI9R9MEPkRlaM~4i1p> zZKLB&?1kI1|9LFOWRg_0Kao=J-9+txe&2IqIBHt}v6klZS;69e_%k%5$w`zRD0D%I zPJk*EFxQ0({Ofr5iJ9cl!IK`s45X;IkU+4zq`|2oVC>>dH1_y5N&Ndx2Fer~IY5ImA!YQ6^o!xq}We3}6zbX$LI ztnySP;KDUUX|LWaRd@qIpc{+G$$)fWa3g1z&Z3I`1HFre0x<<)a5X4-0uJ==xc~Ja!5o5O5#eo&(-fo7|}B8Kz>8Veb?sK{PJYtX(h^*}eAJ(^t_oBD$=%rZ$|K1tC60YH?@Sgi9*Mi@S&P=KW>A>09tqeQ0J z)nI>Tr3Ih^n!f@+)Bg&RKSBy7#ou}=116!I4~cD+!ECI?<{~E0L>OKUngu1`1~R!o zFz=0kmfzqB04lXi9w-}1r5CT%!+9>C91pHrbdwg5CzweTe6u*Hgu)~CbqMmi62RJCot-9DfDw&TH_`rYZ z`KEyra=nuakn=%RT}@@Dz{=EB;5Q9|PPL3zL3|Y$8z>__Qr^gw5*_$q)VeUV;=9>) z?;0Kk89)B2_~#0ZpIF5cN4AH>_X5-ZY?$L&$<2k#0VBu=lye{a%OXb^el!?>;B4g@ zZ%#ZZ`2cPjm>1fnADk+Y=i+`VyInx-RBU#u3-d{QG5ZI#r1%w#oFd;KZG|(4&oR7&OS<} zl4Sk82<3_LLu6s04m4mNVCNUad3G`2xFe~`pYH8nU-@@gwKBJ&UVNfL}d1GyCi0ceH zzgQFoFvwBODn4_7thsZ}myL;dGE>UK^Hr`fNM?m@M5IDVe7Dj{;um>%m9LhCQ&zG~ zZPgUK7O>x+Yre=Gk!w7-IjVyzWz}9J zJLFm2?6^&T)X0_(*I3&90f0S0i20eVX%~gTa9CIRBYL^U*7Ms%DScspww+(?K8N&+))PX@JtGsF91w3 z{{Dx;z%K%JKqLR;pr3Uhu&02Zya#l`V>w_=ff>FaAaXU|%|2qJ);&Vp(LzN7Mmthu zx&1t#$La37@%&RnNf+Oqys9i19&{f=OvZ!>s@8x41}E&pI^`-ZNi;NJJ&lek(8^+o62?+cbfR z3Hrh=)&istj`Zh-&jXC{2C&%#0bn5vvc@caX_xU@vVeizXGT4 zqDIi%xZk<0ak;*JImV--G9B7YYfppGIH7-b23vRy0sN~xL9|xSP@ry zIJR2px>CWuH*_b-W2UPP>gE~v8()9V=Wt`7t|UR;U*Z?6eQP_4>=>`c`YvILLC zGLH~M8p##NXIeW3=bns}AByH#lJ-su(XK=jlaB9f;x}P3SCQ8JAGH)@z2GmW@b$}| z&)H^HtyC^nC^xtqv}x5Ax}*k3%)plX(Dz$kUtQ@52*;KL`?23!Gjy78-^G#y%My`1 zc=Zrzy*PH)=X^W$-WUyCq7d<^PeNNlY2YUjX*Rxl`aMF&J?1Mq$+uHFM*Kcw4Gn~f zyPuroh+u#Px4O$?Nl$jn+lD&ZFV%!wx;k_%#u2dJOAnsyoS;>55ubtC7nN4 zZTAoV_Hv!C(DxBtc`@wZzsYB+&3X+gNRRCd3eJ9hf=@OwezP{<|2fw_QzT!eJXxq3 z=+Akx%&Sa!utAsIB5pW`ERF(w{DxsmT$yrY^jbe25O0s{Mp$3;#DnS}_F5t{)4u1y1mc2O#d)!Nc`Ew zK;wTT*k8GRHmnOgB8n99xyx~IHb&UeX@EYlhLxMaRx61-k&obivmrxkAwc4C!R%s6`FD7a?jWQd2n^VZBtooUrbv0&HR zfOoFbPuVYqgr8ebK+pMq48p!2f_COd0A57((Gq2be(CvDd;KtBt=)IUIJRc=c+<16 zak+AXRrmk+gDy-9AagIS5oF+b_X2we<%%(!bWC)P>wqkW|Aq`vI8h{M^MKkE zAlLI{{M{j{q1NSp4w4j-a@>yxY&)9&at{A`)%g*jKFIJMVydLfEg2B##dPqqD9zcj z&J7C3e_Kfx_Y0s4`3qa)ED_unqD}?@40)zp!MZNI{QuJ>UxBE5{cm@r?=kHOI*5<| zpDl+JQdfJ*1Ww^%LgAT*c1}U(8sqE|F4P%N9~a&RqtJhOJjz`CzY^h}M>O`zVq}7_ z^Tn0?k4O;U0F4ue%NRvkA?Ft5{}avA?>7_y#sm2B|Lv>(Eb8C)_TTRd1cV-M|6T<| z1=5TE=l4YlkvvMQbMRcRSO2?s@XX1EVnBOAL!bBk|GN_b&fk4NnBw3g0n=#%3jjrd zf8xadg)je2>i?s2J(uUGPx=4xBMRpcQ@2)~yOzkQqumd8Dgx+T;5)&gUu$;97lCn* zJH#bOA$fp|(8RbbD8aDbdUw<~;+8O&RzD3?|B^Yu%Y)xpLjzRnQ$(JF?yIg7i8ai| z-nH00%m7+G>+sl$KyNth)6z#xxPQ7jUbEtjLmvlTWOKM&Ru$o~^}a~!DR(%7B;fR48!RjSQtJ_<&wF@%Ymx#IeL%1Db$icF)9x*{V(8$|EYxi z|L%PgL-<^fA)`c|dyTIWfjuIyTUu7bvqzm}m?gRAD`}l_di2XT#RmFfxXdcW_4E+T zCqP-CB6a&ps4877NnillcL;%5VeY>bQI?$u%0gSoOnh+}Sgqy18*mt&>KntRn)+hv zj^-xK{;}kQSHa+3z)EbFK^uf0r0mK`qBhe_L1bq?v?T%T0*FprCO_ia;9I0A(Eg|& zP~Xx)%;)^i;J0VMHM)2a@$CfQbNrvarJbj%Jd57-`J6s@WJqJ7_<9rBO4G&?E0dpv z6bH0*bfevu!A@&sc&gNLVx_dszDoR%wQBUO2)$zjJN8QSCTOTqXJAqrdzvt^8H5x6 zfqXO6b&bq*)cnbY%4n(Ohf~WJE#^DrBMiYBC)bH8BlIf1FKh3w?EliAEVc5iI4rr9 zVog*y?P}vT@oHr&BYDMtwR2p%rOo4D@a>C%aLX5m`@gF2HN)S=o(|LR;%T~}7u|6C zwmC+fC{lI(9dN3n^kOTyBzL3)6%6#%F-8_6xg|ZyMh?ySoXjLWi$<#V!H1dEjn*6} zxsK`Iiuy1y*D^Nd>e*4_W}?6Jc!)i3uoT!$8+V*Ug}S4Z&z2yhn`m8MOCR-i;8gmyrOn`vUV!TYu7?S46)Cp2?xBT#7N{QRF zMA@g@Uayv$$ho5U$taKvgB&NNOygdxZGRs9oPQ!y*t33 z&rVq3WHB)-yga&CyFVJxGS$pv5q}!=Qwh_;1o^E6;Q%QVm zJes^taO=rbX^Nx2_^E$=Ewgm@>XVnvY-Ri8t`Dore}F2|n@;^)VR!wKKI`!)kB@e%*t8z41)3(jRxwB2irO=T1Hht13;^s}) z09hTp$enBgY{f*a1%0hG@9&zVpH(OA+x0?L19k?2bVsqc4xesfG1z49tA$s&Q@*QJ zo3KGzFAcwy5~6B-r>!ubPs9gS#u+n~#?@(?RW}vBEse}hkv_Jng`a+$_HtJux5&e= zJz^^}?;$J`QT1%G8ZJ>=<+60{aV-1w#^40tGo-jBs;3*QyWwwl>V9KsRmy~(;_Eox z%^MP4tZGC|HCe`Hhi8dn&>hAC^ro%W#k(qse4=Chcm>hHwQTg=EVXxkkS*>@^0iV>~=;KJ9KrWwBxPvq~0Qhd$B#GWoEhMO6DM*2ly%r2u{ zOLCPKX?C|1{|o?e@-Q=ED=>-%s0LSGVnh|T4*AZxuiPfMVOH~&PA6*aZDor={%q&z zKrf10gFXQnm0zn^7FknTJ8 z-gR_mnX)1T0RXC{ot1hsL+@&}^_8W!$F>Li4C$#V8~Ig+zcSyt+_Yn@jjIuok?s7E z>ACpg7`fCzo~}Z~{_XApNGvhXf+t4) z@%`K1Tlf-%we&=KQ=N~GA7d#knSU~HrHknkuq(;&A0ESV>d77Yb+21-f;Og!D|@ME zzCij*_7jrwv1Ku~V<|?a=u-Ns=XNGrdds^ib@lFk?^Ch$QjH^j$hc0?8?4ajsLcTFJb#*EkAE)5E#A{Gu`B7xsEr0+}U0+KJhh~ z?GHNjbls&SYjIdbAGJC(m}?-Kmq@+#>65|3<}WsyxF#QIBFV3)YC0)BDk4$7S}#=K zcg@$z=*R6Jm-Q1lL|G6oyoMnn122qMACjDqJ#Tx+on-#1S9{(F8Cl${^s1YD13xCF zZ1t9zX=nS=Qqq`-PUEnfHIjwBlm^;7~e8Eb@sE!fs3ngVFS?E3>JsBYF`z^JEY$XpSSRudECn{&Wd}CD&WRTUb?_BLq zFRyH&xCWs3fo!c=bNLVY{*s|xmO|*K{pfi1a)xx~6;h@rnV%=tt zzyiZjg_F~Fqch!|y>UV!yMD*7L0~J$e`{HWt>rtL*?^LBjoRxi4nJrelVS5`f?PSl zXA=rKO=glr(|G2RttN||E?e$D-)*QF=@tzMAf=q2neh7mOvV}rp#k z<{C!pJ|TXre`l^E

0Sw`bZrCCE%9Q@^n$3fV?Y#2oxD940m$jKmL#eqR9P&cg| zgS}1MM@}v-BKA-0l<+!AO^b{X-u+7_dsY2KHbXpjJK1;M3Z`sc4!rZN0v0v4TV;MZ zC)X;-sb9-sebrUXMRts#rAlHhe%*Gqx;*_sQglNMhKHT9s0m!ZIQJTo*xQ_V`tGQE zXx*avJ8HhWk*afA%}BL*k-)@Zc5_8o`CU(s-i{=#8ULqc)#C4Rb!){4L+=Y#J9dm< z*^`wCnHM@QF5VIBdS6(x(x?@*FK8%4d730Gr=nb-9XMO?)Giu6X`uwomGwQHVO6H4 z2&6O6IPGJBiXJLm?iVYRO2SxnoI^L2eAe8VV;cW`(`>f7(!=ch=*>$7o#-g}p0vAE zxP2G04i)+=D}`C5FmPx3ZAHYcZVk<2b>fd?fjiR^)Z04+l`{E)0gGol-H+i~T!Oxr zR==}@6{Xj?fQe9n4Pq?Z(saU=y;Z$}+Gy;Q#yq%yFMsmz#J zvsaeo)Q4l5m`rO}*KO6Da3u&^Jg$!on$#d(a%fs)7lEAAy`y|w`hAGdbbDK;BfN1O z?$Vr1T3K*~f_*IpTGgQ3pPw_d6bDZ|5*+ujoTyIMsPUlTy|@y@iK+ zM~*xTzx@reqngo&scTtZ?E0%)36diT+4;4(S7(qZIfLU8Q`FA-o%hpBwQWd^uxh;v zQ=^^_e|#(=9-nkd=&?uUl$a9}yA~?I^+ovRX4LjDA0LgwgmNwAONKI44Ii;>hwh&- zFTA8vWPOv{xz~Ps8>tP54nKP-a?9MYP-DOjaV+-YP0c+}ZF;^zBbexKUf~Y10y^Tn zru+#}@A<;CYm7g~3NH5x+%TCNQfZ*5OQcFRs}+gy~c zEU#W|E~f@la2=)zQ(t4%xqPg289%+nyb<16Ixwj}*Fomwg_RPs&h0vd&T{|QGMZ9z z8gWAJl3ISM85m90P?({cQgPVxS(0-9v)eCYm0Y+0>Y@3Yf!(`yQ}n4hb|9q8gr z4^*xeOw#}IvFylfmc^K(7GrQg8@YBcnV`+=y&f88mQOE4r{`U+y8X_+-K;GxL%A#A zG7qtAS=kz87vkPKj!Vppc6BCU9}6<1Fg3qnMdTCk#$|LWx0&6W8zkI7d_)WThGn&K zcbr7g++@XA_VNi?lk6Vbdj?k| zIS5{2ciIgfs*it=E?HbMjLOgm!tPVXlr?y(vDR<%h5o^_p#l1D3e#_cJg-L!je8AN zvf*K>uwu1+(lcBA-n*jzOtv9rb*jX?Hs0C14l`2pp$NFXP;h zwtH=}PB(@0TzTSJSlR5n{jfjLJp0CR~xx(vnR zoj6#0G)f00_*COrOH{U`zKk(?J=6U8qUhT_e43XN?v@SqTRZ~BnFp!&!t9Wy z#tMh5;Z*d?4TXv~X|=1@+2fR$c^^qG%D4}yzpT6#>^mj6DQ>36e&yb31GmJpoF>Be z?Q?|~ZzUa+XFX=W)#TL@sNB+fHQ&JB*0kHUpzaxP6c0)_Ozem4M6}r^4i+Bl^!9uR zrk5?hzm!+#l4R15<>U!{FJY2YTplehd~U;WsFhH63SPF_G|V49%u2b;xkvqHt_ zZUz_fKDBxf_5|xky4UKLg3Xh%Qf_m&5hL5^UfSMey`lF_@t%sFYuvy!TTYq1^u^2o zB5L9?O^L9q+lI8EZRT_~VLkB&;&0{~mRRjWZ27pki@$r^HQ|zoS3<@VWim#u#g_W56#+o;)~7k}k2`+wPsapUUzoQ8+HGh-TP0(GtC-tQ}}iY{IQ zzwK9-%E~6n+FO^Axo_mL&QbYrVRnH(FB_XtJ2GO{`xTRxn8H;)-Ma zY?_N(U?(lKslZ;Pf54SS%PiCNfu4pxs4gZB37YHSBERofBH)wR0y*rxrR z`JE5tmv|^^o3u{*f(6%ixhgjfbFDR>Qm+pA{viDvnv?O;3D>o0@j2cyZ^KGAy@s5) z&D8g8$ZdwwJqrT%VP%8)pGVN$RXT!N(l0AU55u@a5o7-M8XX4DF8&MmY4lCR=cS1go+lV9u0#w&n#ocS|b>hay)~65s8p1ogzH=Qh4$`1O1m^)pE+CqZ{MAv30RZ z2Xz7||PhMN=UU8*F(f9OQ3UJd1c| z28tLwJ?SD!p5BKMlg!baow-A;Xi)HmaB*LvxAB9AA{&M3jg@TBp90#kmXUk#?;J?R zxF9N>J0wZ(N}jl-e-j|L6?^vuN(xcG^}z_?4OWV{`2`#nNlqIjJ6Grv0(?(+;kyoA zN^b>vQ)s^sGC!sY8Buzl4AMCxJcL=8D81tV&#e;{*-k?3vW?_y5#+V?-!7X!_Ro+^ z{RXYieJso79Q)h=VW{^s1|Dc!{uNqeL>BYFv@lx+pShK<pUoR*shuZ4sRmPpwsYn%P{cI)x%Xhy<`jFq#ej$TUMm^~Fgu<0oI z*`g0)rX7zsK5WFjD=q+ASNbuC{h4pTq&;*i!Bdx)-Jidp8A*Np9_;=ulLc$Wr>Yn5 z%=@r`Ww?gLsz<@t6h9(aVzCVNN@Z>tF?`&d&bR7GgZQ1A z>#>T_I+$b6$RIY{WAt_lMM(~gA7a4(wdWK4sV);9OP_N|4*Sg{bu`_xuOp>o-#DR! zA|iBb{+bHak$}Xg(Jcv*rh+T+*w-0fe0_3t@QQbPEB_-%vIT02xHvt+xx zp`|nvIUXaI31iuj#_dm4{hK3RgiPd{qJKE#`Pq@DEgkK-MwSAVt=V&J@V}nl^1OJ3 zfHJ_9|927LNp+CFAnidt-VR$WD1}WXdGlydaBE6piEL!OuayS3ZGbci#vFH}f{3*N zJIPTgkt(Im$B{%erk6-`k?ZN>+zcxN4a|ApyxL&>Q0;GT_PDVZo>FV3eiu>eyk9BX zxY16WFni<5s@!6C`rvH_`~e$V{h&dHEE~J=@#pROuMcZ5w=u3a?@(zV(wy^jb{AA; zMi==~f45uSKd$V5Ou2iX0ssA-A@YZHVK&<%>zrF{2kOH`wQ$rL2#YuD>o0{q`=upw=XeVXE}yZ)JULw=%*yhilo%aMA1I0b2UYUs{)0 z-xG4H>`(~Yrm^BDaj;?{e`?ed z!Fby=az0^^v~g{mlpBm~F8Q?@y_?ucHiW_iWq||1jq~fY7CLl`X99x`#j6}L#&9Cd zG_$sYyepIq6(qC4smFsUOZD1j<#Ha5lsd=ax{n=ev4Clivh|^%jZu9B!)W$ZWXJJejvMli0J~1Td(N)8b_k0X z=m(gm9<#hW3rnC#$F9y4gWj5k22SrBCUjRu+-`J+bLscnQ3^XZBuefY)9#u{pBct7 zw^IHM`NH%JDBY1I-^TPR{U)fxo$Dd1Or&}k1HKsqz`k88N%)+09B*YRcw5q&`vWV_ zu%LJ{)Kl6hq?gX`sE7k2%{l2q-|uR_!*t8V{^mLCyZSviDpI6EvyWSEf>lz==vMdO zSgT!!VF?hWX0{?7qvF)R7y@T(WM{0g92VgjfsiD1TfXatujINei5r)M%sIA!NVR3Z zD}FVRs=Wxdvb6jC<8HNJ=^K6OS%opToU zktz|4N;S?f9N}dB! z^shy_?vtS`p`u^p^kb-6IQ5YsFh%Y|$YEL5{5pYOeL z)}ITDv}A>x%I-IaAf>LvC(37`o)a+SWpqVgu$x_I*^STv^g{Mb-E3B1>Dr*M52oh0 zF{s&FUuDcOe9*3tz3O}KkQ{jB-LD5G&l8uh%e=MEQR(&81_Nr=im1qa=*WH}xaepB zV*$61x(X)DBt5YRGI1Y;LOey9moUo-4hk#i10pUy;2z&myoaFR1j9+*m_F7~UUFIm zuC?T7Oo==vl8hP{Xaj5Pw||Pvbc+&l>uSY_9G2ACWxLNDHmdxAOeCd>)w1uYUu_(H@yhG7*Jco2r_J`#a_9BeQRb@wc)2m)=OwR79gXEY&_w{yi0u3*^$3_|` zWNYaeyYGneyI~A~QyTp+#_;DOkWn%d^Bz*hfuM5B81Iepir@@)6X!c`CUl!tW=e+n z<|Wu(p{3#jPIQX|Jaw~?`{S@L0&zYM59x)tByRXc8)yYmSMVarIr-cU2&~007jO1$ zrR0~@TTa4v9#c{FQM&a3A%37FoDv~7$Lb=cXMw;tr~-`l!b0Ovmv7@22&i^l;1>}d zdHt#Jl+KN3a+a=ws$b_!Uhj|V&dKE(D@rka%>6JAIIt5e!r`>5o9L4hIJeNXu7i4s zco_0fI?R5UM5)m2Bm6q|P8RCSe{6~l>o`_aROjmtxH z-m=N|*QIE(y^x~!H|AqGW;D6puy@6^T#$Vm6B1ScWX;~)p1czukjDr30@tZ6b#%R0cgY_)MGWoO2g?07Jg?Cx8p( zzZ&4hu?YX3rwCC3u2N;eF1Ta;Wuq*>sQ8L`4P>{yyTLy)&&KfD+eehq-Meqs9W;@~I`*Ik)O3kBV zy!KH?^WN%Gr{9RHti_)JL*cmWi@kWaYOQp4Pu{3-Z~zI2TG2EZ6>{yhLzITkZ1StoO3t zBg~8=x*CnAG4R9CL!DIBch07aH`#uzP?lQp*q{ac!Z)e!0n70@YI74^miCyColVxi zLrGkH_|MH)7i%&DI)!qeSqkt`PR@;9RjnL=B_P zfy(j|F^tq-SRhw9Y=3F?!R`R=%SvzJ>jS0o{fb$wv?5Wis|eSv;}x`B?sg z8?hXVt*joA8g4Tk8R2b(@1`-zcLT4^^o`Rs@XL*T%Fkzt%C{`?yoM?bjA!VF)ZoGB z{mpPi%cL1P=VMiU2E>+4It5iWp&fkN0M@)*j)Gsl(23YfV$> zD?x&Q^dRE8TlJT58C`X?Sm(DN>__kq{5JH}p)>DvON?SjPr#4Ii-v2GYaS8n?^}i7 zCQqz-uktCg_%n4d4Io=`xYHnwG7BsNx$VKPDqyQS9Cn8t&wu^kTugeNsPpQXb!!j^ zY>1tmwZ;DfdDE$ZA&td>^Fu->sGLg9TBe7hm-8scYYf%#5lS?=omFzU*Kr1lxS#)2 zV_KGAfe+TEqM~kw!=M%v1=+`E6bq348uY3+FFN|y58+nSjH|(mLk@P&vEAeiz~n1& zNVIZ<_W0MVL-8+>6Wy@34D>&hXF65+uDP^dlTnG-bG)F&q%y`esPPK)@46|zC11`8nIPsaQqH>x$E+{9w@_I)+Oklg4{n{`{>>b z{mK7(sxhX1*UW$oo+LA;Un{n|Wq`jE(E4d7U#c-KHZeA>xFt zhb<>`WnuZ0C^(w9)N@Up<&N|&3G!MCRvmn*w6K#1eaV$RoC$m3|B!!dDcp9by d|ChmM4mJPqQe8(E6Vs_E0oOKymECZR_zyqN|GxkL literal 0 HcmV?d00001 diff --git a/src/antenna/model/circular-aperture-antenna-model.cc b/src/antenna/model/circular-aperture-antenna-model.cc new file mode 100644 index 000000000..e409a613a --- /dev/null +++ b/src/antenna/model/circular-aperture-antenna-model.cc @@ -0,0 +1,193 @@ +/* + * Copyright (c) 2022 University of Padova, Dep. of Information Engineering, SIGNET lab. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation; + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Author: Mattia Sandri + */ + +#include "circular-aperture-antenna-model.h" + +// The host system uses Clang libc++, which does not support the Mathematical special functions +// (P0226R1) and Boost's implementation of cyl_bessel_j has been found. +#ifdef NEED_AND_HAVE_BOOST_BESSEL_FUNC +#include +#endif + +#include "antenna-model.h" + +#include +#include + +#include + +/** + * \file + * \ingroup antenna + * Class CircularApertureAntennaModel implementation. + */ + +namespace +{ +constexpr double C = 299792458.0; ///< speed of light in vacuum, in m/s +} // namespace + +namespace ns3 +{ +NS_LOG_COMPONENT_DEFINE("CircularApertureAntennaModel"); + +NS_OBJECT_ENSURE_REGISTERED(CircularApertureAntennaModel); + +TypeId +CircularApertureAntennaModel::GetTypeId() +{ + static TypeId tid = + TypeId("ns3::CircularApertureAntennaModel") + .SetParent() + .SetGroupName("Antenna") + .AddConstructor() + .AddAttribute("AntennaCircularApertureRadius", + "The radius of the aperture of the antenna, in meters", + DoubleValue(0.5), + MakeDoubleAccessor(&CircularApertureAntennaModel::SetApertureRadius), + MakeDoubleChecker(0.0)) + .AddAttribute("OperatingFrequency", + "The operating frequency in Hz of the antenna", + DoubleValue(2e9), + MakeDoubleAccessor(&CircularApertureAntennaModel::SetOperatingFrequency), + MakeDoubleChecker(0.0)) + .AddAttribute("AntennaMinGainDb", + "The minimum gain value in dB of the antenna", + DoubleValue(-100.0), + MakeDoubleAccessor(&CircularApertureAntennaModel::SetMinGain), + MakeDoubleChecker()) + .AddAttribute("AntennaMaxGainDb", + "The maximum gain value in dB of the antenna", + DoubleValue(1), + MakeDoubleAccessor(&CircularApertureAntennaModel::SetMaxGain), + MakeDoubleChecker(0.0)); + return tid; +} + +void +CircularApertureAntennaModel::SetApertureRadius(double aMeter) +{ + NS_LOG_FUNCTION(this << aMeter); + NS_ASSERT_MSG(aMeter > 0, "Setting invalid aperture radius: " << aMeter); + m_apertureRadiusMeter = aMeter; +} + +double +CircularApertureAntennaModel::GetApertureRadius() const +{ + return m_apertureRadiusMeter; +} + +void +CircularApertureAntennaModel::SetOperatingFrequency(double freqHz) +{ + NS_LOG_FUNCTION(this << freqHz); + NS_ASSERT_MSG(freqHz > 0, "Setting invalid operating frequency: " << freqHz); + m_operatingFrequencyHz = freqHz; +} + +double +CircularApertureAntennaModel::GetOperatingFrequency() const +{ + return m_operatingFrequencyHz; +} + +void +CircularApertureAntennaModel::SetMaxGain(double gainDb) +{ + NS_LOG_FUNCTION(this << gainDb); + m_maxGain = gainDb; +} + +double +CircularApertureAntennaModel::GetMaxGain() const +{ + return m_maxGain; +} + +void +CircularApertureAntennaModel::SetMinGain(double gainDb) +{ + NS_LOG_FUNCTION(this << gainDb); + m_minGain = gainDb; +} + +double +CircularApertureAntennaModel::GetMinGain() const +{ + return m_minGain; +} + +double +CircularApertureAntennaModel::GetGainDb(Angles a) +{ + NS_LOG_FUNCTION(this << a); + + // In 3GPP TR 38.811 v15.4.0, Section 6.4.1, the gain depends on a single angle only. + // We assume that this angle represents the angle between the vectors corresponding + // to the cartesian coordinates of the provided spherical coordinates, and the spherical + // coordinates (r = 1, azimuth = 0, elevation = PI/2) + double theta1 = a.GetInclination(); + double theta2 = M_PI_2; // reference direction + + // Convert to ISO range: the input azimuth angle phi is in [-pi,pi], + // while the ISO convention for spherical to cartesian coordinates + // assumes phi in [0,2*pi]. + double phi1 = M_PI + a.GetAzimuth(); + double phi2 = M_PI; // reference direction + + // Convert the spherical coordinates of the boresight and the incoming ray + // to Cartesian coordinates + Vector p1(sin(theta1) * cos(phi1), sin(theta1) * sin(phi1), cos(theta1)); + Vector p2(sin(theta2) * cos(phi2), sin(theta2) * sin(phi2), cos(theta2)); + + // Calculate the angle between the antenna boresight and the incoming ray + double theta = acos(p1 * p2); + + double gain = 0; + if (theta == 0) + { + gain = m_maxGain; + } + // return value of std::arccos is in [0, PI] deg + else if (theta >= M_PI_2) + { + // This is an approximation. 3GPP TR 38.811 does not provide indications + // on the antenna field pattern outside its PI degrees FOV. + gain = m_minGain; + } + else // 0 < theta < |PI/2| + { + // 3GPP TR 38.811 v15.4.0, Section 6.4.1 + double k = (2 * M_PI * m_operatingFrequencyHz) / C; + double kasintheta = k * m_apertureRadiusMeter * sin(theta); +// If needed, fall back to Boost cyl_bessel_j +#ifdef NEED_AND_HAVE_BOOST_BESSEL_FUNC + gain = boost::math::cyl_bessel_j(1, kasintheta) / kasintheta; +// Otherwise, use the std implementation +#else + gain = std::cyl_bessel_j(1, kasintheta) / kasintheta; +#endif + gain = 10 * log10(4 * gain * gain) + m_maxGain; + } + + return gain; +} + +} // namespace ns3 diff --git a/src/antenna/model/circular-aperture-antenna-model.h b/src/antenna/model/circular-aperture-antenna-model.h new file mode 100644 index 000000000..84b8feb1a --- /dev/null +++ b/src/antenna/model/circular-aperture-antenna-model.h @@ -0,0 +1,140 @@ +/* + * Copyright (c) 2022 University of Padova, Dep. of Information Engineering, SIGNET lab. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation; + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Author: Mattia Sandri + */ + +#ifndef CIRCULAR_APERTURE_ANTENNA_MODEL_H +#define CIRCULAR_APERTURE_ANTENNA_MODEL_H + +#include "antenna-model.h" + +#include + +/** + * \file + * \ingroup antenna + * Class CircularApertureAntennaModel declaration + */ + +namespace ns3 +{ +/** + * \brief Circular Aperture Antenna Model + * + * This class implements the circular aperture antenna as described in 3GPP 38.811 6.4.1 + * https://www.3gpp.org/ftp/Specs/archive/38_series/38.811 without the cosine approximation, thanks + * to the Bessel functions introduced in C++17. Spherical coordinates are used, in particular of the + * azimuth and inclination angles. All working parameters can be set, namely: operating frequency, + * aperture radius, maximum and minimum gain. + * Since Clang libc++ does not support the Mathematical special functions (P0226R1) yet, this class + * falls back to Boost's implementation of cyl_bessel_j whenever the above standard library is in + * use. If neither is available in the host system, this class is not compiled. + */ +class CircularApertureAntennaModel : public AntennaModel +{ + public: + CircularApertureAntennaModel() = default; + ~CircularApertureAntennaModel() override = default; + + /** + * Register this type. + * \return The object TypeId. + */ + static TypeId GetTypeId(); + + /** + * \brief Set the antenna aperture radius + * + * Sets the antenna operating frequency, asserting that + * the provided value is within the acceptable range [0, +inf[. + * + * \param aMeter the strictly positive antenna radius in meters + */ + void SetApertureRadius(double aMeter); + + /** + * \brief Return the antenna aperture radius + * + * \return the antenna radius in meters + */ + double GetApertureRadius() const; + + /** + * \brief Set the antenna operating frequency. + * + * Sets the antenna operating frequency, asserting that + * the provided value is within the acceptable range [0, +inf[. + * + * \param freqHz the strictly positive antenna operating frequency, in Hz + */ + void SetOperatingFrequency(double freqHz); + + /** + * \brief Return the antenna operating frequency + * + * \return the antenna operating frequency, in Hz + */ + double GetOperatingFrequency() const; + + /** + * \brief Set the antenna max gain + * + * \param gainDb the antenna max gain in dB + */ + void SetMaxGain(double gainDb); + + /** + * \brief Return the antenna max gain + * + * \return the antenna max gain in dB + */ + double GetMaxGain() const; + + /** + * \brief Set the antenna min gain + * + * \param gainDb the antenna min gain in dB + */ + void SetMinGain(double gainDb); + + /** + * \brief Return the antenna min gain + * + * \return the antenna min gain in dB + */ + double GetMinGain() const; + + /** + * \brief Get the gain in dB, using Bessel equation of first kind and first order. + * + * \param a the angle at which the gain need to be calculated with respect to the antenna + * bore sight + * + * \return the antenna gain at the specified Angles a + */ + double GetGainDb(Angles a) override; + + private: + double m_apertureRadiusMeter; //!< antenna aperture radius in meters + double m_operatingFrequencyHz; //!< antenna operating frequency in Hz + double m_maxGain; //!< antenna gain in dB towards the main orientation + double m_minGain; //!< antenna min gain in dB +}; + +} // namespace ns3 + +#endif // CIRCULAR_APERTURE_ANTENNA_MODEL_H diff --git a/src/antenna/test/gen-test-circular-aperture-antenna-reference-points.m b/src/antenna/test/gen-test-circular-aperture-antenna-reference-points.m new file mode 100644 index 000000000..51db047be --- /dev/null +++ b/src/antenna/test/gen-test-circular-aperture-antenna-reference-points.m @@ -0,0 +1,105 @@ +%{ + Compute the antenna gain pattern of the reflector antenna with circular aperture + specified in the Sec. 6.4.1, 3GPP 38.811 v.15.4.0 and generate reference gain values + for the CircularApertureAntennaModelTest. +%} +clc; clearvars; + +%{ + Consider two testing vectors, one with elevation fixed to 90 degrees and + varying azimuth, the other with azimuth fixed to 180 degrees and varying elevation. + The boresight direction, is (az, el) = (180 degrees, 90 degrees) +%} +az_test_fixed_el = 90:10:180; +eL_test_fixed_az = 0:9:90; +% Uncomment line below and comment line below for the fixed elevation test +[theta_vec, az_vec, el_vec] = theta_vec_from_sph_coord_vecs(az_test_fixed_el, 90); +% Uncomment line above and comment line below for the fixed azimuth test +%[theta_vec, az_vec, el_vec] = theta_vec_from_sph_coord_vecs(180, eL_test_fixed_az); + +%{ + The theta angle which represents the input of the pattern formula is + computed as theta = arccos(), where p_i = sin(theta_i) * + cos(phi_i), sin(theta_i) * sin(phi_i), cos(theta_i). The default + boresight is (phi1 (az), theta1(el)) = (180deg, 90deg) +%} + +%{ + 3GPP specifies the antenna gain only for |theta| < 90 degrees. + The gain outside of this region is approximated as min_gain_dB +%} +min_gain_dB = -50; + +max_gain_dB = 0; +c=physconst('LightSpeed'); +f=28e9; % operating frequency +k=2*pi*f/c; % wave number +a=10*c/f; % radius antenna aperture +arg_vec=k*a*sind(theta_vec); +pattern=besselj(1, arg_vec)./arg_vec; +pattern= 4*(abs(pattern).^2); + +% Manually set gain for theta = 0 +where_arg_zero=find(~theta_vec); +pattern(where_arg_zero)=1; +gain_dB=10*log10(pattern) + max_gain_dB; + +% Manually set gain to minimum gain for |theta| >= 90 degrees +where_arg_outside_pattern_domain = find(abs(theta_vec) >= 90); +gain_dB(where_arg_outside_pattern_domain)=min_gain_dB; + +%{ + Plot and output the C++ code which defines the reference vector of data points. + Test points are assumed to be encoded as C++ structs +%} +scatter(theta_vec, gain_dB); +out_str = ns3_output_from_vecs(az_vec, el_vec, gain_dB, max_gain_dB, min_gain_dB, a, f); + + +function theta = theta_from_sph_coord(phi1, theta1) +% theta_from_sph_coord Computes theta (the input of the radiation pattern formula) +% from the azimuth and elevation angles with resepct to boresight. +% phi1 the azimuth with respect to boresight +% theta1 the elevation angle with respect to boresight + + p1 = [sind(theta1)*cosd(phi1), sind(theta1)*sind(phi1), cosd(theta1)]; + % p2, i.e., the boresight direction, is phi1 (az), theta1(el)) = + % (180deg, 90deg) + theta2 = 90; phi2 = 180; + p2 = [sind(theta2)*cosd(phi2), sind(theta2)*sind(phi2), cosd(theta2)]; + theta = acosd(dot(p1,p2)); +end + +function [theta_vec, az_vec, el_vec] = theta_vec_from_sph_coord_vecs(az_vec, el_vec) +% theta_vec_from_sph_coord_vecs Computes the vectors of thetas +% (the inputs of the radiation pattern formula) +% from vectors of azimuth and elevation angles with resepct to boresight. +% phi1 the vector of azimuths with respect to boresight +% theta1 the vector of elevation angles with respect to boresight + if size(az_vec, 2) > 1 + el_vec = repelem(el_vec, size(az_vec, 2)); + elseif size(el_vec, 2) > 1 + az_vec = repelem(az_vec, size(el_vec, 2)); + end + theta_vec = arrayfun(@theta_from_sph_coord, az_vec, el_vec); +end + +function out_str = ns3_output_from_vecs(az_vec, el_vec, gain_dB_vec, max_gain, min_gain, a, f) +% ns3_output_from_vecs Outputs the reference gain values +% as a vector of vectors. +% The entries of the inner vectors, representing a single test point, +% represent: (gain at boresight (dB), gain outside the 3GPP pattern region (dB), +% aperture (meters), carrier frequency (Hz), test azimuth (degrees), +% test elevation angle (degrees), reference antenna gain (dB)) + out_str = ''; + for i=1:size(el_vec, 2) + out_str = [out_str, '{', ... + num2str(max_gain), ',', ... + num2str(min_gain), ',', ... + num2str(a), ',', ... + num2str(f), ',', ... + num2str(az_vec(i) - 180), ',', ... + num2str(el_vec(i)), ',', ... + num2str(gain_dB_vec(i)), '},']; + end +end diff --git a/src/antenna/test/test-circular-aperture-antenna.cc b/src/antenna/test/test-circular-aperture-antenna.cc new file mode 100644 index 000000000..e6cfa9377 --- /dev/null +++ b/src/antenna/test/test-circular-aperture-antenna.cc @@ -0,0 +1,242 @@ +/* + * Copyright (c) 2023 University of Padova, Dep. of Information Engineering, SIGNET lab. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation; + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "ns3/circular-aperture-antenna-model.h" +#include "ns3/double.h" +#include "ns3/log.h" +#include "ns3/pointer.h" +#include "ns3/simulator.h" +#include "ns3/test.h" +#include "ns3/uinteger.h" +#include "ns3/uniform-planar-array.h" + +#include +#include +#include +#include + +using namespace ns3; + +NS_LOG_COMPONENT_DEFINE("TestCircularApertureAntennaModel"); + +/** + * \ingroup antenna-tests + * + * \brief CircularApertureAntennaModel Test Case + * + * Note: Since Clang libc++ does not support the Mathematical special functions (P0226R1) yet, this + * class falls back to Boost's implementation of cyl_bessel_j whenever the above standard library is + * in use. If neither is available in the host system, this class is not compiled. + */ +class CircularApertureAntennaModelTestCase : public TestCase +{ + public: + CircularApertureAntennaModelTestCase(); + + /** + * \brief Description of a single test point + * + * Description of a test point, which is characterized + * by the CircularApertureAntennaModel parameters, + * the directions towards which the antenna gain + * is to be tested, and the expected gain value. + */ + struct TestPoint + { + /** + * @brief Constructor + * + * @param antennaMaxGainDb the antenna maximum possible gain [dB] + * @param antennaMinGainDb the antenna minimum possible gain [dB] + * @param antennaCircularApertureRadius the radius of the parabolic aperture [m] + * @param operatingFrequency operating frequency [Hz] + * @param testAzimuth test azimuth [rad] + * @param testInclination test inclination [rad] + * @param expectedGain the expected gain value [dB] + */ + TestPoint(double antennaMaxGainDb, + double antennaMinGainDb, + double antennaCircularApertureRadius, + double operatingFrequency, + double testAzimuth, + double testInclination, + double expectedGain) + : m_antennaMaxGainDb(antennaMaxGainDb), + m_antennaMinGainDb(antennaMinGainDb), + m_antennaCircularApertureRadius(antennaCircularApertureRadius), + m_operatingFrequency(operatingFrequency), + m_testAzimuth(DegreesToRadians(testAzimuth)), + m_testInclination(DegreesToRadians(testInclination)), + m_expectedGain(expectedGain) + { + } + + double m_antennaMaxGainDb; ///< the antenna maximum possible gain [dB] + double m_antennaMinGainDb; ///< the antenna minimum possible gain [dB] + double m_antennaCircularApertureRadius; ///< the radius of the parabolic aperture [m] + double m_operatingFrequency; ///< operating frequency [Hz] + double m_testAzimuth; ///< test azimuth [rad] + double m_testInclination; ///< test inclination [rad] + double m_expectedGain; ///< the expected gain value [dB] + }; + + /** + * Generate a string containing all relevant parameters + * \param testPoint the parameter configuration to be tested + * \return the string containing all relevant parameters + */ + static std::string BuildNameString(TestPoint testPoint); + + /** + * Test the antenna gain for a specific parameter configuration, + * by comparing the antenna gain obtained using CircularApertureAntennaModel::GetGainDb + * and the one obtained using MATLAB. + * + * \param testPoint the parameter configuration to be tested + */ + void TestAntennaGain(TestPoint testPoint); + + private: + /** + * Run the test + */ + void DoRun() override; +}; + +CircularApertureAntennaModelTestCase::CircularApertureAntennaModelTestCase() + : TestCase("Creating CircularApertureAntennaModelTestCase") +{ +} + +std::string +CircularApertureAntennaModelTestCase::BuildNameString(TestPoint testPoint) +{ + std::ostringstream oss; + oss << " Maximum gain=" << testPoint.m_antennaMaxGainDb << "dB" + << " minimum gain=" << testPoint.m_antennaMinGainDb << "dB" + << ", antenna aperture radius=" << testPoint.m_antennaCircularApertureRadius << "m" + << ", frequency" << testPoint.m_operatingFrequency << "Hz" + << ", test inclination=" << RadiansToDegrees(testPoint.m_testInclination) << " deg" + << ", test azimuth=" << RadiansToDegrees(testPoint.m_testAzimuth) << " deg"; + return oss.str(); +} + +void +CircularApertureAntennaModelTestCase::TestAntennaGain(TestPoint testPoint) +{ + Ptr antenna = + CreateObjectWithAttributes( + "AntennaMaxGainDb", + DoubleValue(testPoint.m_antennaMaxGainDb), + "AntennaMinGainDb", + DoubleValue(testPoint.m_antennaMinGainDb), + "AntennaCircularApertureRadius", + DoubleValue(testPoint.m_antennaCircularApertureRadius), + "OperatingFrequency", + DoubleValue(testPoint.m_operatingFrequency)); + + Ptr upa = + CreateObjectWithAttributes("AntennaElement", + PointerValue(antenna), + "NumColumns", + UintegerValue(1), + "NumRows", + UintegerValue(1)); + + auto [fieldPhi, fieldTheta] = + upa->GetElementFieldPattern(Angles(testPoint.m_testAzimuth, testPoint.m_testInclination), + 0); + // Compute the antenna gain as the squared sum of the field pattern components + double gainDb = 10 * log10(fieldPhi * fieldPhi + fieldTheta * fieldTheta); + auto log = BuildNameString(testPoint); + NS_LOG_INFO(log); + NS_TEST_EXPECT_MSG_EQ_TOL(gainDb, testPoint.m_expectedGain, 0.1, log); +} + +void +CircularApertureAntennaModelTestCase::DoRun() +{ + // Vector of test points + std::vector testPoints = { + // MaxGainDb MinGainDb Radius (m) Freq (Hz) Azimuth (deg) Incl (deg) ExpGain (dB) + // Test invariant: gain always equal to max gain at boresight (inclination 90, azimuth = 0) + // for different frequency + {30, -30, 0.5, 2e9, 0, 90, 30}, + {30, -30, 2, 20e9, 0, 90, 30}, + // Test invariant: gain always equal to max gain at boresight (inclination 90, azimuth = 0) + // for different max gain + {20, -30, 0.5, 2e9, 0, 90, 20}, + {10, -30, 2, 20e9, 0, 90, 10}, + // Test invariant: gain always equal to min gain outside of |theta| < 90 deg + // for different frequency + {30, -100, 0.5, 2e9, 0, 0, -100}, + {30, -100, 2, 20e9, 0, 0, -100}, + // Test invariant: gain always equal to min gain outside of |theta| < 90 deg + // for different orientations + {30, -100, 0.5, 2e9, 180, 90, -100}, + {30, -100, 2, 20e9, -180, 90, -100}, + // Fixed elevation to boresight (90deg) and azimuth varying in [-90, 0] deg with steps of 10 + // degrees + {0, -50, 0.10707, 28000000000, -90, 90, -50}, + {0, -50, 0.10707, 28000000000, -80, 90, -49.8022}, + {0, -50, 0.10707, 28000000000, -70, 90, -49.1656}, + {0, -50, 0.10707, 28000000000, -60, 90, -60.9132}, + {0, -50, 0.10707, 28000000000, -50, 90, -59.2368}, + {0, -50, 0.10707, 28000000000, -40, 90, -44.6437}, + {0, -50, 0.10707, 28000000000, -30, 90, -43.9686}, + {0, -50, 0.10707, 28000000000, -20, 90, -36.3048}, + {0, -50, 0.10707, 28000000000, -10, 90, -30.5363}, + {0, -50, 0.10707, 28000000000, 0, 90, 0}, + // Fixed azimuth to boresight (0 deg) and azimuth varying in [0, 90] deg with steps of 9 + // degrees + {0, -50, 0.10707, 28e9, 0, 0, -50}, + {0, -50, 0.10707, 28e9, 0, 9, -49.7256}, + {0, -50, 0.10707, 28e9, 0, 18, -52.9214}, + {0, -50, 0.10707, 28e9, 0, 27, -48.6077}, + {0, -50, 0.10707, 28e9, 0, 36, -60.684}, + {0, -50, 0.10707, 28e9, 0, 45, -55.1468}, + {0, -50, 0.10707, 28e9, 0, 54, -42.9648}, + {0, -50, 0.10707, 28e9, 0, 63, -45.6472}, + {0, -50, 0.10707, 28e9, 0, 72, -48.6378}, + {0, -50, 0.10707, 28e9, 0, 81, -35.1613}, + {0, -50, 0.10707, 28e9, 0, 90, 0}}; + + // Call TestAntennaGain on each test point + for (auto& point : testPoints) + { + TestAntennaGain(point); + } +} + +/** + * \ingroup antenna-tests + * + * \brief UniformPlanarArray Test Suite + */ +class CircularApertureAntennaModelTestSuite : public TestSuite +{ + public: + CircularApertureAntennaModelTestSuite(); +}; + +CircularApertureAntennaModelTestSuite::CircularApertureAntennaModelTestSuite() + : TestSuite("circular-aperture-antenna-test", Type::UNIT) +{ + AddTestCase(new CircularApertureAntennaModelTestCase()); +} + +static CircularApertureAntennaModelTestSuite staticCircularApertureAntennaModelTestSuiteInstance;