From e987deb8e045910595fb289b1f079ed740f849aa Mon Sep 17 00:00:00 2001 From: Jon Beniston Date: Thu, 10 Jun 2021 20:10:19 +0100 Subject: [PATCH] Add support for linear interpolation, which can be more accurate if there's a sharp knee in the curve. Add support for displaying noise floor in results table. --- doc/img/NoiseFigure_plugin_enr.png | Bin 17543 -> 31004 bytes doc/img/NoiseFigure_plugin_results.png | Bin 12689 -> 12776 bytes plugins/channelrx/noisefigure/noisefigure.cpp | 36 +++-- plugins/channelrx/noisefigure/noisefigure.h | 11 +- .../noisefigure/noisefigureenrdialog.cpp | 130 ++++++++++++++++ .../noisefigure/noisefigureenrdialog.h | 9 ++ .../noisefigure/noisefigureenrdialog.ui | 146 +++++++++++++++++- .../channelrx/noisefigure/noisefiguregui.cpp | 5 + .../channelrx/noisefigure/noisefiguregui.h | 3 +- .../channelrx/noisefigure/noisefiguregui.ui | 18 +++ .../noisefigure/noisefiguresettings.cpp | 5 + .../noisefigure/noisefiguresettings.h | 6 +- plugins/channelrx/noisefigure/readme.md | 5 +- sdrbase/CMakeLists.txt | 2 + sdrbase/util/interpolation.cpp | 18 +++ sdrbase/util/interpolation.h | 99 ++++++++++++ 16 files changed, 471 insertions(+), 22 deletions(-) create mode 100644 sdrbase/util/interpolation.cpp create mode 100644 sdrbase/util/interpolation.h diff --git a/doc/img/NoiseFigure_plugin_enr.png b/doc/img/NoiseFigure_plugin_enr.png index 94b75e2edfd7a32a8efbbc55b38831ac11a3273e..ee2c83dff4f518ad29b4537f1730120bf0644f2f 100644 GIT binary patch literal 31004 zcmdRVWmH^kvn7P!!Gl9^3GN;U?oQ+GPH=|+jXMN)cWd0;Ew}{;?hqV;O_TSN_uf0- ztXcDKey}*Lr_XtwUA6bFs!#sm_8&maMvpJ&Qepx$CTU{DoVNDr7-F@h;O1br6wL3j~ z-W@-dD>8+IBLObU*r=51A8z9mBE6U;`omiUhGOF&hp1%zAmG%JhA9pPirI45i9c zm|EPF_x+9G!>yk^Z*GziF>Jq#(76iDLR;beK;-6mTXc83w_d9G^D$GPZ`&#!03USV z^J~NR-1p@khKj)T9nZ_v+jItUFQEXocy}(VTdU-0VL&}^rHM!5%MsV!sVUv0!knUx zYL+`7TD*wXFDWglx1EbVq~As|B>ZwxfT%YZWiXxFQAO6;Aobv8;qXg0H1=JE72=~Q z2$bc#qW8REQXSWL>n3v>!nF91wG;q7dD?FTAj;jo_fEdvhrQc>>0sMD@7r(1-dc5+ zM#Tu#AApPKZ8g_dmD-Hy569j*XFS45FA z2|23Kw{-`H^D&og=(N9h3UBJ>HChlK85l`!NLt|vWz&KZXw^2`E+>@hYeMJDPgv9? zyZD#Jd;|u6`4x5Fj`+@GA>(TtGY>YAdLsJ-5&Aw+JwGiqHtmlQZ3VACR4P`9;|ADY zNcSH2eXMGaa4cj%?d_r7Ho34;7O4)>oW9yR30o|CsU2zC zp~RxUZ%cIy<8r*qfji<^oo>aZMu`M`H8v4!i=yFJ8;M%BitOb3k&N!bR)^HVQ@rxVa^>|gX@sQF# z#dwxh|Gu_wT4|iImk(%4Zb+U^AT9E=Ya9}r6neo_^8gu!R!%TFa8Gp4Li1Z>F?ZVI z7%-@##9l{v5;`R6?(go{X=r6PgfsO$)9RvfSO}Wl4g|-I=?W9i?oi}M#>&6%^MJ83 zJRz-iA*^Eu18|yi2R7MPi|ElshH*Q3KT3o1wvRnPG>8^`>0Cq7jp>Z;N1{87hx5Y+ zWO%5`<>#8zX}N(7liuP4V-BjZZFc511fzG#EDW*E&VED5e*I^+Tkbb40n3 zfbjKelg=Elk5sF{TJZLC74XsJ{3PJ|B+o`#h(#lmZFYM`zUSrY zJ~p=d0~rnS%aPTi`$Ey()bM)$S4Us}D?~Z=fdoAnIFvbA;;MqGx+{&Fp}zfRT1{Qu z+xzJQqYxj|GQ43ALgk*uwI-q=m$C#ql#}$8O;HxR$~2d+%SVYB45r zQ${1JnYQ7pXB*egW;sRga8lX{;DnM#r6sgTh*smG9s60HZ5@_O>qu+El0U(|tay!{ zty^|@3wmGHnVfM%x4%^EY7x1$-meb$mN9#|H@l}N7o=B|JcsMqV)>9ipK=VK?#@O$ zho|XKgx<1kiAb4Md)n7Z9P-%=ca3iGnM7bHBRDzh4&6L(mMyh1C-!Qdu?rmCB_2GD zOE;G5)@j2`x-8mQ6B)HUZ>0`i-$vYCa9Mfs+uZ5s3LOi7uxtF`5Khxi>45ukRU}ZN zByB7cHKSU0DBijQl=Xce@I>*BdhxoUm*aZIxpIDl$dEt3T1OX>5D&5wBL>HR_-6S; zR3VAG|B(UEQt7~H0!|kEsx^>O8jgfRD4qiRft)`H!>Q$058{0!BREySoBlg({GUJc ze|--4`y%ADv^0Bgz}{Rmitc9~{GJ9+LKvPW9*yVw^05Art6U^E@P!&^4Wi0$LhrcW z#rL|NQxoG(PXS*GA6dN2@cAH-9eJvr&wYU+a$t^)nhSk4WxU zNKFYdIL6*}X5W&l90qBL<{wccm7&LL%HUYzm$`)_*=ooO*r;HMg3{ZWOO)iQv{ch9 zYQ04yR*Z}&TQMCO+EB577gtLKK27oBN?KO0F4n(W!p0(9%nb^pKf<)e=gj$NVI70g zKv~{0%=09x9?I-Sb61qZ+0H1g8K<3TO}A2Tm>Wp=5FW#k>)nEAyh{RV8Iu$xg;HFK zk9`gqJ1k+AkV2FsM(D=&5w;~GS)IwB$>11ZA%*R z@3QR*nI{Ua``{c=?4PVLIOqA%u={#qOR>%IhAy~9N90E`+fdD>^EK)wOWa-d({JOz zjfj4PSig|tgF--8vi)XExlj&{t)*sQTEv(LLB3<0f@eJKXp4mj*>om|pf5Lad%X6G z!3~Q65>_ANCtu|1>0~)mQ;TFff|3**GaEZO44BDt4&_e)qKJn410aMMJl1u`pPuvq zoj1MejLpYJW9kiyLKcRn75bAb<<=|<0`EP@97B?gl=H08@J5YnrJJ8=`#V78oqn1E zWVKi+bif?-`N>7o`IBS2PRY8#z|W~F*&MRq>x$VLCRf1EVF*S(@)&i$;}#Rv6hr4+ zGzYPiF$-ms9Wf&om#%9GDnZ|_9k|OgR#>&=y?1Apm=9=r2o#I6NH&orkP&2 zIl_)4sm0lgiJqcizyXRB1LCSPn7IOMu;_vQ&Kf%`AtomqG#-q?At$h!RUd{8m*{Iu zXrbtgs%?&QOTSR0%G6p4q7U$yWITT~aB%z*zqF@+j9+7&bn9ZG+1j?C7(U6<)S&U} zX}4$69<5GRGEvFK#dJiBEjB^xjwPJp=~5MaR=7e|0^dDwYhvJ?@zo6aOUWbVZX(qmjQ2vY z(iB>AN8$_+0-7z5@#6Q4I%>-KlP2)|D}}#4?M8oZ41ak&e>42FSpHpk<0%f3H?Cyc zcb`>glIpS=$-5zfaKEPaSGy_1!||3LLj=Lx7WK(Hw$iCm_~?cYe)FRtOI?*FwF+r$ z91<^?Y)0;Zr$z&$@miFu^v&s;)6L8$AHDS&dZL`+%47vb@#oLq7e{gkxkd!lN02jw zSi}dm{rF(b9JCg%pz@I+7>Y`1@mh*L^~X%;vC^6~s#!=ZMbjjjh5$z&?{fh2yf$3T zJ0n<5!6fS7A3oXF%VZ1ySQPszqcce(tt=b-;~r3gEDo!{FCP3=4AOXqG;`Bf`@Dhn zao12xlwH;V9=EFX#qJ9wjRZ21Sq>qed!Sao2+1`6+IFVP$R5jAzTkm~} zb;N|M;*PDmmVlXLXfhP>3yCR*k-;y$WH7o2oi~aW$*~x9C$0jzT)x%ahE835J!qvYmxkyBw7? zed&ey#6)x~X*NCjm!WTsQrYVHl;j1!Rg=aTggFm2RIWc7&#ujmh?fi5u>QOVa^r)x zB>U-5ObbbdWxRe5-s-yGtqy!7?LPy@=DXX@mkKE$>NN9xH;T{Km&ZMU()a0$rwqiv z7_Q*|kw^YRN(iCX6T3UEot#A_0rv5)Kdjx(DtzgAV)3`3JA_YtjoT}GZ8@pQ0pw$e?kuM_gR^c4-?9L`B6NbuPomK5dI)Syvi zd4WnvYPxcSgJ)6CgDpT=UKgP&pDFxgR6m@#q?OxvgB+l#!z}S*?*chH;K0Jj`iNnF zskVrwo?^JE*@+p{TAYgwtH|w4nrW>f+A7(BTM%A51$AesREv&yiU_i4yIJ+FK8C@zV6CxDT}*@Ab4KN*#s10PT$Nj` z$Nzhuj9uh$qpJ01Za4$W^N1~EC~my(R!eJ_Zhj8(;I9o;(JB1`gRvN@5^dxoNe@)m z)*JCu8WMM6pMwPz((H3dx?(;!r{RIkl+$LQtdf$s0KgWd#hYr!$i<7W4Z2Lv; z2>X)?-SS7uR<6Z{^TsoO1C7&?aNsA`?*654gR#=c8$=6FhK@VK#^2XRMV5KeKdy&+ z?oi`=ozJD299MbDYm2vcPt8vm4yC;g&2;@wcb$^PL_#Tc6LE@m_)@2yb`L7YOwFK2 zGAennb-ovj_V4?K;@Rm7DCRL?Eou8`+}J8VvzGREo^mGDX=CN2H z(RikL?3>b$*R)ybKHO>PwTG2l%W|aIs^=dHjJBwxJTa+*U!n5U-xHD<9_i`3ARp6o zfMqzPnJ5cXj!9sLM(khue!a=dM5bmcWs&SW*uNhJReAR6kr9e9EU4cTr;2aaHpTK$@hBLX&tj9NY@$jPtMuZ<8PRjx!5)AFX%O* zua7Ed0yk1%V5_%MG#5e>IlAW5JhIUzXGc=SX8E^RDl4C0NR|lKH^Mkmslo-~zK?Us zqv&^bs)cLwL0Ak2e3y;bswxOS`=$F#vBV>c1yfDFVgjMO+sbHzFYrF8(7$q3sPlLs z#qt5C!gd{g{GwxQt(j@`j!9&}`r4p)u$D0Cvs=MYuXD$9uYBtn-Gi@I*OQ%wZH+tw zmd&yDU4)veF!@8^V(&x-Z;jJ|5+l6^s4r%T6Q6#wy!EqAb10P8__s3q>p9gOgM^ug z(N0UeTCwzU=`oCDCI*cKK)MT!&re~lIj?l0(s-E(`8oG(jUbfqQCZhjF)O zJ~YvX4@XXs+j#O3&EGxmi^?syC+kv3p|An4^K__3rW4Xb*8MED#81D11=ybF=jR(@ z*lnxEoEJ`bbKJG)wuU;#7g_9b7IPgI#=G8KmUgL|O}bT{?BmHdv_GZLpFZK3R z7|*F`I)m6rl+*DIVpZqq(?|C%JsSMC;36*Sfo|jULJM+zE4MbZeTY1BsE|Z zPoLcRy;|em9p@|kUBEfX9A;i+=PUpdb-V-7>V>zaz_ztoN3io5&Lk>-3*DEqLxzpn z#_Q6mlCJ28T01L|?l3Nv^4)Piwq9TOCi)!43kA%Gn*&}Ykx8cUnoVmR%0pzWbb7Iw z+o5d44X_q356;N|}3Oj;m?18(0{{GQFt2YG6ZlrF!g2hBc1 z4O=#5W!tfq@k@p6>*fL?yR%Y+1)pPASkr^mMQQz^9%|(0 zR+{xB9;YE$mgti?nU|l4j;@!(b!fWroj`EJJr1fhVQjN3Q?_{NKal`QdqWfWd(@W2lL0i`K-M(hvRQuT5-g^DXZh zy-`=(OH4B8SXAYE6V3RDRGydo%~X=4JJXp@%V+UdI}n{;w31dF-pQ&L+}O9BCG~B5 zQS|yl<0AOo%g4}kW!t7gEmuz{+BCvss7HWv`AaF{&;!DXQl9Cze_o)` zSg$&bylLm?JkPdY(qgBM{$xk3XXoS7neS^Vns$}rI~M_5{5+HDDu1~cWxO{5F7}23 z#5li>*`7w}W~CdwJN-rlnu`2>L*+2o734kmgpMH*@YK)fv!S+^<=c^SIl((A?b0ig z=oWjIV(rx)>(OFXQktF7z z9JDe@eAY@+<7$VU5*N%WaySbuH!(D@skmqgV!=cKeAs51>mUWzQPd<1OZ65ks{vy; z0#~75twv_>JK}R+pJ@Q5yvj|9cPZsVx#ivgFXmz=(&G}TD-nbJ)pp06v^q89=L@tS zsKua@I<0RovRlyi*$CA)FOeqCbLHk+>)$Qr89v9%yjv_p@c%><0xF0+&y|rNMP-kd zl}ig%y0ppm(Mz+=-0so8W$RsP#l3a9o=y2JTXM%+jVtTQxdk}C)m5xSA`uHD{WNz5 zg|r6|{`5%RKZ+7sA}jmGqu7IRx_rI9flS78EU#<7b@poWtTk7=q6gT=+K^JdV5FHe z)_0|-!&fzkMODjQNp?c|Q*||kMk(K{B}_)Vza_l$HpC%PCgV-NMA;C|TbYAAgbG39 z;>*4)KuKk;AJfu8;DKpo?#SkuxuWe)yWE(!#5^h1Ctfn?TxiN~+M!ibZHjpcmfWg3 zG+aV17;3eM6(SI1H;5w?ObZz45KokRA1QyHS_7ESc0b8ImaxbI9(CGL_Dxb5NbF@I zR`24d-1JW#1iaf-vT-m=d(5sZxFYNRb_rj*to#;7HaL1U1LtRT6(4=!N-K?Z>qvslSFDX7pq$b`ioT9D4EU=HnenIE!hw&!yN1cvAz#7%_ z8trow_BEu@CL?B8kBD*cG+S8$$%uH(E<7vYMtqHFi1otLmEW-{4)PVYCD!$)ZE4|t zHdB0-T5rK`lZIJzk`RUstv}DHzLMK}yPrYkC@uWjdf5wGO{I8N+beAqd2AQ|HcsUa zUaM_7Mb3u8Qw$~VqJf<@CS{A{OBEe?`xHi^ewGL2oy%%LqW%kuy!bmr#Z0h20{_)U zU^yc$E?4R+S>rqD2-oR{Y9o@$%uZ+|B&53;pzp@R`jZm0gy(sdo!sY_LEizQlPHqA zyw#kk4WBx#%C9BuWt2Zl%PK8P+|yQRk6Sb`U-FhUDpv*JEbuo^58epDg;E&~8Ji%8 z_6Na{*oa!2fR;zqXiCBO`gjcH*r@mhu8;+2B>5E~gz>aKpL5kmMiX|(3k2gr@nvO>byc3E?k_%>Cf`o!8 zR{GLlyNWk2gyDIAAn!X8&lB|X87>m}b_F^Zg$P*#h#FU14fX9`&JsFn^cOTC3DO>! z|GnGPS2^(IQMrsiJmD7|ka3X3N!gJ6qdUzUL`0#p%;x; z1l4ZLvQk7vFo9RT&<_{tcsyy0KU$+dBJzzhb;oAWJ{xfYE_3Yn;FA9Ex5Fu75N)u# zm>3>eZ0xc$PCitM?kWV$u5i2AR`>ykX>3CzpuDUYz4;fDaY}0s9vS&WsFbT^5T@OBM%30~HnG z#>G&BxshTM>r`Aa@{Z8c$f|9&vD7IP-oi2Qic*9mj})KZB-1z+(ASvBX}s8_*kbSZ za^2#gK0P#z(8qY!&Ot9`*d6f1I75S4wXcVFvGdFXQS&It<*Ga7=T$DosdZTJh!}=? zX^LFwtO?rbT&t8x;#G>=ZE!6j7NfoVv-69Xl*79blxl;zH_9FG>)B3e%pkyD5EUXv z>3Kl+ZG1myPWeJ%Z5gPvRxR`aVQx#HggY>o+|In7RE$&{XD>&1hbe17YCI*<|4O#l zvPQ>Ip+myB49fA7X(>}B%QWk%&Cn@u%!$gSag!gPq(mTk z%9vCwx(4(qr~5laL6dW&Ta+pC_`%w|gS0CY^X4cbhXt2#QawNaMAyPjLmS)IPbv; zk_70)^tZa+e=;*`#*KGa&2z#~h0rmdKTACBB4~c`N4b2e|CwQ)5v+}kiH>I!ib!yq-5x073)pltxLRJK44{5 zW!2i8v!e6Fu1)m^OFPMRqL$ERdU3L~!?Mh`O08h$ASq^6S?EU+xq%kZUWNGer8_L~ zy$C!mEX>k)xqkp1ZIl}3qI4YN6?I~Asw5r&%siO~&0E=I%G>x7zZSI$*u`he=1hx% zLK}ZahqqQw`n7kK@72mNzTlX@i1}~lrjVV57Luo7(Kl5d&4rfz@&qA2CF;3cCeRL_JN6W)x zB#QLyWo3@F?`zP2gb^}CTUm>UdF2JuYGwul#XkF%-;nhOQ=yafP7m^|%*}tMp&7zG zqx)KSKcV4W3|Iwwu=;C5DtoGp_X5i*djLU{`fH`_y7oeFk}A13e_$>0e+TP|$!-`7 z5+FHEL(1_~p13g9pJOme|7-H@shWeKQtec=N*hMTVfvilU+a9N`s^b3uKR3c5bFog zD?w@f`Tv6Ku~chIIqpN`6uZ(2WxG;9{)AZb`Hf0Wi^GDJvMgz2Y*+qkZl74her`&;9vpzEF&!P58#kc<)5M_J zH}Au%l{11zQ(6t*8)IU7I!pJi_EcF2{Tp?PHJ)iCjOt_+VCTC; z2K@sBN{&c+*9?n@DeaL5x3(Zs?102k%<9`7gUF|?EQD<=UgmPm>tQgW$LsWGUP01t zJfn>q{)hxaYkMM94;z8J7c_+GDK1`%aik{S)xkQi^zob zv&}_|!&zLXO5?!DOH(si!8-LfB^_Z9hmexb zGk!DyzBQx{EHfJtdij|+T);yg6K{34k!ZJb6rdxYH5HY~ zSxdmDF{E(-n3jH1+y&OsL1lO=X>Cc7ewqQvJMukzi+MZX62HpwVyW$2FRnaK(B;Rj z{N?(Q-trw1X7i>oG{V?(>!HDC4gOs`4TVp;yZC}?yW|XlqZfOn_?qAws;|Vn;?~iG z=Z*|}5%GjRQqbXLtNN*M&M*s)_wxyOU9kU&-!(3WQ6?zF7`4*_GMoR7Td7jvc3i>A zuX3JhvRy!cXZs->3AtkE$P8HRPAmRZIsFfg_g{t9K92Z$epq~&SAqOzm;?U{-f;mK^Kul!3t*H?3Rbt-|+3^F-vK0eXK0=nbDJH zRkpt^K)Xv+mD+OrFxvf=H=#4>EsP#aoebW-ulZLsVJ*TK6G=?Y9g;w9 zGR-D3zMcn#dg4p_UMJ2_nQB!=6qlHPY&;-D7tl{#dx}7$c}lDI%Cm1A9`Nysb%z=F z9}izi^+qY@jcjQDSNKpPYgUaj@j4en163@L%uYh@0p@MMkS&5j#3LRc@n3Wr@!^Oz ztS>YxO6DLDytE25ySs+A{jjC^f{j%|1@iJ-9GFIxRtrrxNI@jw4ASUdaZVXfQ_TD% z_#A0fmi%kG6CiPC^90G|qjV1=BG|+<1;2Wm#4Sr3HzOOTuCfOoDt!0_H30^~EHR0F z#1mzj7fcJmzCUEaQH@VKSXLC2s6%WQ6meN?crF*-I27k3zGAL-z2l+~l6?YCWv7h6 z&w~96ozhb)(Qy!Kd)F#TTB>r0!4DmB!4M(J2ND;8AASp1O)Ut8bU*wUT)&ZWDioEs zT+LF5U9&u034=*F!kc4S#zR^rPFDsMyGZH@(R0d#iup;=XEv%JKr84++!)QQwZp4& zH}cIYcSKYj*9xSUQ(I~_!Bjol537`XelT|Y3$zThC^{9>2-wwW`j{ z@+F6SO~x64*m~)K(g32gt!eFu(?+N&JNty*q7ROjqhH^|(hrZ7Kv0jWMZXaxAoG*# zx84c@uwM`$Z{sFi`(-AvqwRi-1R#gWu=g%L(rnT1ho-)pi`LTf%f)MIMJx^GW*+xy z3y`}se>=}$Y|UnDoghSJI#nIri% zD1Zrf^oLJ)pg+eaR`udh1d0XUxlWt5z%{QvY@Z4auA1NVnkzCcU0)%=6|@Si#*0Qo zLpUEb{)YddrGLE|12zBP_Cy<`uQtGcD76{R8t-Vy>DVzimw}PXC@z~3>9BQD0$RJI zu?YAV(Z*O|_^xHC^H4D{3HPd?<5$)c2uC6p77-#A0@JPwXSHnMLmhLCLBmb%rXRr? zfJ!ASg_El6_h;)Kwrd7o;Zrq{aE#_x#4{bThWWKwUIM{T`ZwU)Ocs;UFWzva`-q_u zeW3{7-c@^G&4Y;&&0Gk?cP{*kZyz~t0Q=6GqnG)9IB}AtGi|5f)gvXPVkL~a5-ru*LoK}|__7Vy zicx!sA_wqi{}l9!QRUVlN~bK*=>3{{t-Fc^ zrr_AKdONKY%RbhP@+s~M_-0J-SPOhd$#Vo`6_a7-JrH(1Q>rOSi&k=J`1xBV5Mo}5 zM7F+Ef}=-db{M3|{qB~|KXQE&yhbEnFqHd%5BybId8n7|{AF^YbJmAaEDbA-j!7hj zvIBH58@e$Ce*5+w5T_lcVd9#tr^f9C!HczP@Qg*K|NZ;-E>5yO)JKZvKXQ9JLFF&b z9?fC21}ytB2T;Obva4msiW|fblF&Bvb$ylM`v0GB_IGL4G^R-l&1=>k#mHc6<$>@2 zKS=voVgAvSc9}5drLJs}$pLl5TOURO919?e$!b_{5&i z1M2OwZ4mhBu=Z%@AS2hfVA#5H1zb(KV}! z%&sI2)>rc+t?yP5nd$13`QQ|?+9>LFt{Yolk+8X{t7&$$MShCZa#^}fGfJw8wT4*VG z6&u$%QW9+#^)mC!zmmFcc!1yJ=c~jcSqY&{X~;lX{$NMWmpF(vOd*KxeJQ_Y-izGq z`(YWHX`ImHFzsuRuS#Mm%GyBVY{&3f4%nRwcz?fUCPsxHA>NAgSyxPvs>f$Cj=mV^ zdm_WeggAGjm0Z;Vv(h&nV}x8t69%MYakxs?Ok71NOVJ@aNs!Q$$xRm`8Xfn2pTVpK z$GuU{v~+IBkt;^+qy-oE(LtA=BKJf8$goJuU8BUQe0T(~kyI_Y_6K{Z?y+5sJ8>KWYdc4KtM;FCLp;dvkDn)xFl2MRdgh}x_g36R&F5|-`1~F& z$+h!Hw>*^zvg}6lh>sK)BSH}-j}2~h91QpmY5wnVW<^Hzmakj@Y_R8o&HoRdJlKr? z9Iq@`MiX%7O68FFPNk#f27@=TT)9uhJV!hslm1s4XtSgFipLXw9RKx%KU65M_sOMu zqc`|33&E=e+&cM>&d?t5rq?!^K_|O$cBFCUgHT2%+8gT^m72*TxcSc)Pt6;4gKVzEf1nwE%2f9uAlMo@bPkz(`n6p&wbbytU_+dG*|5gt}Jw_y```~AYh@jc#;LHz2s2z zmjW=S;DQX-#oS5OW*%fkNh#^1xu3C!kaOPisN8nL$R`P?%7L^rL#}gi;9@` z@XB2SMZWx@Cz}Z{pgfymi8oQMe<2_8l7)bNp za(}qVd`D2mez0I+2_=e9XC>@;jztUP`LH2$NjrgMF$FkT{^c|PT%$6(RyyA__shL2 zQ}qT>^eV8@fAIwNAu8_2SM)#5!A{u?dz9-jvgHxE(4{k43V$|btJW4nI5hda75wUX z6x;giOImlGWbxoTPZy2zFFhJw(ZM+dM8Fz?>Q)y?SJ2p0vHA!!Q1&*>Z!=;~t|Fg*{W!+f9^z?85&NSq@9S3Kvf} zcEzei3bzbGJzD!VXAw@&6rXBt3}F^K@O-b0rolj`ZsWA$s5rti0)fnv3 z%RTa2sOgt6PS^VN+plszF@MBIQgoh4TpHsWr%X(655dAgYN|4|)J2dKt>GU_;-=)E z3dmb|1!84=$YF*xM~EflwO|0-G$|&*7A-!>oCtP5ybE7!2;8y^ev^UGgpX* zG!<^*7L1C2>l;b1H@hH9PnMOWtJ_}Pht zklaZ>)7 zVr;(J5h5F}BIBUxUsgd3aPzfk;ffW$pO_G0BaiL%aMC!SX4-kTi3q^pr^<9A<#9d)o_X!@mWs*eUn10d3&AmmhKe`k zA4o#+9+lVr5W`|9wCK29^SlGY@%%MNinfJ1>E<-Z!;z>O_8519`@prq<1}JHi>YQ- zJ0lVpPnTIQ#|v+@SgRD4seq`o%;)LlW2o|68*jaS9~6lV;FN4%figa?LR6%vQJ;W` znicp;)W~(-@(5WJv0qx2?T`ae3Bd}4!7)Om`@1Yg6gbW?n1h7Cr3~y7bC9G29(8`C zJ1tI&MGqR-U=ig_wdRaSk_K@~bCJ)~T@Q-KtVBKTN`V;ABZ9=Q=q?N+MD+o8hbG$TQ= zTK(Q?p_09>4wF(2jwII5%GGb;JD)@Jw(?np!j$aF-7LAb<2ZW|2_qsSe zPgAxlYvWHtg%62yf7tw1Mt>ELz{a{j-G z=l?W`@YjC4GXc7Y_++U0D4&Egb||m|QF*z~`!J?Tl&QcQYF&|iqu0)>A09jdfGqI3 zm3T8z4T(&JF4sKIa67fo4;BtAK=%HR25E>4Oc3W5FYmHW&d@6w8$Ofv5g5@7p!VpNZZv>+XNk7H z3z?1Rxd}>4%>iiW-93frH6)=ni=3%0qTcig-Y|lE+uWH*#We zco84VN9(O{L5y@F2{a8@PZPcv(&CXl)WWRQSCJFc$;=A8wym@!v^)2X;D!jC*U><2gNe$|G9S|V%!mvXC$i;Ez4q;{C1?1;5yN!&!XZ^I*rlIlHh%MC8 zNJ+7v7#r6#oP$^MC$^7zu}XA-242 zTFzdW6;(fuZE$$)kS>^PVB>n?qg~h4x@wh1%Ou(dDiVOzW|it|*o0*E`t{Q>IrHgj zY>mh6OI=NygN+Cj=~nv(<@!s&sF)(!{&u{)n5`|t#pR`z#QbQJEl-L0H1aC6R*gf7 zLFDE|E1K-4A>R_`@F;s`?);q)-uIz zyH&Pw9@I*>w28m?Tx#`0jTVTj`(u4okEeKD)NL=EVgF*lrOi@iJw%NtIbv8o{5%j+ z?@FwoV6{l`en51eb7;o4fIz-su89bC3R)+AEZOIIUR~)W{Q8o8Ag_3coM_PFw0TdH zua;&Mq&wg|BPklcco1LzotGP)G(jb4gJB%E+&`{R>{v0tk7*e`bZv85H#ybQ$5B5c zBkKFgO|;Kmr(ZvrxM6-tuBq|*xm5xnZ0sheK)uCnchM3_UAF#T+`Ro0f|q(&bxPqlQDQ)?jwZIyf_#l&e`L zFYq+Wr7J6(;A^5W{}5#bU<;LuU!7YdNAKpY9Q_iG(A&0UT!(>>vc*JIfmQqCG8Ka@ z9;x|c9(2ZTkcger>GagSU|`uvNbQjJ%aAO7t`W!1ZV-;^@lQ&&5f#_yCYf6cvt#|Gx$F@HGFoTP!XeA>0T|zDlD>w3jVJh{%Bxvt zqOU$W$c8V$-0l8jA zu8zMA`CJ$DZ`~?Aa!xrnD>qx2)Dr=`=NN zN~q7cA1-b$*&$PJO|0mn*@Az=EVsi&UX>iUT-cpVmPKC8ki?FQSab4uANi|kY`vWK zvpb)irGWr}`W0%X6G!j;wxxMKcY2r({aQe|&Te~*46V|F+CbkwgR}HCzV2(CT~?G|B%UGlwda>-XrF{Pb`uegbP~4mue^@x2q?YNYRc zypRS6!_{E8Pe>9y>>W&|*K%($R=S|6U|MAp*{trg4#!DAG@ayLhIDc$HohT&x5(cLwJ1b~8HA>_X^HNCD;kx|2-)_-fuzfT>CbQ|wLb-ou>tSQ{r?;JjykIbbmZO5dr=G0hi zQG732-we*C1YOYf)tn6W;%R`554QP`**GM$M&IVJ< zk>z(U&2qKX(D<7?uk|i3l46=)@~tTRimYm*t%`K}!^FhbX<0IpX;RAC!p9LH}F(W257E;R!wvC*YCP|?^xHNq8DAA~^ zdQce4VxQn@kZ~!T?FsVC%%R^3aMSK9Kbohjm8*#y&?xk^FEP=1w88FNQLa;q4L@CV ztkJafT(=<~-Q`CEF*GiCl@wEJR<{xd?Kg7hm2n8B-!*P2YEyu^9UVsewCsAnkj40! zTuyZD5-2hn<`LQS#e_khE{a$kpQ6NaOh&+qq(#Yg_H%0#xXA;cjefQXm}By^GxI&Y zJ6?GdXP;C^I+trxNCT4edX#-7wt|~pvmThc{3;%CF!&Qq+aEU20A`Kh&B;yq06BA; zogTMXc!YBXo_uR0aD%@%gHe*r97Gso<7Rx$u<-LCaxs`ShE$o#f~{U&f8(L@yXTSg zxx@*vwyhHZ!pwe}QK*erg_S}LQdPN{&GHEG81~XyYmlme&#l_IL?rk6_au9r6sBI1 zqz$^fw+8b8zjnre`%F?w4TJ;Z=BNTWtf^wA}3H}($LA3 zt5;!qBa1<2I?dvexb=?s6Ey{DP;Ju%Xe z(CG3s*I|Pp(m0QMJn?n=`EP%Z9;j`t7O27eU|4pc=nkSc>Ab9ul3psK?v~*=mF%e~ zYd@Q2biBn6>KRRDI}(1!yrS+6|7(a$OmD+8JhN--bwAE`y74MWnHgPn2qwQoG2yd2 zJH=dHm1k)Wc-NZZKNV~+ZjHJXa7f~umn{-M2(L4CXPjO$4xZ%tICYN>xIH&We~x(8 zXyz-k58k-(`NgXjXZ~jrS@`1W-@|7)m-k6m=L&9TtDLhjnY9zm5=i}w%r#MFx}m%u z+Mqv1M~M00>;8_v+selv^^Dy_@%aS>9#eZ!Ecp4|SQ0je^VF?|p!0AIxIAb^=8JZ= z?dpU+WXxMa0&cz~l;a@*MJ;`iO|+o=S3$kcCq4m}TU1e{{G-%gRdwzL=_Cfn*)N}8 zZQIw;zWA!BDN z@ju7N4Qb`JbJStE%UVD6?C4z%czDfDG?dyH)KYI{lre`-CN;nLbUUB> zi_VG;qu}gGa#ri=f;`aInr%5|t3uSmz)t?ST$0**DcvV$l8-tpOkOk?xX*wS z%lzSE6cG7=(AAL5vqC$i^5M_YdzM`OW{?iAM;|qaY`DbaG&9F$v zwGcCu7a?$3J~jp1vsxD0+&$ltjxrmaCj5N4KSN_T=Ik*p^JSE|!Q;zstu1IOyz*ss z+G1>zy*VB(u9O)FV=dZn2kd;1Pd&a9(@v{V!L3f8WR=i3oe9z(OU94vEK~ht^Cy{SrQ?}3V_ET|`->TM$_>+CJ|Mry52f33L8K$EmyzZj8YpC0FC^aDe8 zQxAV7h-uY6iNoN`mYL4ZX#-0JZzC+@lOdmNFNXT=3JeL*Tmm9KA!X+Ufz=zP(OJI7 zCc)v(T_$PZcMqHP12xk`@q=DDVOE5IY1+~ufck$m_SRuhZEe`N0Z2EJg0wQ!(2`0c zCDPs9-5^TWfXL9TbVx{t)Bw^T-Q5iXLw+02Iq!RZzw7<3>-$ePd+*tM?X{k`*K^-b z9EuI5|A!C`zX;#0L6Q~Ucb+fdH?Y1?37boTpC0Nqdmf9!TV`sqsMG;6nfo8%A#=z_ z$PF0FPuz%+e){NS({_s!N0xeb1R5+b}c&w{|uKsfTg%;fftM zz2|`|Y9y6D&LP|O#dlGvhy~(`#{CFt5kY)7IydKyuz})^WM%bCV@5$^qNDbJ1%)o7 zA~Sw&#T6`~HdWl=-slnc_fM^8;_Lc0%4QeYM`cS8a%j$N(--PopEf@X7WO;#%ei)@ zO&N=#Vgnv(MHe_z5IFPCbCf8#0M!nzJJ9^&rMz>jjvDiE(sq#+1nUjg(eu9_v8xhz zeUAij^#w8sGZT?vS0OlREsWisK3;y+8a zA&il{a;z)zS36ESe?lf{9gh1M0PS)@%Z8JOY|vzMYEu}=p(v^K5|?AKb>I&tFAaRd-5W5I zp&uU0G_7?_djF>W!Lq1fzW8k>8QScmIKnkj@mj6j-x5DaId(CqLL(a~#K_dMFY3;t zmKz4+(KuU>+NXb+A_qi{2Trk2AIT8PsZEd?CD1{Sr?6w=84MFW3l_d(_eyCk>*15` zT$l+88a9zYHH@OXCE(W3g?7qVFk@b~dZ0E8Y_hCPrQ@&dnsKosN>6G&dty1e%JTczIg_%6AD{t*;1LNwoJ#l_wZp-dW6KD=JPzZwrBF-l@`+b8xQRGzOHQsq3o-c~QQ zftPnpuN&~)K$EPxJEasoDrOB*FMk?vuPz;yHC4{Pi`^Mh+aZ*tTlR9)4;p~@6F!Tx z%_G;>tfld+WG`hlRB1*5;kgdV!`8%{zrTDXbS!}2z?f?yq#fhb=5QV$e;_u@_EGfN^x0=MB5WZOD-$CcK}rx0q;Agb z+mFNyeBZaoDw~*#V*nHVM3lMx8RRhmlM4AyTKB+;U0W&b#1fHq`oKw9!g`Em6)|cD zg{OYLrs!@5Qy(@&_ama7`JEMMN47wO;DJXI*a|JCP_pC0VSnlBB_lK^q~9@zw(p zmqSIdPEoajiY9dvR}r z|81=WOe=(jY3rVvu1u`Vxi550A3+Q_;f9c@VZ^>37lXi%Hj#{hPrurTCP_)byP>4L z!NB3S2yd=PTzrcAC>xK49xaaohhw-iJ5vl4G=+{>HmyzGD!#l*YqNaz$7pI1)4i&4W&&MiFa9d0bnRvrN>JZ8&D?43t)F=bHAuw!U1ABa zE-&s~&EP){#G3Hg^Z|@`L4_naa$|$&`X&KpRP~dky>-RE#R@bs@svq>0rZlc2 z`s}g?qNpOageL-Vh;{4Y?Hx0%$43aDHz;{aBUR*he+<%0a_##pq zY`wh!d?^>(ntOl$FE9RLSV57v@xx3Aq|G6V6a>$5JTXqp`qdsX*by$NRAWRtZj~2h z3OFIatLOO~9@Vj|;*8of%zD*iWw6EQqK#M2DF6SQu0@ORl>0)vzyZ92Xa$~i3J=n- z{_<^n>GqV!A(}kgdMuv~K1^Eng^jg6ZI50QKZ&IFXzZDguRfKx0y>3NOb2hPoVw^J zeVCj;$}T^%?!!p!b9Fvm)g?%9$6>wx!xU5ZhcaBn!B=}puE9Q8Tc5?V=QyX_DZ2y3 z;Qi2&*9BQgiQsD+9k(lfZWI|6;q>y(7|ZIcL{{n}Nlaj+%=_X1`#UEul3POMr=N}5 ze51=EW0g8Nbj~SxMBaz)IuE`-V;vB3N80yVHRUQLfAR&I z`F<=eI%v#B{t>C&g#OJqi>OhM+YYOTxvg+8W=2?4EhfgeYW6T9U3+I@E+p%M)(o=+ zzox`jQ6BGs;mFj;mo&DOQfJk)R#ZVN%ZVW>j3w^yo*zMz*HX@n2}hehp00@0;6jqi zMW7dbpC+U1-K>q0_X4{7@~i}h9At`zntdw{8b4gwx8V0gSH^G)&ah}wI(g$UEhw!Z zQwwlX3Go#FblS6*wQt27PPZ8yGL^6xE@mI@$6#%T6) zS#U2bF9(wfnZUgE)@qB7z|2adX9Cu;fl2z|pbPmhw@!UGYqC-*M@HoCA%~8D~y*t~xyLKK>0oS@;ztobREO;(?#Zh~EY0j6pESUpErB zq5S6DL9C_TnVIk=Z2n$0L#Dj~9q za%`Z^UDenZvL@ZUhs^yb=4SIMv6hj1@b)C3Ft~%P{`_+f6&3F=Y4J5R1%5-Z#%(^C z6}iHzH4|<^%B9=%;yng$zHo*sa?^^IbMwqcmNBH{107{95av|vP?jBChFv?_2Ewb- z${YH8UH-V0ID2#I4t-VK696CVzO_wk|3`D z)ymO}rzcBuwe1;G$;aH>`S5{8v~lkoMXCZh{aq<{B77+20%U8y2q%R)j2b=!A!?R%o0i z;$4IML?lcK2tcajKaXD~cm0%}HAuFFeuJoqR?@o-Dfm{5kbuCy5#(}u73Avjcqm~D zi`!T)6ZwyLbfAqm{;A++Err)bWFo>bHD#gvxh|2IeZa-Vf&^v`=IS=z&e}CvDDbtD^p!E``AN;^^sG+55-2beg8-twUWf zcL9X>iS!A#htC2l7xM3Rl5c4|!KT>}o&RyqlKfoqfXk)JoU+P|()e#)7YJm4+BIfe z2E(Tf@A5~Fr3~Q?TX|8s{~FA(njhHcIo#j4XB^vXf?P&}LI3>}*=h9{x8W_to|M+b z)5xcNB#`W*gonKcZK^T^9xD-a6ajqz+{GdzXq2f4w~0(fuv_UjQi+x8B<>K^o;7bA z5fjnV;eq?BEjxwu%s^34vlI;g26qjsm-Us3&#K;fwR%4J>2r}c_1^eKVBL0C6wbe& zOiKMpo-Lu-m9`iIW#yRSivE9JObiHt%#GKfD2=D@I8sN;T30*Sj1%&AW+s@pnW&$I zLd~uO#OVCWfkc(+d4JPzJM}FM=@yO_wNI_P!?rrvvFYCT z{FS)Btx{&VqOcRnAs#?|F@HhxHE1fD*bF4T`~(2U&mVry|M#4NNbC_vf=rE)OAb8n z7wW}h{^Y$8ZaZ;%0&k_`wK(uW)>JSHRrI$DmEg-GvfaDyD3y(5M0-Ay&w>0gEi1!* zfz&8{oe#&mhTw&yG+{Ye$=3#8n&LOyk5&eC6MISi1K-&->p;@C%0D8FPp7ngJ>K27+(_o&%lJfH8jGL_%PO|{|Uux ztQU@_f{Q&;O8n|C0O$e8(nH*Snan`P&ZD}nxv>ffALay8&mmBsmc*7dKD@2S)gXc6 znshH{VG`1QeKwr5*DF<@xwC9oC~g!G$gbIPy7w@Ila%yFL+7{V-;%|r=FtN}P`1~N z5py&kw^P9`)%)wxj<{zI8b#ij{BX9c27lbMFS?moxcwGf9NF>frv2TVt)N(+ODywA!Hr2CS`A2$#Sg^p#H9&8FI>3oTfN`g8f`I_5`t3pDCZFijuQAnDc zc#qF@K0RVSB`>;|c&#cXz5BdU6Ld^&+FT|Ak$NjjsU~LIZkcx@4Mg>|_C>b(asAA& zCz5rDWK(uF^h~fBwwDmXv7BaF13nBG~XIdg~RUa29LE=6 zGDr8OxFCno_z`6*%`3f35^Bc}V;1xyHgSx7Vm0*Y`D8|nXbvko6tg$ml!m!%1|Mlj z2?|#aLABasJKK)kgz6-5k@Z6PzZ6?xa-)q>%V+@4`>-KV{SYpETofQMnD2nxJNimBFn)JL_n2f7APD704X3s}u%Cl7i*55=+dI)nxtvYe<}$qS74~+%wA{ ztb2;fSFFsb%PzOLco|Qr*ncm{Kkyn9j-C_Gam1}X$CV}h=X&J(>r+++ZQ_6o&Xw-I z7HuYTO0wO=)%^@#6-Y+}L3k4~On4I@$q7sQ9(&BBDt7IDF1tsUl<>j zXSt0umdS|=^I;&8@TG#n)Z{QjY(}zeQ_aQMD6jJi|2Lny6P3Ms)B{sobLD(?{=lI3 zfazZt*Eb=0_qw$=B6h0)(A81`{~6GJ9~F1n>uaOSH4?T|HkMu0AG6dRugS(<6(0^P z*x?~|O)xLgK%leb`mV$2?B2zg_5sN+L=kSkxyZe~ES}E3HRkGZUAubseZxw{Cpp&& zmD;V+MW!5xhwI&hRI!4dET~yakeEMwi}?vldfWamxzDX^GkR2!I9cN(C9?M6j7p|n zZb=|6J~die&#y@5;k8mjHoc_%_@v8TwKAVN0jDDpVW!DXAe3v$QnNK5J#mN;-U;}i zM>$X#WBQVyrb(vbY`Hm{YFCTVtitwZWgQczYJGMw-atiBQ=f{FNl}9NBLB&7@oyU) zPfuESBU^BWZX`#f)RJqdRLlFalyE_2DW&hbiMXOjXosj1b%{6A!ak?N5#7#owDzAU z=8XhUBdj{^WgmipJyevc>T3OcMG*~rSVmc@R! zEFTR4RsXQzp$y-iOVP-oz)H^p`=*-)uG70Y zeHBkEJK-BO!JX*2@LTAXy&Hs4#h=QKX9K*bW;?_BC)l`01OC{Cd+P(Z$#F8Ep7zS* zoa&+h5Qd4F%+j97D9AbtZ4%a8z`+U6&ewSNMS54Nc6Yfk$>aEp;d;+rv`i#=Aw6%~ z7tOWN!eMTubnS3trB)$mkl<)y?o9jeE9uV{f?M2CLpz*Vy73kz2cEu;b8*^{Yo{VO z6HYC+GtH$&F=vo4U#~g2M!Ra;-N!$6KfeaI^b$V4VqN6djDFQ}aykY1tTCx>_&N@Y zynK{5@(ENgYE(hCjp(?b919vZCZE3|5oT`B4Q($e@DDtRw<5uj3H?;=Qm*kEq_P{X z9#b({U+mwZvQu?)z-f2wNzioG#A;o5q)qhgA*EsSeovURqn0FU)ouBA#ZF-l-=noj zc7SV8j+0wIjZKJAFSl2F@Uc{xgD2m3Ako~i7w5y%EAUgeYT}Sd`&vEbggvPs$UgF+ zy-0&b>TUw{sQW<>zh1)-aZ4IA_94xg>PpQ)d&j6_uJ8= zCmn0u6_n2PHzXKWOmg?z0!Y^(O;{eix`k5hdI)Wlje6@o|13ah$$vt=mB?Gx;b116 zf6$4CR<(oi*MeQl(TP{691UrbUV=}$W6521Q}ZP)Gr6LI9c&~;J;G2|=X$i*pLut* zhPijtM~|VD{XF1;_{F+=l99mzvW{J)-pyl*&8aSC!~(?jM_D#9=M?*^vV@}@lsCX6 z*c>H3mvi%|;KSIsseVdzw}2#2Cxot7>{!&@f@>59$%FFVhJ?#n_Iq7)JYrME+!I)Ey)?i{P1tBiSZmzL zMStvuC40Tsl{lkL4b+tU!ihv0nOV0d)!30|b_HJE^_yN0=sVudycMbU)jZer^@jOk zg6CK_GZACXHOj2EwZnn@`4W!RbvXC zO8JV*m7&_cyKBFZYj&pr!d3pEmk@NNP@`Z!7t&aupi{miHu1#UjGL#CW}E&Y+&E?b z&)z!zdoL~i&8uwn@J0^Dvl|b*Z4af@#wD=;EH=dK^=8CLG0^G3`rQxp)JXF}r~Imu zOTCOF?5;J5iwk4NW%MyQm!IGxdY|jxFSvtS&WJnOgqv<1ZP<1KzO@IEB3KRnnHUCj z3%Bhh7vC=4@d{7cKl0Ms@7O;Vn}n2qFKhe2K+ftP^X!y>{l;s8mVQH5goxmXo$|P; z6!=p4BgE~U+?o-O<(akR`&Vk`1!)FT!sJ|k_QtKQK!nOqN;ATG66{4)F-s4q?3~iR z=LW~~FEKZvi?sCYk{wpwg3A><*Q$KlRuY!+>pB@$=1ZIG%dH@S%ovoJlz|f%L!X!SUA}(M=@T8o?ak2Z;Gzm7Bo_BWNXn6OF z;P`Nf5=T>Ps)Yt zjbvsSZT7>k$Mh_ZB1DZ=nt8^dYmRT>z27AYclh%4EA6PuXU?mNS%_9ZZuV)IDv)=g zi{UfA4R&iS$kXCRb)FxIT2T5;?EH)2dFLdL@nK}X<5I``7f{ev;n6?~y872znsiI~ zKBwr*6&p3ros(F42r9^@j1? zu*Q|(4!(4r8cz;Wa5Ez-AVb`1%28z;pC=TH<)zpnZxTPMH83$Z&SaQ8pDJQbdi>4P zPKQC*M#q(vLRv;fDmQuYdZ|4SFJutmT&)Qmq)obMreAMTiso{`FJmwwOlG!phTAnh z7Ly2`u~(oHY;vRyMmL}0TYDROq!;$G4exz0t9X(lDRZQ?OnvM_vZE~bT1;A#j?;%T z=LXE9@6;*UE{^j+1Ph6bxd1~R?EfT~l@!e;LHU(Kr^DC)mby{?b;FUVY%Y@wx1X?5 zMR{S{#ZK>#+$*+0kEP@B)Hmrx!C9u0*1zpU#O*VUH2yzLiYY zu#Jx35GDgUmt623OS{k!=<1GZGTD&D!(bS)CHD)M8f9izRs2 zk47Q*#oT;fT)4?Lew%+3W7K&3p@WP|6QAMs(nM85>MbQtGKx`RVDS#xc?2^Y1Niv9x&(f5^7u;CuCP0j>qpS#RFP&^udjIyn5NVdM6619pCe*}%sp zsLD8hdPnMbOkrI2v8QvjVVKKgl>6ec_x9sx*`QntydXKXQml9q07v*Lr-ImGQl~2F zMlqj#q5pVUgjK^#1L&n1AW23+!jpI8Zs1PruS6h%Rvs>WgL z5l!UZG`pMS@iHo85~Cj@0G(zUb*ch#b)J{7npNNBAA7iwob~Jij`W9AgGFAg!>U9o$B{t|uZUeN8Q|Y!XgW84s%~ zr=$JQ`c@of>+(1>@;csWHs8v2<9f!2#Op zoEHFv3({9{pGN@82{~;mYq`~m{|Jbl0`x*UCI*lQx7JC|8$j&vjkiba-n}3tee_;ZY?n2ZTKMkTnc|fbQvdMvSvW}^&>n&hK56VYBEwifhL0_n+h`fF@^W_n zQL;_sbH!k`=8LwAlaUu#(iAuXZVGnu3aP#>H_Yfm?;=g zhCG)P^2+9!R3UOTh4LHe7-J^xJPOxi4-Si1 zZ)AvXM^winOP05!SYkSr1`qEPpq+sA5|NS>D7%dV;)7Ht>3iknUr(>;J#mAWsJpK2 zimEQXH7H&ex8ihqruQnhf^>B1CPDB*MnFG6K#4#aG8meh@|g+)2+vFQ)Ll@2L&q&8__W$__JG?H61=xubMAO_NjkY zoJJw_PkReDY6cH^a|w#wPc=rB>ZY_j6OVd?RKU)tpRIT!riZ3}FwdlM>ss___vct~ zR%25xYr?8wQx8sja6{Nu$t$11Nby<{pVSLptmSa=_axrM(L@@*B`a7|!vK|_Ooy21b#fqIwTu&g2@B8(-OZzK0#q#FJgiWc(MEA+4yhfcSVQ^oU z&rul5Rkaq4LJUS_d0#DM+*BCfq|PUFPu#*xk=1WT%CzwO?4ES>4m7R~?!oyU84Ih^ zXS6+hmQ5TONq7bMPhwy8D~jzgkItTFmcK;nxkSoW{i>x0k#9spB;rpT%4nOy1qcIHu`q;Ar4&5CcnZZlWF{m&ebh#*k9}Y1G!$@q>^F`hekkRQE88LGKk4h zrly!(+XRa@i^noL_ftzvtxHjr&UAmP0TFnJVw1XZhX|~g^z0P2W+^YM8TJ*esL!S= zSvNml#%!1(w!H5+wTcH2$_ha0NLC7D5^9CKwDhaS3+ehIb2A$Nqd)!W<9MU--h4J6 z-39p32$cF2`owG*6}f8n2*p+1ZCA{ITC!qjg88#h(vTJ=c?1FxAF; z5*Jo_%z$Us?Pa%CU>Si4uk9yzSuq1$UbemCpRli{Exy&c{q?q(V;?=@h^dR(vy=HZ z4;GA~p8#omtI=LX#jsL6BPFP?f7#?sZ`=zcju1>W_u~FiOlnWtp)(bmx%#mS!)P7}N=C)7m9L!yOk^H%opkjpGL(!H zdfp}AMz_7`r_E#2D`6tIw0KDh)jqk}Cdhg+b}7r-1Gwh8MT)`E!Q4499GqIZDAt8@ zOJJhP3rdigWKpkyyad7RZ^Ou;SdOWAmIE#1Re;XhYF*Ye4o|ZAII6@kWkIFbXR<Dn>!hdtmyOoz0{gy+aT)zU#m|pQPVj!pOe#W*oLzQIFy^`;LcMH@t z*UA6kf!;C#a;21ixzm@ka!>|xF(3GpIiJ^Q{!y5$PIu%Az$?jWl|+TSw4Yw~$o?ZC zaE~C18leJU?|(GQ{*^KPKj^5@F~_YzP2VGcN58EVRDLJH`NK9qum~A0~WSFgk(`sz#;THzQyk zT*|_q#XmX&y{*U~c0Ci}A&=#u9b6*@4L7?_G~6@EflggWjvc|bK1LzpiR%gazF~N} z=Q&#kcdc#x83iABM-0AMrn-)6+MPVV1&c%i3|65Pdp=3oIC^mZ3h(WMwJWcsN#mAJ zcej9&3KVI1_2BM-RJ2dT>^@fQndJ@7)#Xl^BhWKrQD6aqeS2eXH}US`zEz8T%gs~& z+mj5(S;ww|=|$ihbZ~84@8%z=!tSoGlxb5kKg!yH>iSo!PeS@&CMdF>u%6YGtny&g zcn2}a8fC02v}&AZO~;!XcNCOsoXIoJAA|vnpJ4rCA+36Le_^|u&Wyv&GP^dUf>&&dIr@EF zLK7a5YAj!iW?cyZGNJ$Bc&b48zL=uGUKYT4u#Ea#svr17>X9|jLnL2`wO`{3g&*RB z%Zaj`SFY?UrK~98MOa@AB>l`(UvNHez>EgkePS{|JO# z+<*>kk{mBB8cdD)9yj)d1^u`-Na_zv{m5rUJ8GC5kmB z3oPxE&e^8lPG_`6hw@V)IO!2Ij60A2Yc(q`C8{SOsed%DAnJ?6BJM zd(z6Cy?81rE5W{7Z*=%PQBVr_>UBQ-n|At(NVc9*nUUAH1U%T*m4*2Ih1FAG@`a%;)BmeaRp)c$(g{nwCi zY>J;!#5$1?K06I(?TEZPG=Hx-RTS(cgE$g9ucz)JKB(#gEH_rKa=Yapt>V`w9G@X4o*0=MCzqP(3fy2Ir@fNORX^I* zTAU8*taWs7SM)t^tu3l0aSyBCpA%ny>7#_y;XenyC{@DcF0Hr*f%8sOGLL>dCdybGB`Gu$gNem8!UI zJe~b`lh=y+z71;vy8p}SF0tCO@t4a*N>giE%wNuWJ3@x2DBIwl0ABP-8%gJ>ldRVd zOV*qd89}-k-4`=Dp-TNT;Js!oyrNFueXp#bo1w-_oP5{h(V`aHVuvN1j+n&tC`(4; z%V^u8%UJdxG!o6>usm2nyUcyn8 zcS!8{oG6tb|E^WS=rOR=JOfQFmtpM1(+ literal 17543 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 2) { - int order = size > 3 ? 3 : size - 1; std::vector x(size); std::vector y(size); for (int i = 0; i < size; i++) @@ -172,19 +173,28 @@ double NoiseFigure::calcENR(double frequency) x[i] = m_settings.m_enr[i]->m_frequency; y[i] = m_settings.m_enr[i]->m_enr; } - boost::math::barycentric_rational interpolant(std::move(x), std::move(y), order); - double enr = interpolant(frequency); - qDebug() << "ENR at " << frequency << " interpolated to " << enr; - return enr; + if (m_settings.m_interpolation == NoiseFigureSettings::LINEAR) + { + enr = Interpolation::linear(x.begin(), x.end(), y.begin(), frequency); + } + else + { + int order = size - 1; + boost::math::barycentric_rational interpolant(std::move(x), std::move(y), order); + enr = interpolant(frequency); + } } else if (size == 1) { - return m_settings.m_enr[0]->m_enr; + enr = m_settings.m_enr[0]->m_enr; } else { - return 0.0; + // Shouldn't get here + enr = 0.0; } + qDebug() << "ENR at " << frequency << " interpolated to " << enr; + return enr; } // FSM for running measurements over multiple frequencies @@ -295,15 +305,19 @@ void NoiseFigure::nextState() case COMPLETE: // Calculate noise figure and temperature using Y-factor method + double t = 290.0; + double k = 1.38064852e-23; + double bw = 1; double y = m_onPower - m_offPower; double enr = calcENR(m_measurementFrequency/1e6); double nf = 10.0*log10(pow(10.0, enr/10.0)/(pow(10.0, y/10.0)-1.0)); - double temp = 290.0*(pow(10.0, nf/10.0)-1.0); + double temp = t*(pow(10.0, nf/10.0)-1.0); + double floor = 10.0*log10(1000.0*k*t) + nf + 10*log10(bw); // Send result to GUI if (getMessageQueueToGUI()) { - MsgNFMeasurement *msg = MsgNFMeasurement::create(m_measurementFrequency/1e6, nf, temp, y, enr); + MsgNFMeasurement *msg = MsgNFMeasurement::create(m_measurementFrequency/1e6, nf, temp, y, enr, floor); getMessageQueueToGUI()->push(msg); } @@ -407,7 +421,7 @@ bool NoiseFigure::handleMessage(const Message& cmd) { if (m_state == IDLE) { - if (!m_settings.m_visaDevice.isEmpty()) + if (!m_settings.m_visaDevice.isEmpty()) { if (openVISADevice()) { QTimer::singleShot(0, this, SLOT(nextState())); @@ -415,7 +429,7 @@ bool NoiseFigure::handleMessage(const Message& cmd) getMessageQueueToGUI()->push(MsgFinished::create(QString("Failed to open VISA device %1").arg(m_settings.m_visaDevice))); } } - else + else { QTimer::singleShot(0, this, SLOT(nextState())); } diff --git a/plugins/channelrx/noisefigure/noisefigure.h b/plugins/channelrx/noisefigure/noisefigure.h index b174bf526..9a1deba49 100644 --- a/plugins/channelrx/noisefigure/noisefigure.h +++ b/plugins/channelrx/noisefigure/noisefigure.h @@ -95,10 +95,11 @@ public: double getTemp() const { return m_temp; } double getY() const { return m_y; } double getENR() const { return m_enr; } + double getFloor() const { return m_floor; } - static MsgNFMeasurement* create(double frequency, double nf, double temp, double y, double enr) + static MsgNFMeasurement* create(double frequency, double nf, double temp, double y, double enr, double floor) { - return new MsgNFMeasurement(frequency, nf, temp, y, enr); + return new MsgNFMeasurement(frequency, nf, temp, y, enr, floor); } private: @@ -107,14 +108,16 @@ public: double m_temp; // In Kelvin double m_y; // In dB double m_enr; // In dB + double m_floor; // In dBm - MsgNFMeasurement(double frequency, double nf, double temp, double y, double enr) : + MsgNFMeasurement(double frequency, double nf, double temp, double y, double enr, double floor) : Message(), m_frequency(frequency), m_nf(nf), m_temp(temp), m_y(y), - m_enr(enr) + m_enr(enr), + m_floor(floor) { } }; diff --git a/plugins/channelrx/noisefigure/noisefigureenrdialog.cpp b/plugins/channelrx/noisefigure/noisefigureenrdialog.cpp index c979698cc..1200fe989 100644 --- a/plugins/channelrx/noisefigure/noisefigureenrdialog.cpp +++ b/plugins/channelrx/noisefigure/noisefigureenrdialog.cpp @@ -17,11 +17,15 @@ #include +#include + #include "noisefigureenrdialog.h" +#include "util/interpolation.h" NoiseFigureENRDialog::NoiseFigureENRDialog(NoiseFigureSettings *settings, QWidget* parent) : QDialog(parent), m_settings(settings), + m_chart(nullptr), ui(new Ui::NoiseFigureENRDialog) { ui->setupUi(this); @@ -29,6 +33,8 @@ NoiseFigureENRDialog::NoiseFigureENRDialog(NoiseFigureSettings *settings, QWidge for (int i = 0; i < m_settings->m_enr.size(); i++) { addRow( m_settings->m_enr[i]->m_frequency, m_settings->m_enr[i]->m_enr); } + ui->interpolation->setCurrentIndex((int)m_settings->m_interpolation); + plotChart(); } NoiseFigureENRDialog::~NoiseFigureENRDialog() @@ -52,11 +58,13 @@ void NoiseFigureENRDialog::accept() NoiseFigureSettings::ENR *enr = new NoiseFigureSettings::ENR(freqValue, enrValue); m_settings->m_enr.append(enr); } + m_settings->m_interpolation = (NoiseFigureSettings::Interpolation)ui->interpolation->currentIndex(); } void NoiseFigureENRDialog::addRow(double freq, double enr) { ui->enr->setSortingEnabled(false); + ui->enr->blockSignals(true); int row = ui->enr->rowCount(); ui->enr->setRowCount(row + 1); QTableWidgetItem *freqItem = new QTableWidgetItem(); @@ -65,6 +73,7 @@ void NoiseFigureENRDialog::addRow(double freq, double enr) ui->enr->setItem(row, ENR_COL_ENR, enrItem); freqItem->setData(Qt::DisplayRole, freq); enrItem->setData(Qt::DisplayRole, enr); + ui->enr->blockSignals(false); ui->enr->setSortingEnabled(true); } @@ -81,4 +90,125 @@ void NoiseFigureENRDialog::on_deleteRow_clicked() int row = indexList.at(0).row(); ui->enr->removeRow(row); } + plotChart(); +} + +void NoiseFigureENRDialog::on_enr_cellChanged(int row, int column) +{ + (void) row; + (void) column; + plotChart(); +} + +void NoiseFigureENRDialog::on_start_valueChanged(int value) +{ + (void) value; + plotChart(); +} + +void NoiseFigureENRDialog::on_stop_valueChanged(int value) +{ + (void) value; + plotChart(); +} + +void NoiseFigureENRDialog::plotChart() +{ + QChart *oldChart = m_chart; + + m_chart = new QChart(); + + m_chart->layout()->setContentsMargins(0, 0, 0, 0); + m_chart->setMargins(QMargins(1, 1, 1, 1)); + m_chart->setTheme(QChart::ChartThemeDark); + + int size = ui->enr->rowCount(); + + QLineSeries *linearSeries = new QLineSeries(); + QLineSeries *barySeries = new QLineSeries(); + double maxENR = 0.0; + double minENR = 1000.0; + + if (size >= 2) + { + // Sort in to ascending frequency + std::vector> points; + for (int i = 0; i < size; i++) + { + QTableWidgetItem *freqItem = ui->enr->item(i, ENR_COL_FREQ); + QTableWidgetItem *enrItem = ui->enr->item(i, ENR_COL_ENR); + double freq = freqItem->data(Qt::DisplayRole).toDouble(); + double enr = enrItem->data(Qt::DisplayRole).toDouble(); + std::array p = {freq, enr}; + points.push_back(p); + } + sort(points.begin(), points.end()); + + // Copy to vectors for interpolation routines. Is there a better way? + std::vector x(size); + std::vector y(size); + for (int i = 0; i < size; i++) + { + x[i] = points[i][0]; + y[i] = points[i][1]; + } + int order = size - 1; + boost::math::barycentric_rational interpolant(std::move(x), std::move(y), order); + + x.resize(size); + y.resize(size); + for (int i = 0; i < size; i++) + { + x[i] = points[i][0]; + y[i] = points[i][1]; + } + + // Plot interpolated ENR using both methods for comparison + linearSeries->setName("Linear"); + barySeries->setName("Barycentric"); + double startFreq = ui->start->value(); + double stopFreq = ui->stop->value(); + double step = (stopFreq - startFreq) / 50.0; + for (double freq = startFreq; freq < stopFreq; freq += step) + { + double enr; + + // Linear interpolation + enr = Interpolation::linear(x.begin(), x.end(), y.begin(), freq); + linearSeries->append(freq, enr); + minENR = std::min(minENR, enr); + maxENR = std::max(maxENR, enr); + + // Barycentric interpolation + enr = interpolant(freq); + barySeries->append(freq, enr); + minENR = std::min(minENR, enr); + maxENR = std::max(maxENR, enr); + } + } + + QValueAxis *xAxis = new QValueAxis(); + QValueAxis *yAxis = new QValueAxis(); + + m_chart->addAxis(xAxis, Qt::AlignBottom); + m_chart->addAxis(yAxis, Qt::AlignLeft); + + xAxis->setTitleText("Frequency (MHz)"); + yAxis->setTitleText("ENR (dB)"); + + m_chart->addSeries(linearSeries); + + m_chart->addSeries(barySeries); + + linearSeries->attachAxis(xAxis); + linearSeries->attachAxis(yAxis); + + barySeries->attachAxis(xAxis); + barySeries->attachAxis(yAxis); + + yAxis->setRange(std::floor(minENR * 10.0)/10.0, std::ceil(maxENR * 10.0)/10.0); + + ui->chart->setChart(m_chart); + + delete oldChart; } diff --git a/plugins/channelrx/noisefigure/noisefigureenrdialog.h b/plugins/channelrx/noisefigure/noisefigureenrdialog.h index 84d157b31..7a2fae08b 100644 --- a/plugins/channelrx/noisefigure/noisefigureenrdialog.h +++ b/plugins/channelrx/noisefigure/noisefigureenrdialog.h @@ -18,9 +18,13 @@ #ifndef INCLUDE_NOISEFIGUREENRDIALOG_H #define INCLUDE_NOISEFIGUREENRDIALOG_H +#include + #include "ui_noisefigureenrdialog.h" #include "noisefiguresettings.h" +using namespace QtCharts; + class NoiseFigureENRDialog : public QDialog { Q_OBJECT @@ -39,10 +43,15 @@ private slots: void accept(); void on_addRow_clicked(); void on_deleteRow_clicked(); + void on_enr_cellChanged(int row, int column); + void on_start_valueChanged(int value); + void on_stop_valueChanged(int value); private: + QChart *m_chart; Ui::NoiseFigureENRDialog* ui; void addRow(double freq, double enr); + void plotChart(); }; #endif // INCLUDE_NOISEFIGUREENRDIALOG_H diff --git a/plugins/channelrx/noisefigure/noisefigureenrdialog.ui b/plugins/channelrx/noisefigure/noisefigureenrdialog.ui index a0986ffe2..09c0a5e25 100644 --- a/plugins/channelrx/noisefigure/noisefigureenrdialog.ui +++ b/plugins/channelrx/noisefigure/noisefigureenrdialog.ui @@ -6,8 +6,8 @@ 0 0 - 284 - 496 + 347 + 638 @@ -28,7 +28,7 @@ 0 - + @@ -102,6 +102,130 @@ + + + + + 0 + 0 + + + + + 200 + 200 + + + + Chart + + + + 2 + + + 3 + + + 3 + + + 3 + + + 3 + + + + + + + Interpolation + + + + + + + + 0 + 0 + + + + Select interpolation method to use + + + + Linear + + + + + Barycentric + + + + + + + + + + + 200 + 200 + + + + Comparison of interpolated ENR using different methods + + + + + + + + + Start (MHz) + + + + + + + Start frequency to plot in MHz + + + 20000 + + + + + + + Stop (MHz) + + + + + + + Stop frequency to plot in MHz + + + 20000 + + + 3000 + + + + + + + + @@ -117,6 +241,22 @@ + + + QChartView + QGraphicsView +
QtCharts
+
+
+ + enr + addRow + deleteRow + interpolation + chart + start + stop + diff --git a/plugins/channelrx/noisefigure/noisefiguregui.cpp b/plugins/channelrx/noisefigure/noisefiguregui.cpp index ba3578768..fd9b7fbe5 100644 --- a/plugins/channelrx/noisefigure/noisefiguregui.cpp +++ b/plugins/channelrx/noisefigure/noisefiguregui.cpp @@ -71,6 +71,7 @@ void NoiseFigureGUI::resizeTable() ui->results->setItem(row, RESULTS_COL_TEMP, new QTableWidgetItem("10000")); ui->results->setItem(row, RESULTS_COL_Y, new QTableWidgetItem("10.00")); ui->results->setItem(row, RESULTS_COL_ENR, new QTableWidgetItem("10.00")); + ui->results->setItem(row, RESULTS_COL_FLOOR, new QTableWidgetItem("-174.00")); ui->results->resizeColumnsToContents(); ui->results->removeRow(row); } @@ -86,17 +87,20 @@ void NoiseFigureGUI::measurementReceived(NoiseFigure::MsgNFMeasurement& report) QTableWidgetItem *tempItem = new QTableWidgetItem(); QTableWidgetItem *yItem = new QTableWidgetItem(); QTableWidgetItem *enrItem = new QTableWidgetItem(); + QTableWidgetItem *floorItem = new QTableWidgetItem(); ui->results->setItem(row, RESULTS_COL_FREQ, freqItem); ui->results->setItem(row, RESULTS_COL_NF, nfItem); ui->results->setItem(row, RESULTS_COL_TEMP, tempItem); ui->results->setItem(row, RESULTS_COL_Y, yItem); ui->results->setItem(row, RESULTS_COL_ENR, enrItem); + ui->results->setItem(row, RESULTS_COL_FLOOR, floorItem); freqItem->setData(Qt::DisplayRole, report.getFrequency()); nfItem->setData(Qt::DisplayRole, report.getNF()); tempItem->setData(Qt::DisplayRole, report.getTemp()); yItem->setData(Qt::DisplayRole, report.getY()); enrItem->setData(Qt::DisplayRole, report.getENR()); + floorItem->setData(Qt::DisplayRole, report.getFloor()); ui->results->setSortingEnabled(true); plotChart(); @@ -647,6 +651,7 @@ NoiseFigureGUI::NoiseFigureGUI(PluginAPI* pluginAPI, DeviceUISet *deviceUISet, B ui->results->setItemDelegateForColumn(RESULTS_COL_TEMP, new DecimalDelegate(0)); ui->results->setItemDelegateForColumn(RESULTS_COL_Y, new DecimalDelegate(2)); ui->results->setItemDelegateForColumn(RESULTS_COL_ENR, new DecimalDelegate(2)); + ui->results->setItemDelegateForColumn(RESULTS_COL_FLOOR, new DecimalDelegate(1)); displaySettings(); applySettings(true); diff --git a/plugins/channelrx/noisefigure/noisefiguregui.h b/plugins/channelrx/noisefigure/noisefiguregui.h index 337e39f32..f614aa611 100644 --- a/plugins/channelrx/noisefigure/noisefiguregui.h +++ b/plugins/channelrx/noisefigure/noisefiguregui.h @@ -106,7 +106,8 @@ private: RESULTS_COL_NF, RESULTS_COL_TEMP, RESULTS_COL_Y, - RESULTS_COL_ENR + RESULTS_COL_ENR, + RESULTS_COL_FLOOR }; private slots: diff --git a/plugins/channelrx/noisefigure/noisefiguregui.ui b/plugins/channelrx/noisefigure/noisefiguregui.ui index 4898fad03..7e1b04288 100644 --- a/plugins/channelrx/noisefigure/noisefiguregui.ui +++ b/plugins/channelrx/noisefigure/noisefiguregui.ui @@ -696,6 +696,14 @@ Excess noise ratio of the noise source in dB + + + Floor (dBm) + + + Noise floor in dBm for 1Hz bandwidth at 290K + + @@ -767,6 +775,11 @@ ENR (dB) + + + Noise floor (dBm) + + @@ -849,15 +862,20 @@ deltaFrequency fftSize fftCount + frequencySpec start stop steps + step + frequencies startStop saveResults clearResults enr control chartSelect + openReference + clearReference chart results diff --git a/plugins/channelrx/noisefigure/noisefiguresettings.cpp b/plugins/channelrx/noisefigure/noisefiguresettings.cpp index c85850181..35d3ac513 100644 --- a/plugins/channelrx/noisefigure/noisefiguresettings.cpp +++ b/plugins/channelrx/noisefigure/noisefiguresettings.cpp @@ -54,6 +54,7 @@ void NoiseFigureSettings::resetToDefaults() m_powerDelay = 0.5; qDeleteAll(m_enr); m_enr << new ENR(1000.0, 15.0); + m_interpolation = LINEAR; m_rgbColor = QColor(0, 100, 200).rgb(); m_title = "Noise Figure"; m_streamIndex = 0; @@ -106,6 +107,8 @@ QByteArray NoiseFigureSettings::serialize() const s.writeU32(24, m_reverseAPIDeviceIndex); s.writeU32(25, m_reverseAPIChannelIndex); + s.writeS32(26, (int)m_interpolation); + for (int i = 0; i < NOISEFIGURE_COLUMNS; i++) { s.writeS32(100 + i, m_resultsColumnIndexes[i]); } @@ -174,6 +177,8 @@ bool NoiseFigureSettings::deserialize(const QByteArray& data) d.readU32(25, &utmp, 0); m_reverseAPIChannelIndex = utmp > 99 ? 99 : utmp; + d.readS32(26, (int*)&m_interpolation, LINEAR); + for (int i = 0; i < NOISEFIGURE_COLUMNS; i++) { d.readS32(100 + i, &m_resultsColumnIndexes[i], i); } diff --git a/plugins/channelrx/noisefigure/noisefiguresettings.h b/plugins/channelrx/noisefigure/noisefiguresettings.h index a7eea22c3..a569c7e17 100644 --- a/plugins/channelrx/noisefigure/noisefiguresettings.h +++ b/plugins/channelrx/noisefigure/noisefiguresettings.h @@ -28,7 +28,7 @@ class Serializable; // Number of columns in the table -#define NOISEFIGURE_COLUMNS 5 +#define NOISEFIGURE_COLUMNS 6 struct NoiseFigureSettings { @@ -69,6 +69,10 @@ struct NoiseFigureSettings double m_powerDelay; // m_enr; + enum Interpolation { + LINEAR, + BARYCENTRIC + } m_interpolation; quint32 m_rgbColor; QString m_title; diff --git a/plugins/channelrx/noisefigure/readme.md b/plugins/channelrx/noisefigure/readme.md index 5aae88682..066b6cbce 100644 --- a/plugins/channelrx/noisefigure/readme.md +++ b/plugins/channelrx/noisefigure/readme.md @@ -34,7 +34,7 @@ Determines the size (number of points) of the FFT used to measure noise power. O

5: BW

-Displays the measurement bandwidth in kHz as determined by (2). +Displays the measurement bandwidth in Hz as determined by the FFT size (4) and the sample rate.

6: FFTs to average

@@ -64,7 +64,7 @@ Clears the current results from the table and chart. Opens the ENR dialog to allow entering the Excess Noise Ratios (ENRs) for noise source. ENR specifies the difference in noise source power output in dB from when the source is powered off compared to when it is powered on. This typically varies with frequency, so values should be entered for each calibrated frequency. When a measurement is attempted at a frequency for which a value is not specified, an interpolated value will be used. -Barycentric Rational Interpolation is used. +You can choose between linear and barycentric rational interpolation, and the difference between the two is shown in the chart. ![Noise figure ENR dialog](../../../doc/img/NoiseFigure_plugin_enr.png) @@ -87,6 +87,7 @@ Displays measurement results. * T - Calculated noise temperature in Kelvin with a reference temperature of 290K. * Y - Measured Y factor in dB (difference in measured power when the noise source is on and off). * ENR - Excess noise factor of the noise source in dB. +* Floor - Noise floor in dBm assuming 1Hz bandwidth at 290K. ![Noise figure results table](../../../doc/img/NoiseFigure_plugin_results.png) diff --git a/sdrbase/CMakeLists.txt b/sdrbase/CMakeLists.txt index c07266312..01c14871c 100644 --- a/sdrbase/CMakeLists.txt +++ b/sdrbase/CMakeLists.txt @@ -191,6 +191,7 @@ set(sdrbase_SOURCES util/fits.cpp util/golay2312.cpp util/httpdownloadmanager.cpp + util/interpolation.cpp util/lfsr.cpp util/maidenhead.cpp util/message.cpp @@ -396,6 +397,7 @@ set(sdrbase_HEADERS util/httpdownloadmanager.h util/incrementalarray.h util/incrementalvector.h + util/interpolation.h util/lfsr.h util/maidenhead.h util/message.h diff --git a/sdrbase/util/interpolation.cpp b/sdrbase/util/interpolation.cpp new file mode 100644 index 000000000..69b194daa --- /dev/null +++ b/sdrbase/util/interpolation.cpp @@ -0,0 +1,18 @@ +/////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2021 Jon Beniston, M7RCE // +// // +// This program is free software; you can redistribute it and/or modify // +// it under the terms of the GNU General Public License as published by // +// the Free Software Foundation as version 3 of the License, or // +// (at your option) any later version. // +// // +// 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 V3 for more details. // +// // +// You should have received a copy of the GNU General Public License // +// along with this program. If not, see . // +/////////////////////////////////////////////////////////////////////////////////// + +#include "interpolation.h" diff --git a/sdrbase/util/interpolation.h b/sdrbase/util/interpolation.h new file mode 100644 index 000000000..0d0e8285d --- /dev/null +++ b/sdrbase/util/interpolation.h @@ -0,0 +1,99 @@ +/////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2021 Jon Beniston, M7RCE // +// // +// This program is free software; you can redistribute it and/or modify // +// it under the terms of the GNU General Public License as published by // +// the Free Software Foundation as version 3 of the License, or // +// (at your option) any later version. // +// // +// 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 V3 for more details. // +// // +// You should have received a copy of the GNU General Public License // +// along with this program. If not, see . // +/////////////////////////////////////////////////////////////////////////////////// + +#ifndef INCLUDE_INTERPOLATION_H +#define INCLUDE_INTERPOLATION_H + +#include "export.h" + +class SDRBASE_API Interpolation { +public: + + // Linear interpolation/extrapolation + // Assumes x is sorted in increasing order + template + static T linear(Iter xBegin, Iter xEnd, Iter yBegin, T x) + { + // Find first point in x that target is bigger than + int i = 0; + while (xBegin != xEnd) + { + if (x < *xBegin) { + break; + } + ++xBegin; + i++; + } + T x0, x1, y0, y1; + if (i == 0) { + // Extrapolate left + x0 = *xBegin; + ++xBegin; + x1 = *xBegin; + y0 = *yBegin; + ++yBegin; + y1 = *yBegin; + + return extrapolate(x0, y0, x1, y1, x); + } else if (xBegin > xEnd) { + // Extrapolate right + Iter xCur = xBegin; + std::advance(xCur, i - 2); + x0 = *xCur; + ++xCur; + x1 = *xCur; + + Iter yCur = yBegin; + std::advance(yCur, i - 2); + y0 = *yCur; + ++yCur; + y1 = *yCur; + + return extrapolate(x0, y0, x1, y1, x); + } else { + // Interpolate + x1 = *xBegin; + --xBegin; + x0 = *xBegin; + + Iter yCur = yBegin; + std::advance(yCur, i - 1); + y0 = *yCur; + ++yCur; + y1 = *yCur; + + return interpolate(x0, y0, x1, y1, x); + } + } + + // Linear extrapolation + template + static T extrapolate(T x0, T y0, T x1, T y1, T x) + { + return y0 + ((x-x0)/(x1-x0)) * (y1-y0); + } + + // Linear interpolation + template + static T interpolate(T x0, T y0, T x1, T y1, T x) + { + return (y0*(x1-x) + y1*(x-x0)) / (x1-x0); + } + +}; + +#endif // INCLUDE_INTERPOLATION_H