cutesdr-1.0.5/0000755000175000017500000000000011720506076013132 5ustar bottomsbottomscutesdr-1.0.5/cutesdr1.icns0000644000175000017500000027413511711303214015543 0ustar bottomsbottomsicnsx]ic08J jP ftypjp2 jp2 Ojp2hihdrcolr"cdefjp2cOQ2R \@@HHPHHPHHPHHPHHP pS ]@@HHPHHPHHPHHPHHPS ]@@HHPHHPHHPHHPHHPS ]@@HHPHHPHHPHHPHHPz(˰C'n37k)j* w]r&: ʾje{ǜ9 dioXmaBXin /Ѱ0ʔ8ڄz;ʿq>}@AaPW%y";L#^chH.bV|Hl2.nu$BRTMCGDl=|dʍ3 grek 㚥rFEapۗTi:<]S{Hj,W@R:E~wdX#hh>} 9I*ҫS+G^[,GZEWgۀe)7 ]Xq6ArEV`>6^p*%ưNL) H$ ܷF(k@7{8f4-4 xN}V+Hx$H(}Ǵf'1ۢFʩ*]ȗ/ 6eDCG^[bj1=favQX D!|dp53_%YCa8`xB?Ilވ(r@9xcB3B,j|@"50, fŰԙ-$}Yԝ78IWhbl<ֶO؍DE|OWf.ÍMyԝ!i䘵\ H}n8Ζf?М4cn 'juugm_wdzE SWv$oFnz t,nqϾaӳ HNG*> "uVmdȬq&,jq Pʸ_DIT?8<c&̹S"^٤fӃ6zk RP}9"W~N1H\Bn/e'l2VƄԚe\P)uV=MK}PN(j"7eQP@m oH*{Rk3v+ZQGZI]2KL ؠɊuV=R5,lL]@9skGN'V|rӠ0#'p١]n5?gvD aǢjivfq6+(:ӀJNFY>[?|jhr,W=Y㴍:D1;: a8|xNa^i Oשax_5]w&'0ٿy`\I؃hɡg9KLlA$9;Wx{u7ۀklg1.TqycžIbB^+"e$~tKj5GW G57Ѡ$3_62O~vl} qU*2Hx5!@aeZO!B؉[@S+fOBc5̓(.9G(<ժ(n)\Zk(CI Dhv'ZIl(.AUGuh·~SxE)fGHp%*;Or7SyBnl /+`|"#uکIFV-Mvxuuh9\mShAݫ8͘'?V ^;J1BvOFtֻ.Gah̭~lbh|',C i?De*ӎ@S`gXx[eC5>> `$)C[cqpuk(nk@*%)A#uʩD# ҩPଶŧ&]4 ~$/ogἰuw](x.HqCҒGKd@/GU>(. _:RfXg͎,sRYUrK)0ScˣK$n\Op.7XQ%hKj9Xm]nL'\FֱbRcd~G.݆EYZ# OTcoFc1K-2cK2bͨ&1vKU"Lު8ҽ*ދ7H+V(# Lm;9pn(`Fq*j.a*aHB·VuR:9B(\R](i4l$٠&J(R?s(_Mq%aRu~a~޽ZƖMtH "}V#3rENRS.7]؟D  elS/GhBB'䀞pVGȒ@p*Az{yR"D)PHVAyT>NyCv$f_=uoaAHkVhFsG7G_1> 3c[Fi)+ +"ttS+e}òK+>&M'wiK5F^Kypsߎz-=ZF#/ʈAt sWQF#r},a3jECNBdkd?oN&'[ޢ;v>݃w]3 ֳ jD/!85ĔHlfkC\)lbm]|' (9yYLz'2^eV l~%UuH5)C ;wm&>ypX#ؑ݁%<658#x'_1[2ZU\` ܛ7Rϔwz(XEQ2fdrWs&.$bH);͢~P`nF'k@sn~pan@A޽߷URi`c8ȍ:kMvMW]Ns<9* ?5GF BM@}zOqxoLl; ߗipAZƈt]n0JXyf\?kIޚcӛ1O(+ h:n9nYzf1kj`5:"Z@ o0%5v $ ϒF־Y.=׽{퀊\94*IPLюvv{~3/6P}A%]adۜ` :Bmu"x4w9aT:RxjܡX9+])^,j|(u ϛAQ+ϋWOuQM(3Mn@yc#r ?_pP׽SEK2e|hSA !S39X〪a(J`XwYf FnFHEQx`#W UۥTf3'\6DBek]zK? UoiT͗WJkb ecZĆGǐ,&;aMcc:O,n*}{CL*#yN]Y7S~6;(vm}nuA\*L?Z #)BcYfpkN1=Y1KCi&U8 ad'o]{{ \~{֟ OVFUv@/?나& etuSR0÷]v)SRs+hǀ~+RGȻ8l _gd$〶niƽ~qg9‹7 ;Z;U0zz ׵ tV!h{HLj"cGr ZhKZvBbo{6b('{|144L`bug[&Ov cQXd>1X~fB˜N-/!1􅢥vjHrڡ(.$m͑'=)TV4+0o8`;^悇؋7!|/ 벐 S,U$K\G܅ZX(.=ŃkuHw](11VN&rKe Y}}+ V3+X~1k1PK@&'wTE. )~~v?v .,ߦ@P)& QC#|ޛC<aPbH<3D, /VʍsJ` 3pBRq!8Moc~dB'E%u{ tq3͒]x7v\5^nU2Bw<|1gWEOLPb%lκ@ rN+KPh$+FF.MdeH+ ?a<-f8ZA~y~'?1mBt<\=ՠ|qR8wivZz>QDhqlJ;'n* 9I"EOum v90j,nC.F( )D7h+2r T*t8.jg_ =3@ d&A4e.áy }ur(۱F_crby]aGq qd'!X*b?::fֳ{OVK\B\{2@\ MQ1/%բpGqlʡE9n Qg g_Yrkm$*S>}euɗT45Ɨvxtnt*tGP-#(3KbqcF~0P%}9^1=r\/RJ CIsSqOC>yRq쎞YO.6K;+$JsLClm'N K SAF E\Vxq+ n@ 2΀_,e-ҷ[,~iirDZԝ޵J#!b[ih,ޖe)8KJIX^5C@c| ,wĞkߑ}0_ie2)Ƌ|En^[a.ړT#PKZYt7JI_L^J09TY0#iH~ވ͉Otܓ/2Wt7WTGqVUI$IXW F;Taq҄0z^?K8;T;/2l+7nBktiq?nJ0^-EhXN'ID?`[m<~Ig=X'wUgK]AGuld%nnƿ?JKp_v%4K 'a4u*BK.7IUWkǺcrJE`n,wa@]hBǙN ػ'p),&eÞJט %PV$ ?fҶxb}EyNǀlݞu,@4Ϝ1ICWKt{yj-uNv ' s\*zMVx Xio/f!N&^L*ZHQd̮}:-*PluZ*"ԉq>GER`p87K{z|KJk6كՎYlŸ*k1;/FErM h[!Ϛ+ 8Yg^+cBHej z2$ S.]^ePk}kî,ÝiYE #5x=qآO^ hTeȂ#@\H)2W.B {]$͋{|VUe7!Spnl# D Ɇ} %RxFp{CBKAdB^HK@octnyHF@;#G-Li(ž:yXtFuюb(OEf!)]_Vأ:@:]KMBn#[*k̽8yme NjH߃ CكD@ZZed̼JAD~1ɈS(:Q` B (k+м^[􁯌sA|ާ>h;փA O>ŽE-Řbe1롚8I(wi|‚o weaIz^ Zjnkc_ Y6QVb'&2YpwzsK}J Ҽ&Y!S o/@ m 3Eu% VSHpڬ $\a.yب (YѪ4Wj7YC"%L}M43[axZ ynEp5:5׼N)qcݝ7_,xKyQC W0q|J0^u>{!3Exq6g HX˽<a>]*s nR嚤;?l73IE&IxR+}:,:6ύ=L_[C p2N׆Ʀ%_uꛑiَѶ 3h! c  ع ɬa;wto$5m::+EAq_tE/&]elt@N"g72fd#XhK\_C; 3IPmjukJ9dpxv^e(2xIm K{Hw^?ܼ秝6+O=!8Smm>qq) ĻNB6J;܎AݶݹLV zwM;3zAr)BĶ@قvQ%0rqBahlڲClN~&J&:se%#p#sJ~Jb[th՝B5I]s"Cj`UT؝ٖso&Uw 5bх>W +(}{$j Lk#蝰]O3@ !R) +w%de`'xUs&if.;-#gF8Liݗ^• oܒI$M=mߛdQJ{ܕfR lL-R_:,(7Y^~Qԡ2r=K̫ahzMbi~[4:?w`52hO 81̋t@_ke|,N堸҄ "?K$xp5؞Yhtc(h($v2b?tI@RO*Э7K]G޸l-d93 02g)LXL _!\Bl#mM-`} pN%VQfK:w -JM9ȨpfxɈo y8t %縮XuLY殉YsxyOpKX%0ŗֵs>XUQC-e1g~J. bR;2KF8GsþՓ-DHo1W\ ^u# A0-azzzR}Q ^s!څ*iգ0 v"j.[nrK?I*Ldy;Ak=բSka:.SaѣĽ2X` 0ԃ305.j5=NJe("&9l\}+p2y$Y~C0(AY2~n!2=B!ljj٤&>jvfV؄q ?Qd8rye%WE10uIR5 ֒-lt8=/s  p(c%Bʹ-93FL:g:P 1FV9_$٥d=$V;rM2 Ok_`kNɼ0׀)K^Vy> 9`߯S4ycB- ']n9&"op U.˕L<_rUd/6e/[ swm.X.P} KlScC?9 VFTQbOȢ2hv:Ҕb+QsjoZL\kȖn\;n,-%[B >:}sh/>ᷪnKW%7.4p\ΞKXPBRYSkK-6O%*g 3s9Dh 3 @=Xu/A վ%b5nqm#G$rFNfgTy̢Q;H58"=0KsL/ /x{'f _M"'qa3j$~zXʅ|!EGql0+w^;'djOLE>F8 `]bxJ HC-<̙6`׽D؍,wj4z WRnzq(i4(‚Opub7vG1 Wa۳ByDJ Duםϥ9nfl3M၀Caf|Mvxu4le U}pP2Iu7~F!5wp'_yhՓ4ZZ'.oOjΐ;2QkMӧt9 L(gdPj&j l`}&Ud?% VS;5a|@ZB|2n j+8ʜd<ңlCU%=1$]SKZ@MyHg,ߗprl3O;j:85nV> 0$S>k;[U|۶ CL'\s,kصP84\en>mf-mjP@jz{d%`.ש<#,h.LWBugʽ|>ĭ6٨ m7}%J6FhlXS?Z6]v/`:5{4a*Gc :}7Z'!h?) a!=;Wbz'nUIBF3EO8ZKKnz9`=&{3Vh<`A]G2so6T)@AC])!OVJe/nD2J{ln^U2Eo-6Gh}dr].Hꗪs5xxkO;T/js P)ΊidYŀ*J5;;ux$<(D4.'OO?.آyB J{`2 r|4:dqYB}X }cHU'btg)|E;O}Ļ$ў6.1"jM(sF)kКyE-tTq$$AdOѥ}eo07w#Vkd=@A}[PB ! (C\葾!W`Etx|.٧M.;q&n߼yt~)*>* cr @Yɢ̖No oqaÆiYj$i(Iˢ j=Q)DTZ$9[@=+aHH[2rR2ay=+Nq;R2b0L֔Wґj^P0)BSo-@f՞?l"| I0m K ܒHUQ$1XGgپ4J*#'?C#Ei *x$Ug dؘHcO}[4:^̮!B a+}.wwL z?E6ܺű،_gKR{EǧxEހmߋfVy4EtJ $⤕B'` h1-fQ(PQeǤz'񲮦NեbK9v/zv3Óפ tT.hǭ[9C皩H5Q@%$!}YFCvn2kレCR1π 8\c`e Ú(mi!w\Ruq=^6U]3vZaGVq;W͗V_)k;M ?i?G(;9;209$V=!|DL:Y>'IM e_ɏ"}վ)%; pX O7WQrà#,  ~rnźx2W=20}\I#1#vSIYi]-ci+&Ͻ_z` IHV|? .1'[D8/Iw8 YxRO;#m剟~MQ2nH^ `=3jؓ2?53h)m"9$e{RjE^Rw?=Z4+ 6S' >CO57DB6<-wC4\dӡ6R!K? ]:x*$Cՠ6ph$Z2g&oX͂QDٞPPt%;vJ )%>HzǎjVc^5-M{w :UۈaMYp5Ep@P<9ۙCL`Q8.];խw׼q9" GLɦt yݴaYqsS|NH"6BA"E+#/,Q FWv6 *;tll?#![NEPG)'r!q{uEˇs\+i޴77v [k*Bԇ!AR6Bg|׈9Եf";2Ornntߍ*kr7u8E^!-ts<3ziOZiMR4<>q"K]KF'MgupW&R!?% VSp0xOĚ%DI QuCd'DTPs ܍1H1LnA"walMrDM >:cxR;-3w J6)W; :Ej j.~ʯ\KR_Xh+kx5d;j\Di$Mm6+ :ϕ#}osX|C-wϳ ۷<'XY_Os;6l"3ԯȪ-l\+jyM'Zg,xG^>>tG$\ Z$46yl5wQgp B~y^5~ktiZІ1-8rIWNIX5xKEɦ#*Sm@#UI$I$rI$rqH@ն^7J ~ۜ=7z"Jd Ep':B%9/[4:^̮!p:-܈݂aDzO$;:Ú1(q1ލ%`Jpw6v%=Uu6tX6vB~ijqHH0ukr^d>,酾0ֵiMR/D~඄vrl޽ TA.(cmՓ#RX_ T?mvw)? $UDXPRQ|~ 4$ml>4z#dd6& OT!Dtůʩme;m*&b#WmNs_/99z-|&`zҞR~ "=?M1mG:|pxjcJ7Y 4fm`>{ybږ%-W \ZpTek8 H1q{՝ ^WriqRia@%HaB_wu5y35)'Cw @ϸZ/XHF ßp&JX<kVnU?YHE{T+qQgt?O&ϽI0!2+vhN,ծW'z-_Vg{c'zBFXieӛ–]6VC4 ;6fhjP3i* ﵓ9;V.>"e7_ZnH~ :U. C¹L蠩"à]CY@oyQ(F'dt;t3`aw>nM@ppNO:޾g!Q@Qc& -/w-pۥD}62…ÔOiPgByp6k;pɚ1/VmO=!?_SD-2b]Vty@-o,1,>CˡWA*x^}wc~w2i c3'~˓pzrx |.D@g{ ,w_PzEI_E(SI'L 6ZO0p~BHXXa[gv@mkBWz) i>| Q %WHm@5+*kE]7mpWeny%z3j/|3+Ǐ_ZxqrNo^Gdqgo820Sq[ #Bt}$ʸ]=gx.ANzgʀЧwm@Z1+C:I<ɶӺ&LlJ-cÑULNC@]eNS1`!{:s|}$C,kƘ`H ƿ5¼9iʪZnbyP̈́mD:!L2EFq7k]KynWUVb?,I;E䱋] 636Kf.*B8. +>DyV_Ԟ>A̦,ڎ~ۊ}v"ތDn)}4p`TTdcrgZF دxv|1:ⱫF㎑]gApta ^K dpp& У&:ۻciRbmˎ?B>lZᾺղFoٞX_+Lw1aGܣhHa0FZaAm)9{ehjk?Q)O ֬쏚5 g,8Q}ǹ'4 Vp Kuc"e3// (>/!I) mz#Zv.VCe=Q>nCE <2Xw9愔j=m yݬB),.}W/%n|%D^ƴZC (M)y6( ЬqN1sp깊ԅk)5m4)Bgla`tNɍ^ h*?62hIA.[U(,O[Ju :wY63dnn jݩ!oNZYu m밃4,La)Qb.SC}躵̊zahQwŰO=R* E bi:YILG< dEP;jW0T1 a +1۝eAТ$':;8M3/:PRJ%@,R2LI1;4!Ht`@H@{ C3`^.]񴢴!,.\oik>u(?-J^>M׀N@N*\(+?۟3C!:gԧ| = ndM=&pv#Bdui%9CwbUy9Ĺ1$2ɪ 8na J9 ڲs+jKKI;j6f𝙴y&$㰈,i DJ۔&ZXۅQ4BU|d^Et!{^lx%J9Y<5sݠ&|#3csl-('[^g\>U7#1h? ʐ,-g& L! ᗻk<-HC"vlrlT3$"ƨE0~*I۱="Ƒ6z9acL+٧L7$"Óc5F?;p4^c,~R[4 `z&+l^>%tC ( U|qb럨 j햀8Dah w*RlFsD@wvi$Q_rCjD;,B>͡Gh>ir}s2aZLBa~% >Kc3ru`gt:G ju z%l>VOr\&j5=gR5Jhu 6GQ;X߻!tKVd>JӤJG@yX[p K~r&30 #|PQq-'dvvC_r*ғ5@6I$ B,YoRtAyXV&I9PaD"4>(ԧFR?cCi{>p(lrPIL($I$I$lZtv ~%ʬ|dAVͤ=s1f/UMXyRbV=seKǎ Gͬ(eonSW/FȜN߷kffj-HtYC̨FK dk&Smmܞt$+( 1~ > c4HO2jR#Q _Z3O%~xk8+͉wY^*?_Pjq3ԮzaO0`RĂ1UiXsINds0nOHaҁ/ %Eeho#mV6)P3@ `}e-k +A1c8n5jI6mmmmmn^B? qC3Rd_UPT^l+^(8 OdܒI$I$I$I$I$TW% ݙRc8Y_1=㶐Br0OU= q(ϩ>eYFY~-|dSy\̶r1fv0̫uO/}=~v\G Z6@݂zf c3E#A$I$I$I$I$I$QImu<+Ï1;3DA}`^kn8nNCRvڿGeŋ4dU>|zjv H)cb4t=CqC1鬏nOx2Ydyq#Pt:Mf`?4;@ O˂iڬOxsޓ;'/Ae}9hY Y# [Y|c@\> ;{[~OMJJiamnazpq^-(z Ba>"PY>k˶wjwʥ"5WRX;ΓJ?'yc#/zIAr*92j5W N[>˴\LiJϲ7Wznu}n_oE_WR@FYzZc|n&ɴvIs4;Ѭ VӊѵgM*\W!?LY{KK DFD|ѯ AS?=`O~k2ŐmjO .xF$ r?/G_R5-mѐ.(+F1Z-rm%'bI]zq =NjW£m[r(2X{{/DjPRgvQ dNrMlk|J۾I$Q][`Fď#{\H-uͥD+qNܐ$s$VFIa!_ 16_ wHUQW(hWO=!9g÷h?S2#⻛/_saCsSq`.afl*?@>K kJM/"Ԫ-q0iL*願e _%yBCӥZ$KGP;D11ڸհ"~J=šbWO)W,?3ģ$΋)%F#f$φӮs F(g;[""^\?U#"AK:B8? (!j@͠_:}hW5ˊ#*Zs prMc,HXǑW=LE"B#;׊s\i$H%DSҶS !ްńv$-3XGo`_Ȣ(akyM6eXz%l^fIQ-*[pwJjS7DҠض.E9;`Aܧ.L5b`b?eĚƣ\7o8`fkLayλ J>Vϟs=8`NX7yH{k!V-)UIh Tlj d$ڭ`48%+MǬACϩЍ@N"8x:n[#G+GT(D܆*ѨoػԧP+ U睺KA$ /]Z@ QKl"GJtq6.}]hm1bmz܇(3Yq]K[rÌ?ڿ3;J]?ig ͔%fr G4ϯ}9RHƒ|LJRRł7,X8EIFI%8xfS]]E {" ̐f?m?B!D0Գ\4 8RZVε m{;[8xk|y*6OPhkioS`xM@QZvY8kd4K URtXXQxᨊJcOfDӦ. ,p>!X0 b>FLZ|$wQ$@n8ܗMfGK鍵J<|ѣ9=qv^(Y3Zo ;G F)M%X(e?Wnl0ҹ=Imtة69Dw9-YYݬ3GS)xyAHIRhz1|JiC)3q;e-'w9M<>k P,ifD,ҹ0y:NvHNjx4$!7, ?a}Q[ V%pglWTOQ4r}ٶ w#Ô,`i"S?_}tHPj+9VQ܅$eԟ2ٵqF3?ajh6H2húh-fB!,x/g:zؔN$ܯs6 i^y62_uk9 =4-qOXyV-هٻAa:w5d#6neu?EW t^"(܆z.Lɪs\7 {1yr|p E}Yρjg!d| *\&,OM$ '`ǨHn Oӂzsg`cs5b" J0hȜgHəS5!tu`ڬ1F^>DڬSuK®y3玄 MJ477aG/pGzhy~Y|7H@HŽ n,P^zEלuJ]"OW-}zveTj6ʷ:7_^Nu"SiD0ďK.)Ut?b~:ȯ]~]1Bi)?S`D bw%֞~3gߡ}Nf,78 7`Bvp;)#Bk@aK&2G=-755~NeACX &@Ρe|jyfϒ`)i;Ͳ@_1?g)*"<#EznPṊ.5ܠzK}j!K;^V :n"X2琞h@"<_|K1~M^ՈCp{/ }7>f5fzC\ɛy93Z!l`?K cȄR̾9fo+Z@M7[chU)WQ$LJU~ jrm{9E^F1<0r~i*`\e)Ylm߂TD$h[tLj "Zd)bf8L+A5Gt ſ(!IlSVguȚNj63lLfRd8u)6w8qSxʛi?B-V~S35@tjDֳpH4ujt`(\ŝÑ3أRZg>t9tjr-9.S7ʑ?8Zfw[3sH3!=XŬ>#F/mMqc˳TLLtF[x(^_I%E,bTt7Ik)O(J"<_*meG  -$NbHP]/8&:ģBK[Ӕȃk)V7n(I⨏Mz(õk }tgn7䓅W(ݜ&S ;Zq] P [+5nwH" T7 P!HQA$I$QTQU1 F8BY_*&yk~miBeJQU/NPEȀ5ˤimmrI3O,@{G_WYM{qje,dIC)drYGQNR)ezE@3+ѢNn},WKr4rI$HI $i(2zl w+C%n-P#] 1pj`L#Lzh@ʼDXiw!S$m<5@_Y[` 8Yf \3YY2ѭyٖ!w nܣ Nc 枮{LCT-OFi !Ke &dnzSsc!r;6trI$I$ UYZЂ4MHܣ"TU3ɕs݄y5P&֕0od=Τ_jI>xQC+Xmx c`'rNa\v> Ls<:%\ *3&=BB=_>j?T#9j;G@*Qei zt)ܧB<[G407@2E!wJ"Y}{7w)HvJRHpZHA͠'zbF=!A$I$I$I$I$I&2B?V]lj^=1A 1,l~KδF* |* x.۪qMmmmmp};RMDB/ V0~yHO@@'M=fB CAXRGv3 3 ?PC8$'6MܒI$I$I$PQ[n21b}o@*/l_̹rAeZ=7p |Fm+4M1MxpƊn (՜fP`T`MbiVQd)ka_#= A$I$I$I")-]H p3BmM4=#0Cx^͛%Id]qpmBzp'esSShz-;T o0ifJ̏>d/GjlK@TJ=$$CT7/7pw$6;jxU|>$65|f:AXpS{(?Ǘ>/lIB/^/<#C(+-~DUKō-~SN -,tv$"z\JlDsm|/Z&"Z"@qz|l6`MMg)G_z޺([x4+VZ=UM("#tgo'ɛ:S]]|stK "~&@ 40yAv=u 7Zٔ1UfS^ZrL^ĢﴠscnNA/qQ+вldFvqˑsD͎-U} :-W zxY{|%{?yᗆjme?+q<8?{/#vg"tu+4oQ0fq+qzy Iqsvyv:=[P-D^R=^#@8+&NxWeJdq3t 6;T&-b/lLLs C|#};}8պ5>dRH kE/jrI g^S}` LY»ci6soN!~iU [e~gvdGC-ɝd!7(S G QcKk{6[v^3&E%1mj>K: 0n5_r[Ic.ה_zU?%@y( D?Νn7OvL{EZPY1iH|?bɩv89etװ̕Ŀ;_MA0MF|]t}꧐:CI$#cLQ.@G]]ELƓpWO Rjt7l9n#/U" &1W?/[e~r&Ս9m~|ɈfX;C W}6þ6WM[IO!Bt ˀӠuAg3BȮ6qN:;ߦ=#~w[Zs}G@sɟf``<s ۖ?XI!aco>'6%(<~ݗ'/$@M_YlvH!O =P{%>6Պp<# >!u#J'U2e'6Mx,HKm:%$NڒWy4:8\Ԃ`0$EbRj} [_7prwˆ 0LUW@e'Kbf%g6ɡ!36fH2FŌ .2 p1C!C4ٮxl-/N浤c˰zSQ?hQ PCu^3xԩBL)/̠wKݿtJBR)a3%qbGpFQێ2>|~|DA·;y_Ó9Z__e719L݌_8yL<@vC]C|{t67[eSy.d\ewd|C 9n4R{g"}<.ZŌ16L9/_op-fOH)|ѫS Aq(<<8(|V;4OE׍T۪}=kPV BvG2;31n0,;gD2<:&щ/~ DBJvp 8"UiץbWYv+\ SOwd/#K~e1D=nxp\W;b5Ʀݮ>/ ^o-Mo+{#S\sH9 H/T;O}=nZ1jqkvu=!A?H^oڕڽIjKߑUF,<.[,:OW m՜SbsWVzV&Oą Whif ̼V|Al7lu`ʾdR9*-Rga}asRC|8HƂqWx/!WSV> '0x?`D*gҪKw`6=`HE:ŸXB 6^)1YAj JX }زgQAsXCG:?Ec8޳_=$'c\w"dy2 Ub*ksbLW *Ebpb;( \3qlap[dk6:!dr,sJ@ [X4o{Mqе#v"g9|LuƟQÚcLDkeWC<&!DW f0cjeRH`dMBl*QKjAB3,jHz(lc$l𽓢vƿ`GJY,%T5.LP2 [ot^"cgP;LT RhB׍xR8 8N4 54T`cKp 'm]qh':Z\x)holk{Ӧ9ܰb%f[IxOk-qԤ[CEJ!.&5fXQ%h*cNMuq1 uGtE8nJom|O)+HbasvC_\$RBatM7|&!{Kyh:: o+Og410;M6!9ml>]_t?aXY3;kS<2r7aӎx2TKA'@*\}Aw7WutWƙ.oaUѯk\&F+39T$bGC^g2;8`ȭk>`n e $" 1J iZȉ&lZƗU6);0^[:qAZmcȑ},Cݥ;W0e}Ŋ])}Rn-_T2$,:KrJ[jt=#Vg~3 <5s_/'c˼P /# SCPu^)PM\ke)3"Ȱԕa߭dVVQ1 a/@9co9jgV}UWiq]m/LME]8Ȃ 9Ѵ #Tb_Qf-ΒVy} Q=6TaFՔҿM[Zy>)a5̘ZbawI<a, A!~Bb- В1ZnDaWd T׍9 Th`zM*x4ꁩDm)rB ?)"{,@11fuǐ KPiMQXkU# fƠGCiqؖCx'dfdFkosC.n%H I]짬 &5|Tr /;y  ^P;?̓!uN9z&n2)#yOPlxx/ $X 7vXIcd8w@[XY7(=&Z@G9,^W[v.e^ݒG,a*؉*5h^jO2c ^95R:~TbJYqFHT*.ȝ~j6>``QrkBb5Eh.v3&CWXf-(&a'+WcXO 3huKkT $| zM& F60@g+gC В_$NuᏅ1s ]ros0oT}!="y5L, Ř`Yö_a*'mC0VTMT棟&(ٲ0گt'@O`z Nצ@D`e)lQ^۩FDhI I^ܯユ>"p{7`^aA?HX>hϾm i+ wrtNMO<uǿ=[!nQv 1f϶Z?cQb *l$}p,b<_f30[gFH Ϻ0#Z@tnI$I$pԞ\qNa%j5ф"֘1B1P!lH 5Õڂl8H8S¢IYQM07>"+>حZmmn9 #ObOpτX ~Qٕ r]w"|"C,k}>wYG2k7J zħFFul` $I$*!bD6I @Ӛ^Wp1a͂s:f7s6o/4p!j8eԤ-2<tury}4)E-`Ê^d^[5OJXdy,)lws撵b͐jTx?E+c^,T10f˴uH~0.*U{stI|TE&q÷U llR2""źT870*mMJ|,xĎ̬I$I$$eL s׭C$Z,ό+`mzR&1$lA0* ?K~]\e=@u鹡!S:kmSR%.VW4j#6:8I6> ˭\p{YpKӦ_Z 0Wv^Ƈe1/@FIYqI=Bʜ{mmmmmsL!vO<. E2с[zd~I/04a>V6qwTߔmmmmm*DrHK(yb]b7+ˆ ';x{X,(փeƺӻ=C8$'6MܒI$I$I$#@$;>lAfS6сa[<$TMSؙwɔ&cp#16,ן>LU5I @6.h21|:"$Y8†; PKxI'oM^mmm׼B~O#TǤ5ko6Q0}Lu}̼O߻ 7ՠr"mPI5IGuׄ "t[uNrl%X J ҅'^…#'1 qెnIu ZxI+kULr"qіfM U RgTS47ӷ>0pY/'QT})Ȝ ҋt"- Db_v#'UdFX^65>Xd'?do >%F|i-Q%TӕŎm}G3Xgsɽh ROqg9XN=t2vij·o5d1#쟒d-<7\DZ7+bPҤsvU$0Q/IҡcTX}0NI8TWx5B"n 5B(ɍLQ}$u{*]>"<}8ݷKje,t&s9K`~Y_J ΜNaJL\yr8xYUd<)2Q4U㇬>U ú$EGu+20:aևhFgV`4ÁGvK'0Po6~6ek$ 'M[)}z/A= a.Nck-/ _.^S}e-$P4hpwdX0#s[1%瓠 D{p+ ܛӷd< Τ]xa.>F?WC:H ZQf<A*F]".ƉYJ<ϯ>%8={TnH|^ObpŎR#}WPХǜ.#%n%nFRc//%Asecǟ :ѽwk*91Z.g;(:sM,շVMN[{}*Ru11Dhaك~W /~pښ]L$ZJR!\GܽEuy *Hμ~(`O#g-OQI> D FJgmW3++r?2=> f`um DxNj R$La|K:ZAm*_W߄4ҰhPlM' G[y0'/S5D;K^̹#fK-D茡M`"oVe]*YտqLcQ S6,ss-q)qa29$RM'&tvF;Ӧ_6v.d>HDDHF1s<$#I6%4'͉À݈O?5rhbJ\ kȻ*lU?8Z'sit32pwNMLJIHGFEDCBA@?>=<<;:98789:;<;<==>?>?@@??><=>>=>?A@:Vok.q…(j„xĥꏥݳɃgܰݸɂzҫ؟ׅݸɂZຂ ݸ]ŁDŽݸn]\\[[ZZYXWVVUTTSRRQQPPOONNMMLLKKQz|{zyyxwvuttsrrqpoonmlljjihgfeedcbba`_^^]\[ZZYXXVVUTTRRQPPNNMLKKJIHGFFEEDCBA@?>>ݸjUUTSSRRQQPOONNMLLKKJIIHHGFFEEDDCBBAA@Lprrqponmmlkjihggfedcba``_^]\[ZYXXWVUTTSRQPONNLKKJIHGFEEDCBA@??><<::98766432110/ݸiTTSSRQQPPONNMMLKKJJIHHGGFFEDDCCBAA@@?>Lpponmmlkjiihgfeddcb``_^]\[[ZYXWVUTTSRQPOONMLKJIHGFEEDCBA@??>=<;:998765432100/.ݸhSSRRQPPOONMMLLKKJIIHHGFFEEDCCBBA@@??>==Oonmlkjiihgffddcba`_^]\\[ZYXXWUTTSRQPOONMLKKJIGFEEDCBA@??>=<;:998765432110/.-,ݸhRRQQPOONNMMLKKJJIHHGGFEEDDCBBAA@@?>>==<;Tlkjjihgffedcba`__^]\[ZYXXWVUTSRRQOONMLKKJIHGFEDDCB@??>><;:998765432110/.-,++ݸgQQPPOONMMLLKJJIIHGGFFEDDCCBBA@@??>==<<;::[jihgffedcca``_^]\\ZYXXWVUTSSRQPONMLKKJIHGFEDDCBA@?>>=<;:98875542211/..-,+*)ݸgQPOONNMLLKKJIIHHGGF#EDDCBBAA@??>>=<<;;:9;`ggfedccba`_^]\\HZYXWWUTSSRQPOONMLKJ:HGFEDDCBA@?>>=<;:9.8765432110/.-,+*)((ݸfPONNMMLKKJJIIHGGFFE"DCCBAA@@?>>==<<;::987>cedccba`__^\\[ZGXWWVUTSRQPOONMLKJJI9GFEDCBA@??>=<;:988-65432110/.-,+*)(('&ݸeONMMLLKKJIIHHGFFE]ED"CBBA@@??>==<<;;:998766Ddcba``_^]\[ZYXEWVUTSSRPOONMLKJJIHG8EDDCBA@?>=<;;:9876,432110/.-,,**('&%ݸeNMMLKKJJIHHGGFEEDDC!BAA@@?>>==<;;::98876554Oaa`_^]\[[ZYXWEUTTSRQPONMMKJJIHGFE7DCBA@?>>=<;:988765*32100/.-,+*)(('&O%$#ݸdMLLKJJIIHGGFFEEDCCB!A@@??>==<<;::99887655434X_^]\[[ZYXWVUCTNbba``_^]t\[JB6BA@??>=<;:98876543)100/.-,+*)(('&%$##!ݸdLKKJIIHHGGFEEDDCBBA @??>>=<<;;:9988776544322:]\\[ZYXWWVUTBRֈrԓ?4@@?>><;:9887654321(0/.-,+**(('&&$$#"! ݸcKJJIIHGGFFEDDCCBAA@ ?>>==<<;::998776654332110D[ZYXXWVUTTSAQԈqҒ=3??>=<;:98775433210'/.-,+*)(''&%$#"! ݸcJIIHHGFFEEDCCBBA@@?>>=<<;;:9988766554322110/0QYXWVUTTSRQ?OЄp<2>=<;:987765432100/&-,+*)(''&%$#"! ݸbIHHGGFEEDDCBBAA@@?>==<;;::9887765544331100/..5WWUUTSRQPO>N}͆rˍ;1<;:988765432100/.-%+*)(''&%$$""! ݸbHGGFFEEDCCBBA@@??>=<<;::9988766554332200//.--,BUTSSQPOON=L{Ʉr90::88766432100/.--,$))(''&%$#"! ݸaGGFEEDDCBBAA@??>>=<;;::988776554432211//..--,+,NSRQPONML>==<<::99877665443321100/.--,,+**6QPOONMLJ;Icrl6-7765432100/.-,+*)(!'&%$#"" ݸ`EEDCCBBAA@??>>=<<;;99887665543322110//.-,,++*))(DONMLKJJ:Hjpu5,65432100/.-,+*)('' &%$#"! ݸcDDCCBAA@@?>>==<;;::8877665443321100/..-,++**))('-MLKJJIH8F~ӂ4+432210/..-,+*)('&&$#"! NݸVCBBA@@??>==<<;::9987665543322100//.--,++*))(('&&>==<;;::988765544322110//..--,+**)((''&%%(GHGFEE6Cz̄#0)10//.-,+*)(''&%%##! KݸuthE??>==<<;::9987765443322100//.--,,+*))(''&&%$$#5GFEDD5Bwɇ$ƈ/'0/.-,,**)''&&%$#"!HݸsrrlI>=<<;;:9988766543322110//..-,,++*)(('&&%%$$#"$BEDCB4@uć.'/.-,+*)('&&%$#"! GݸrqpolL<;;::9887766543321100/..--,++**)(''&&%$$##"!!0CBA@2?s -&-,+*)(''&%$#"!! =ݸponmmkM;:9988766554322100//.--,,++*))('&&%%$##""! >@??2=JponmlN+$+*)(('&&%$#"! Gݸ߅onmlkjjN988776554432110//..--,++**)(('&%%$$#""!! -?>=0}Z # ݸoQPONMMLJJIHGFEDDCBA2?OzL z =zX "ݸnONNMLKJJIHGEEDCBAA@1>MzxJ x0=3H<+ +++:;:9)  %ݸlLKJJIHGFEDDCBA@?>>=/;JusG s9ssy}|{R  #ݸkJJIIGFEDDCBA@?>>=<;.9H|pEp8pptzyxxP %ݸiIIHGFEDCCBA@>>==;:9-8GqmDm6mmnwvuN   ݸhHGFEDCCBA@?>==<;:98,7E}vkBk5krutsL  ݸgFEDCCBA@?>==<;:9887*5Dzyjh@ h4hmqpJ  !ݸfEDCBA@@>>=<;:988765)3Bwvoe? e 2egoonmI   ݸeCCBA@?>==<;:9876654(28^]LJ. J %JSTSR7    ݸdBA@?>==<;:987765432'06[PG, G #GOQPOO6  !ݸc@?>>=<;:98776543210&/=onml]9 ] .]afedB "ݸb?>==;:98877544321//%.;kj_Z8 Z -Z\ba@  ݸa>=<;:98776543210//.$,:hgcW6W +W_^>  ݸ`<;:98776543210//.-,#+7edVT4T *TZ\[< "ݸ_:987765432200/..-,+!)6bbaYQ2Q (QUYX: #ݸ]97766543210//.-,+*) '4_^][N0N 'NPVVUTT7 ݸ\72**)((&&% $##"!!#&"10/."&%$  ݸɿ[6-U]\[Z%%1XWQH-H $HNOON3 MLK ݸǿ[5+RZYXWV$#/UTSED+D "DIKJ1 IHG ݸƽZ3*OVUTS#"-QPFA)A  AEHG. FFED ݸļY1)MSSRQPPOO" ,NMLLH>'> >@EEDCC, CBA ݸûW/'IPPONML *KJIH;%;;==  ݸºV.&FLKJI(GFEE<8#88=>=(<;: ݸU-%CIHGFE&DCBAA<4!449:99&876  ݸT,$'('&%$#"!!     ݸS*#"=<;::1...0322!10 0/ݸR(":?>=<;!:9871+++,0/.--,++ݸQ' 6:9875431'''(,+*)('ݸP%3765433210/$$$(''&%$ݸO%0432100/.-,$!!!!$%%$$#""!ݸN#,0/.-,*)(# ! ݸM!"$#"!     ݸL !#"!     ݸK#%$#"!!     ݸJ!!    ݸI     ݸH       ݸGݸF   ݸE ݸD ݸC   ݸB  ݸB  ݸA  ݸJ,ݸо-ݸ)؛"KJ  xONLKKJIHGFEDCBA@?>=<;:989:;<;<=>>?@?@?>==> @B@;Wpm/tń ǀ7½¾mńxĤꏤ޶̃gݲ¸¥¸޻̂zҫٟ؅޻̂Zག± ޻ƀA€ÀŁǀȂ޻n\\[[ZZYXXWWVVUQTSSRRQQPPOONNMMLLKKJQz{zzyxwwvuttsrrpoonmllkjiihgfeddcbaa`_^^]\[ZZYXWVVUTSRQPPONMMKJIHGFFEDDBAA@?>=޻iTTSSRRQPPOONMMLLKKJIIHHGFFEEDDCBBAA@?Kprqpponmlkjjihgffddcba``_]\\[ZYXWWVUTSRQPPONMLKKJIHGFEDCBBA??>=<;:998765432110/޻iTSRRQQPOONNMMLKKJJIHHGGFEEDDCCBAA@@?>>Koponmmlkjihggfedcba``_^]\[ZYXXWVUTTSRQPOOMLKKJIHGFEEDCBA@??>=<;:998665321100..޻hSRQQPPOONMMLLKJJIIHGGFFEEDCCBBA@@??>>==<;:998765432110/.-,޻gRQQPOONNMLLKKJIIHHGGFEEDDCBBAA@@?>>==<;;Tlkjjihgfeddcba`_^]\\[ZYXXWUUTSRQPOONMLKKJIGFFEDCBA@??>=<;:998765432110/.-,+*޻gQPPONNMMLLKJJIIHGGFFEDDCCBBA@@??>==<<;::9Zjihgfeddcba`__^\\[ZYXXWVUTSSQPOONMLKKJIHGFEDDCA@??>><;:998765442210/.-,,+*)޻fPOONNMLLKKJIIHHGFFE"DDCBBAA@??>>=<<;;::8:`ggfedcba`__^]\[HZXXWVUTSSRQPONMLKKJ:HGFEDDCBA@?>>=<;:9.8765432110/.-,+*)((޻fONNMMLKKJJIHHGGFFED"CCBAA@@?>>==<<;::9977=cedccb``_^]\\[ZGXWVUTSSRQPOONMLKJII9FEDDCBA@?>>=<;:988-65432110/.-,+*)(('&޻eNMMLLKJJIIHHGFFEEDC!BBA@@??>>=<<;;:9988765Ddcba`_^]\\[ZYXEWVTTSRQPOONMLKJJIHG8EDCBA@??>=<;:99876+432 110/.-,+*)(&&%޻eMLLKKJJIHHGGFEEDDCB!AA@@?>>==<;;::998776544Oa`__^\\[ZYXXWDUTSRQPOONMLKJJIHGFE7DCBA@?>><;;:987755*31100/-,,+*)(('&M%$#޻dLLKJJIIHGGFFEDDCCBB @@??>==<<;;:998876654334X_^]\[[YXXWVUCT~p{H6BA@?>>=<;:98876543)100/.-,+*)(('&%$#"!޻cKKJIIHHGFFEEDDCBBAA ??>>=<<;;::98877655433219]\\[ZYXWVUTTBRpH4@??>=<;:9887654321(0/.-,+*)(('&%$$#"! ޻cJJIHHGGFFEDDCCBAA@@>>==<<;::9987766554322100D[ZYXWWVUTSRAP_F3?>=<;:988765432110'/-,+**)(''&%$#"! ޻bIIHHGFFEEDCCBBA@@??>=<<;;:9988776554432110/ QXXWVUTTSQQ?OpE2>=<;:987765432100/&-,+*)(''&%$#"! ޻bHHGGFEEDDCCBAA@@?>>=<;;::9987766544332100//.-5WVUTTSRQPO>NpD1<;:988765432100/.-%+*)(''&%$#"!! ޻aGGFFEEDCCBBA@@??>==<;;:9988766554332210//..-,,BTTSRQPOON=LpB0:988765432100/.-,+$)((''&$$#"! ޻`GFEEDDCBBAA@??>>==<;::9887765544332110/..--,,+,NRQPOONML==<<;:99887665543322100/..-,,++*)6QPONMLKJ:Iۄp?-7765432100/.-,+*)(!'&%$#"! ޻_EDCCBBAA@??>>=<<;;:98877655443221100/.--,++**)((DONMLKJI9Gۅp>,65432100/.-,+*)('' %%$#"! ޻cDCCBAA@@?>>==<;;::9877665443322100//.-,,+**))(('-LLKJIIH8E=+432100/.-,++*)('&&$#"! N޻UBBA@@??>==<<;;:9987665543322110//..-,++**)((''&%;JJIHGF7D%<*3210//.-,+*)('&&%$"! G޻vbB@??>>==<;;::9887655443321100/..--,,**))(''&&%%'GHGFED6C#:)10//.-,+*)(''&%$##! I޻tshE?>==<<;::9988765543322100//..-,,++))(('&&%%$$#5FEEDC5A!8'0/.-,+*))''&&%$#"!H޻srqkI=<<;;::9887765443221100/..--,++**((''&&%$$##"$BDCBA4?7'/.,,+*)('&&%$#"! G޻qppolK<;::9987766543322100//.--,,+**))('&&%%$##""!!/CBA@2> 6&-,+*)(''&%$#"! =޻ponmmkM:9988766554422110//..--,++**)(('&%%$$##"!! >@??1=4$+*)(''&&%##"! G޻nmmlkjiN887766544331100//.--,,+**))(''&%%$##""! -?>=0;3#*)('&&%$#"! I޻߃mlkjjihgL766554332200//..-,,++*))((''&%$$#""!! ;=22110//.--,++**)((''&%%$$#"!! +7,6.$#"! I޻gfedccba`_^[8100//.-,,+**))(''&&%%$##"! 6*4,"" ޻~edccba`__^]\V4//..-,0$/..--,,++**))(" ,)2ك+! "޻}dcba``_^]\\[YN/.--,+w'(0ƈŀ* ޻|cba`_^]\\[ZYXWE-,,+*&$/("޻{a``_]]\[ZYXWWVU:++*)%. & ޻y`_^]\[[YXXWVUTTQ0*)($ , %" ޻y^]\[[ZYXWVUTTSRQH)('$ #$ ޻w\[[ZYXWWVUTSRQPOO<'&#  # " ޻v[ZYXXWVUTTSQQOONML/% " " ޻uYXXWVUTTSRQPONNMKJD$dz! '6654322%y  ! ޻tXWVUTTSRQPOONMLKJII%# 6M ޻tWVUTSSQPOONMLKJIIHG6( 5M  ! ޻rUTSSRQPONMLKJJIHGFE6< 5L  ޻qSSRQPOONMLKJIHGFEED5B 4L   ޻pRQPOONMLKJJIHGFEDDC3A 4K  # ޻oPOONMLKJJIHGFEDDCBA2? 3K  !޻mONNMKKJIHGFEDDCBA@?1> 3J #޻lNMLKJJHGGFEDCBAA??>0o  %޻lLKJJIHGFEDDCBA@?>>=/; 2I  #޻jJJIHGFEDDCBA@?>>=<;.92H %޻iIIGFEDDCBA@?>>=<;:9,82H   ޻hHGFEDCCB@?>>==<:998,61G  ޻gFEDCCBA@?>==<;:9887*51G  !޻fDCCBA@?>>=<;:988765)30Fވ   ޻eCBA@?>>=<<:98876543(20A    ޻dBA@?>==<;:987765432'0/A   ޻c@?>==<;:98776543210%/܆/Eه "޻b>>=<;:98776543211//%.ׅ.Eև  ޻a=<;;:88776443210/..$,ׅ.Eԇ !޻`<;:98776543210//.-,#*׃ӄ-Dӆ "޻^:98776543210//.-,++!)Ղ҄-Dц "޻]87765532210/..-,+*) 'ӂ҄,Dφ ޻\7@OONMMLK JIIHGG;&kp,:ju 10/ ޻[6HŠZ%̃+CɆ 1G ޻Z4G̀Y#˃ +Cdž1ɊF޻ȿY3FˀX"Ƃ +Cƅ1njF޻ǾX1EʉW ł +BÅ0E޻żW/DȉV~Ă +B0E޻ĻV.CʼnU}¿ +B0D޻ûU-BÉT| +B/D޻ºT+@Kezk +z9gz|~~}o/|{;^qpb ޻S*?Rz +A/C ޻R(>Qx +A.B ޻Q'=Pw +A.B ޻P%<Ov +@.B ޻O$;Nu +@.A ޻N#:Mt +@-A ޻M!9Hgv +;ow-<ov ޻L 8Ij{ +=s|-=t{ ޻K6Jp +?|-@} ޻J6Io +?{,?{ ޻I5In +>z,?z ޻H4Hm +>y,>y ޻G3Hk +>x,>x ޻F2G j +=v,=v ޻E*Z\\[ZY4 FXWVUUSJ  P*HPQK !QP*HPJ ޻D ޻C   ޻B  ޻B  ޻A  ޻J-޻Ͽ-޻)ٞ#MNM   yONLKJIHGFEDCBA@?>=<;:9:;<<=< ==>>?@?@@A@@?=> @BA;Wqm/vDžˀ ŀ ( ĿſoDŽxâ댢߹̓g޵Ôźťź߽͂zӪڞ؃߽͂Z⿂ò ߽ҀˀJ€ÀŁƀǀȃ ߽mZZYXWVVUTTSSRQPPONMMLLKJ2IIOxzyyxwwuutsrrqpoonmllkjiihgfeedcbaa`_^^]\[ZZYXWVUSRQQPOONMLKKJIHGFFEDDCBA@?>>=߽hRRQQPOONNMMLKKJJIIHGGFFEEDCCBBAA@??>>Ioqpoommlkjiiggfeddcba`_^]\\[ZYXXWVUTSRQPPONMLKJJIHGFEDDCBA@?>>=<;9988765432100/߽gQQPPONNMMLLKJJIIHHGFFEEDDCBBAA@@?>>===<;:998765432100/.-߽gPPOONNMLLKKJJIHHGGFFEDDCCBBA@@??>>=<<;;Mmlkjjihgffedcba`__^]\\ZYXXWVUTSSRQPPONMLKJIHGFEEDCBA@??>=<;:998765432110/.--,߽fPONNMMLLKJJIIHHGFFEEDDCBBAA@@?>>==<<;::9Rkjihgffedccba`_^]\\[ZYXWVVTTSRQPPONMLKJJIHGFEDDCB@@?>>=<::988765431100/.-,+*߽eONMMLLKKJIIHHGGFEEDDCCBAA@@??>>=<<;;::988Yhggfedccba`__^\\[ZYXWWVUTSSRPPONMLKKJIHGFEEDCBA@??>=<;:998765432110/.-,+*)(߽eNMMLKKJJIIHGGFFEEDC!BBAA@??>>==<;;::99879^fedccba``_^]\[ZGXWWVUTTSRQPONMLKKJI:GFEEDCBA@??>=<;:99.765432110/.-,++*)((߽dMLLKJJIIHHGFFEEDDCC!AA@@??>==<<;;:9988765=<;:9987-54322100/.-,+*)(('&߽dLKKJJIHHGGFFEDDCCBB @@??>>=<<;;::988776544Bcb``_^]\[[ZYXWEUTTSRQPPONMLKKJIHGF7EDCB@@?>>=;;:99876+432110/.-,+*)(('&%$߽cKJJIIHHGFFEEDDCBBAA @?>>==<<;::998876654432M`_^]\[[ZYXXWVDTSRQPPONMLKKJIHGFEE6CBA@??>=>=<<;;::988776654332W^]\[ZYXXWVUTCS^baa`_^]t\[WE5A@??>=<;:998765432)10/.-,,**)(('&%$#"!߽bIIHGGFFEEDCCBBAA@??>==<;;::99877665543221108[[ZZYXWVUTTSAQnr~~nD4??>==;:99877643321(0/.-,+*)(('&%$#"!! ߽bHHGGFEEDDCCBAA@@??>=<<;;:99887765544331100//CZYXXWUTTSRQ@Om~_nB3>>=<;:988765432110'.-,+*)(('&%$$#"! ߽aGGFFEDDCCBBA@@??>>=<;;::9987766554332210//. PXWVUTSRRPP?Nl~t}mA2=<;:988765432110/.&,+*)((''&%$#"! ߽`FFEEDDCBBAA@@?>>==<;::99887665544322110/..--,4UUTSSRQPON>Ml~t}}|l@0;:988765432110//.-%+*)(''&%$#"! ߽`FEDDCCBBA@@??>>=<<;::98877665443322100/..-,,++ATSRQPONNM=Kl~}}|pl>/9988665332100/.-,+#)(''&%$$"!! ߽_EDCCBBAA@??>>==<;;:99877665543322110//.--,,+**+MQPOONMLK;Jk~}}|k=.88765432100/.-,+*)"' &%$#"! K߽_DCCBAA@@??>==<<;;:98877655443321100//.-,,++*))(5OONMLKJJ:Hettsrrqrpd;-765432100/.--,+))(!'&%$#"! ߽^CBBAA@??>>==<;;::9977665543322110//..-,++**))(''CNMLKJII9Fdtsrrqprd:,54421100/.-,+*)('' %$#"" ߽bBAA@@?>>==<<;::99886655443221100/..--,+**))(('&&,KKJIIHG8Ei~~}|{qzj9+432100/.-,+*)(''&%$#"! ߽T@@??>>=<<;;::9887765443322100//..-,,+**)((''&&%$:IIHGFE7Di}|{zi7*2100/.-,++))''&&%$"! H߽t`A>>==<;;::99877665443221100/..--,,+*))(('&&%%$$'FGFEED5Bh}|{z%yyh6(00//.,,+*)(''&%$#" I߽srgC=<<;;:99887765543321100//.--,,++*)((''&%%$$##"4EEDCB4@g|{zy!h4'//.-,+*)(''&%$##"!J߽rppjG;;::9987766554322110//..--,++**))''&&%%$##""!#ADCBA3?g||{zy xg3&.-,+*)(''&%%$#"! G߽ponmjJ:99887665544321100/..--,,+**))(('&%%$$##"!! /BA@?2>f{zzyx g2%,+**)('&&%$#"! =߽ommlkiK887766544332100//..-,,++**)((''&%$$##""! =?>>1=<0;e{zzyxwwf/#)(''&&$##"! J߽ljjihggfJ5544332110//.--,,++**)((''&&%$##""! ;<;.9ezyxwve-"('&&%$#"! G߽߁jihggfedcI43322110/..--,++**))(''&&%%$#""!! +:9-8dyxwve,!'&%$#"! K߽ހihgfeddcb`B221100//--,,++*))((''&%%$$#"!! 88,6cyyxwwve+ %$$"!! G߽gffedcba``^=100//..,,++**)((''&&%$$##"! +7+5cxwwvuud*$#"! I߽fedccaa`_^]Z70/..--,+**))(('&&%%$$#""! 5*3bxwwvud)"! ߽}dccba`_^]\\[U3..-,,+)**))((''&&%%$$#!+)1\mmllkj\'! "߽|cba``_^]\[ZYXM.-,++*=QF"(0YfedcbX% ߽{b``_^]\[[ZYXWWD,++*)CcQ!#/`utsra$"߽z`_^]\[[ZYXXWVUT9**)(CbP ._tsrrq `# ߽y_^]\[ZYXXWVUTSSP/)('BaO  +^rqp oo_!" ߽x]\[[ZXXWVUTTSRQPG('&B_N  #\ponm^  ߽w\[ZYXWVUTTSRQPOON;&%A^N \oonml ] " ߽vZYXXWVUTSRQPPONMLK.$@] M Ymlk jj[ ߽tXXWVUTSSRQPONNMLKJD#6B< %$#"!FUTSRL ! ߽tWVUTTSRQPONNMLKJJIH%"?ZK %Z* JjihgY ߽rUTTSRQPONNMLKJJIHGF6(?XJ $X) EfhhgfeW ! ߽qTSSRQPONMLKJJIHGFEE6;>WI $W( E_fedcV ߽qSRQPONNMLKJIIGFFEDD4AHUH $U( DXdcbaU  !߽oQPOONMLKJIIHGFEEDCB3@VUSG #S' CS`ba`__T # ߽nOONMLKJIIHGFEEDCBA@2?X^RF #R' BRZ``_^]]R "߽mNNMKJJIHGFEEDCBA@??1=WgSPE #P&APS^]\Q #߽lMLKJIIGGFEEDCBA@??>0=<.:TdcPLC"L&?LLUYXWN  #߽iJIHGFEEDCBA@??>=<;:-8RbbZKB!K%?KKOXWVM !߽iIHFEEDCCB@??>==<:99,7Q`MIA!I$>IIJVUTK  $߽gGFEDDCBA@?>>=<;:988+6P^]]VG? G$=GPSSRQQJ  ߽fEDDCBA@?>>=<;:98876*4N[HE> E$>=<;:9887655)3NYXPC= C#;CFOONMG  !߽eBB@??>><<;:98876543(1KTS@>:>"8>IHC   !߽dA@?>>=<;:9887654321'/IRQPF<9<"7<DFEEB  "߽b?>>=<;:988765433110%/ISSRP>:>"8>CIHGFB $߽a>=<;:988776543210//%-HQQPOB<8<"7<>FEDA #߽`=<;:98776543210//.-#+GONNMH:7:"6:DDCB? !߽`;:98776543210//.--,"*FLK;86 8!58?A@> "߽^988766543210/..-,+*!(DJJIHH@65 6!46;?>< "߽]8776543210//.-,+*)) 'CGFED43 4!347=<;;: !߽\7:@@?>>==<<;::99881%BFED83 3!3<;:9 !  ߽[5=HGFEDC:%@BA@@:/0 / 1/76 !543$ ߽Z4=.-/ - 0-3546!3210#߽Y2;DCBA@?7!=<;1+.+ /+/24!10/.# ߽X09A@?>>=<<6 <;;:94)-).)+10//3 .-," ߽ȿW/9@>=<<;:5;98766('+'-'(.-1 ,+*!߽ǾV.7=<;;:985966543*%*%,%,,+*/  )(!߽ŽU-6;9876553832,#)#+#')(. '&! ߽ļT+5B@?>=3:;;:98,., /,/2213 00/."+(')߽ûS)474321015.-,+!&(!$#"* "! * '߽ºR(34110/.-04,+*)#$'! (   )$߽Q&22//.-,+.3)('$#&'  (#߽P%1/,+*)('.1&%$!%% &!߽O$0-))('&%-0$#"!! $$ % ߽N"/*&%$##,/"! #" $߽M!.30/.-,,1++*)'&) "!( ! )&߽L--**)('&+/$#"!"%$ &"߽K+$)*    ! ߽J* ()      ߽I)''   ߽H(&&  ߽F'&%  ߽F&% $  ߽E!*)('&%$" &#"!    ߽D ߽C   ߽B  ߽B  ߽A  ߽J.߽ο-߽)ۡ$ON   ih32*{SIHHGFFEDDCCBBAA@@?>>==<<==>>??@BVCBAX˼ɦٱW¿óԴhpWUTRQONLKIGFEVtspnljheca_\ZXVSQOMJHFCA?=:86ilRPNMKJHFECB@?=Tljgec`^\YWUSPNLIGDB@=;9642/-ijOMLJIG<;1Wfca_]aUXVSQNLFGEC@><94520.+)ihLKIHFE6A@>=;:8,7Z`][XOSROMKI@CA?<:85/0.,)'%ifJHGEDB4?=<:975*2:ZYWTL-W?=:8631,,*(&#!idGFDCA@2<;98653(0.CUSPH̀\U;9742/-((&$!}ibECB@?=0:875420&-,,KOLDJ7520.+)$%" {inCA?><;.76431/.$+)(3KH@с,R31.,*'%! zi܈`@=;:8,5320/-+"('%$?D=ǀM/-*(&$!yiۅn`>975*21/.,*) &$#!)@9A+)&$"yiفjh\953(0.-+*(&#" 55D'%# xi~gdbT31&-,*)' %$!#27?#!xi{b`^\I.$6UUTTS;*p4wix_\ZXU;"J ^8 vit[XVTQM&G [4 viqWTRPMK:7fCXW#N ^ttssr* uinSPNLIG?NS$t l. uijOLJHEC;V{zNznd, tigKHFDA?8JbW9W%OGaccbb# tidGDB?=;4N}mlElbXput& si`C@><971Fne[:[%SJ]eedd! si]><:853-AfdT5TLDT\ riZ;8641/)?fe[UU5UMEU[]\\ riV30.,*($3MMH<<% <60<@DCC riSJZYXWW.4UTSFE* E >8EGLKK)IH)riOCQPOON)/LKKB<%< 71<=CBA$A@?$qiL5=<;::"%8762(($ ((-,+* qiI5>==<; $9887,+ '#++/0/..- ,%qiF-543220/..&" ""$&& %%$#""qiC"%%$#"     qi?!      qi<  qi9  qi7  qiF&&%$##"!!|d؛گG .   |UJIHHGFFE DDCCBAA@@??>??@@ABBCVDCBYÿ̽ʦڲXĴյipVTSQPNMKJHGEDVtrpnligec`^\ZXUSQOLJHECA?=:86jlQONLKIGFDCA@>=Skigec`^[YWURPNKIGDB?=;9641/-jjNMKJHG;CB@?=<:1Weca^\aUXUSQNLFGEC@>;94420.+)jhLJIGFD6A?><;97,6Z_]ZXO\[YWUSDCA><:85/0.,)'%jfIHFECA4>=;:875*2:ZYVTLނ\o?=:8631,,*(&#!jdGEDBA?2<:97643(/.CURPHo;9641/-((&$!~jbDCA@><09865320&-++KNLDf7520.+)$%" |jnB@?=<:.75421/-#*)'2JH@k30.,)'%! {jވ_?<;98,4310.-+"(&%#>D=h/-*(&$!zj܅n`=875*20/-, *)%$"!(@9̀\`+(&$"yjځjh[943(/.,+)(&#! 55d'%" yj~fdaT30&-+*('%$ #2*b#!xj{b`^[I.$[n*łWwjx^\ZWU;" ߂] wjtZXVTQM& ݂Z vjqVTROMK:o/ӁO vjnRPNKIG?8ӂU ujjNLJGEC; 7πS ujgJHFCA?8|Ā 1ĹǁI tjdFDB?=;43O tj`B@>;971|Ӏ0 ЁJ sj]><:753-xЀ. ˁH sjZ:8641/)yӒ. Ԁ I sjV?><:86+k}( @rjS_qlj* ȀDxɁ rjO \m( Bt{rjLR`s% ;ek, ++)sjIUe~' >mt4ujFRa{& <:75kjOMLJHGEDBA?><;Rjhfdb_][XVTQOMKHFDA?=:8631/,khLKIHFE:B@?=;:8/Udb`][TWTRPNKEFDB?=;94420-+(kfJHGEDB5?><;986*5Y^\YWNUSQOMKAB@>;975/0.+)'%kdGFDCA@3=;:8754(19YXUSKx~}}|{M><:8531+,*'%# kbECB@?=1:976431'.-BTQOGx~~}|K:8631/-((&$!~k`BA?><;/865320/%,**JNKCpvuutsF7420-+($$"|km@?=<:8-5421/.,#)(&1IG@u}|{{zF20.,)'%! {kއ^>;986+310.-+*!'%$"=C'%" yk}ecaS1/%,*)'&$# "1pxwvvu<# xkza_\ZH-#6GFFEE8*flkjii7xkw][YVT:!@a*Kkrrqpp6 wktYWURPL%>^*Hgnmmll3 wkpUSQNLJ98P?E$DBWa``_^. vkmQOMJHF>FVBV$RUddccb. ukjNKIGDB;PSQ?Q$O M__^^], ukfIGEC@>7LRF8F%D BPSSRR( tkcECA?=:4J]JH8H%F DNTSSR' tk`B?=;860EWLA4A%@?DMLLK$ tk\>;9752,AQO=1=<;=FGFE" skY:7530.)>ML@99.9889@BAA skV:86420(9GFA44*4448<<; skSEEDCCB05@?>/-&-..-0544'32211&rkO??>=<;-0998/("()*()//.#-,,++#rkL=<;::9*-6651&!&()&&,"**)" skI410//.%',+*)   !!  tkF/*)(''"#%$## tkC/+*)(( "%%$$  tk?#    tk<      tk:       ! sk7 rkG&&%%$$##"!!}fڛܲH /   il32 }|zxwurpnkifdceghljpuwptwUĽ¸¹pUSQOLJHECWqnkgd`]ZVSOLHEA>;74tPNKIBDB?=7Tfc_YXUQNHGC@<751.*tpLJHE;@><906W]YQqusqFA=960/+($tHFDA7=:86-15USLN:730*)%"tEB@>49742*-+=MFF41-*%#t]?<:1530.&)'&C@rD.+'$tn\:6-1/-*#&#!-:;(%!theS3*.+)& "27"tb_[E'Liih6!~.t\YUR/_C, tVSOL@Np5Pfee#l||{$ tPMIF?b|9_|'q# tJGC@9Xa__,I_Tjii tDA=:3Wk``,J`Thii t>;74.MfVU&AUJZ]] t62/,'ATKC3C;FKJ tBWVU:>RNC2C:DIIGFt5BA@-/=<0#.(.43 2110t/<:9()65.) #),- ,+ **)%t$)('$#  t   t  t  ta~}{yxvsqomjhfefhjmkqvxquxVýſĹĺrURPNLIGECVqnjgd`]YVSOLHEA>;74vOMKHBDA?=7Tfb_YXTQMHFC?<751.*v0LIGE:@=;905V\YQM@=960/+($vHECA7<:75,05USK<_:730*)%"vDB?=38641)-* vJFC?9dG8 vD@=93fGŀ7 v>:74.bE4 v<;85-¿Y@1v[C:vtu~Q<5vtvT>53vhirK 71/vfisL 81/vEkjjEJhgg0Qb#]bbc bb ^v  vb}|zywurpnligfhjkolrwyqvyWǿźŻsSPNLJHECAUplifc_\XURNKGDA=:63wMKIF@B?=;5Sea^XWTPMHFB?;741-*wJGEC8><:7.4U[XP``][D@<950/+($wFDA?5:864+/4TRK{~}F:63/*(%"wB@>;27520(+);LEtxwvA40-)%#w[=:8/31/,%(&%B?w{zy=.*'$wflZ84,0-+)"$" ,:ptsr8(%!wfcR1),*'%!1swvu4"wa]ZC%AOON0!jmll/w[WTP-I_6jonm+ wUQNK@CS0AKKJ![ba`% wOKHE>PS/GS"U`__# wIFB?8RJGG)>GFTSS  wC@<93MPCC&:CAMNM w=:73-FO=<"4<;BEE w9741*?H=5/558>= w>CBA57=9,(,--33210/ w8<;:/165(#' ('-,+*)) w//.-'')(   w*)('"#$# w       w      w  wcis320_\XUt{vqkfa[VQJFA=;_\UO;4.C?:62:QJD0)#E84/+&=?9%_6- '4.WG&(#LF+pppppp UU1UU UUUU -;;);;;;;; """""" """"""""""""""^[XUs{vqkfa[VQIEA<;_[UN;4.C>:51:PJC0)#E73/*&=?8%_6,'4.WG&(#LE+ 1  -)  ]ZVSrzupke`[VPGC?;9^ZTMyy:4-A=8409OICy|/)#C62.)%<>8tw%^5+jo&4-joVF%dd'#ddKE*NNNNNN ==0== ==== -,,),,,,,,  t8mk@  -?JOOPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPOME:a7#2;?@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@?;2#  !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!  h8mk 5IJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJ3 =^aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaK  l8mk{RSSSSSSSSSSSSSSSSSSSSSSSQ3FFFFFFFFFFFFFFFFFFFFFFFFFFFFFEs8mkcutesdr-1.0.5/radio.ico0000644000175000017500000004220611546075374014740 0ustar bottomsbottoms00 %F  % 6 h@(0` %GCaCGïF̯F̯F̯F̯F̯F̯F̯F̯F̯F̯F̯F̯F̯F̯F̯F̯F̯F̯F̯F̯F̯F̯F̯F̯F̯F̯F̯F̯F̯F̯GïCCaGEIU(Z,Y-Y-Y-Y,Y,Y,Y,Y,Y,Y,Y,Y,Y,Y,Y,Y,Y,Y,Y,Y,Y,Y,Y,Y,Y,Y,Y,Y,Y,Y-Y-Y-Z,U(IEFӴX)Z.Y,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,Y,Z.X)EHԵ[.[-[-[-[-[-[-[-[-[-[-[-[-[-[-[-[-[-[-[-[-[-[-[-[-[-[-[-[-[-[-[-[-[-[-[-[-[-[-[-[-[.HKZ+\.[.[.[.[.[.[.[.[.[.[.[.[.[.[.[.[.[.[.[.[.[.[.[.[.[.[.[.[.[.[.[.[.[.[.[.[.[.[.[.[.\.Z+KRR _0^.^.^.^.^.^.^.^.^.^.^.^.^.^.^.^.^.^.^.^.^.^.^.^.^.^.^.^.^.^.^.^.^.^.^.^.^.^.^.^.^.^._0RQQb].`1`1`1`1`1`1`1`1`1`1`1`1`1`1`1`1`1`1`1`1`1`1`1`1`1`1`1`1`1`1`1`1`1`1`1`1`1`1`1`1`1`1`1`1].QbSc4b1b3b1b1b3b1b1b3b1b3b1b1b1b3b1b1b3b1b1b3b1b1b3b1b1b3b1b1b3b1b3b1b1b3b1b1b3b1b1b3b1b1b3b1c4SX%ƽd5d4d3d4d4d3d4d4d3d4d3d4d4d4d3d4d4d3d4d4Z'RQQQQQRZ'd3d4d3d4d4d3d4d4d3d4d4d3d4d4d3d4d5Y%\(̿f7f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6\)⹥⹥\)f6f6f6f6f6f6f6f6f6f6f6f6f6f6f6f7\(_+̿h7h6h6h6h6h6h6h6h6h6h6h6h6h6h6h6h6h6h6VVh6h6h6h6h6h6h6h6h6h6h6h6h6h6h6h7_+c.j9j9j9j9j9j9j9j9`,Y"X!X!X!X!X!X!X!X!X!G G X!X!X!X!X!X!X!X!X!Y"b.j9j9j9j9j9c.f1l;l;l;l;l;l;l;b.㼧ݭd0l;l;l;l;f1i3n=n=n=n=n=n=n=]'_)n=n=n=n=i3l7p>o=o=o=o=o=o=f1澨ᮓh3o=o=o=p>l7o:s@s@s@s@s@s@s@s@i3b)b(b)b)b)b(b)b)b(b)RRb)b(b)b)b(b)b)b(b)b)k5s@s@s@s@s@o:t=uBuBuBuBuBuBuBuBuBuBuBuBuBuBuBuBuBuBuBd,d,uBuBuBuBuBuBuBuBuBuBuBuBuBuBuBuBt=w@wDwDwDwDwDwDwDwDwDwDwDwDwDwDwDwDwDwDwDn8n8wDwDwDwDwDwDwDwDwDwDwDwDwDwDwDwDw@zCxEyDyDyDyDyDyDyDyDyDyDyDyDyDyDyDyDyDyDyDp9h/YYh/p9yDyDyDyDyDyDyDyDyDyDyDyDyDyDyDyDxEzC|GzG{G{G{G{G{G{G{G{G{G{G{G{G{G{G{G{G{G{G{G{G{Gk1k1{G{G{G{G{G{G{G{G{G{G{G{G{G{G{G{G{G{GzG|GI|G}G}G}G}G}G}G}G}G}G}G}G}G}G}G}G}G}G}G}G}G}Gm2m2}G}G}G}G}G}G}G}G}G}G}G}G}G}G}G}G}G}G|GIۂL}I}J}J}J}J}J}J}J}J}J}J}J}J}J}J}J}J}J}J}J}J}Jn5n5}J}J}J}J}J}J}J}J}J}J}J}J}J}J}J}J}J}J}IۂL݄NJրJրJրJրJրJրJրJրJրJրJրJրJրJրJրJրJրJրJրJրJրJp5q6րJրJրJրJրJրJրJրJրJրJրJրJրJրJրJրJրJրJJ݄NއQ؁LقMقMقMقMقMقMقMzAt8s8s8s8s8s8s8s8s8s8s8s8s8d#d#s8s8s8s8s8s8s8s8s8s8s8s8t8{CقMقMقMقM؁LއQSقMڃNڃNڃNڃNڃNڃN{Bǯ鸛|DڃNڃNڃNقMSUڂNۃOۃOۃOۃOۃOۃOu:v<ۃOۃOۃOڂNUWۅN܆P܆P܆P܆P܆P܆Px;x=܆P܆P܆PۅNWZۅO݇Q݇Q݇Q݇Q݇Q݇Qy݇Q݇Q݇Q܅OY\܆PވRވRވRވRވRވRz={?ވRވRވR܆P\^݇QވRވRވRވRވRވR{>|@ވRވRވR݇Q^`SUUUUUU}@bffggfb~BUUUS`bTVVVVVV~Ay:QQQQQQQy;CVVVTcfTWWWWWWCx}@FFFFF}@y߀DWWWTehUXXXXXXDEXXXUhjVYYYYYYEGYYYVilWZZZZZZLNZZZWkoX[[[[[[ZLkmlllllllllllllllllllllllllllmjL[[[[XmpXZ\ZZ\ZZ\WXVVVXVVXVVXVVXVVXVVXVXVVXVVXVW\ZZ\ZXprY\[\\[\\[\[\\\[\\[\\[\\[\\[\\[\[\\[\\[\\[\\[\YruZ]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]ZuzY^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^Yy|bb^__________________________________________^b{byv[`^^`^^`^`^^^`^^`^^`^^`^^`^^`^`^^`^^`^^`^^`[vvflb^``_``_`_```_``_``_``_``_``_`_``_``_``_``^bfi﯆^_aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa_^﯆﯇d\a``b`b```b``b``b``b``b``b`b``b``b``a\d﯇𯆂{e]^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^]e{𮅂|`𯆢~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~𮆢`|( @ D9GKӰLݰLܰLܰLܰLܰLܰLܰLܰLܰLܰLܰLܰLܰLܰLܰLܰLܰLܰLܰLݰKӰGD9IW)Z-Y,Y+Y+Y+Y+Y+Y+Y+Y+Y+Y+Y+Y+Y+Y+Y+Y+Y+Y+Y+Y+Y,Z-W)IL\/[-[-[-[-[-[-[-[-[-[-[-[-[-[-[-[-[-[-[-[-[-[-[-[-[-[-\/LM9\-]/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/]/\-N9U"`2`0`0`0`0`0`0`0`0`0`0`0`0`0`0`0`0`0`0`0`0`0`0`0`0`0`0`0`0`2U"\(Իc4c4c4c4c4c4c4c4c4c4c4c4c4U!PPPPU!c4c4c4c4c4c4c4c4c4c4c4\(`,ݾf6f5f5f5f5f5f5f5f5f5f5f5X#X#f5f5f5f5f5f5f5f5f5f6`.d0i9h9h9h9h9[&W!W!W!W!W!W!F F W!W!W!W!W!W!\(h9h9i9d0h5m;m:m:m:_(`*m:m;i5m9p>p>p>p>b,d-p>p>m9q=sAsAsAsAsAe/b*b*b*b*b*b*QQb*b*b*b*b*b*g0sAsAsAq=v@vCvBvBvBvBvBvBvBvBvBvBvBi1i1vBvBvBvBvBvBvBvBvBvCv@zDxFyFyFyFyFyFyFyFyFyFyFyFyFl4XXl4yFyFyFyFyFyFyFyFyFyFxFzD~I{G|H|H|H|H|H|H|H|H|H|H|H|H|Hl2l2|H|H|H|H|H|H|H|H|H|H|H{G~IځL~IJJJJJJJJJJJJJo4o4JJJJJJJJJJJ~IځL܄O׀K؁L؁L؁L؁Lu;r7r7r7r7r7r7r7r7c"c"r7r7r7r7r7r7r7r7v=؁L؁L׀K܄OއRقMڃNڃNڃNw=x?ڃNقMއRUۄO܅P܅P܅Pv;w<܅PۄOTW݆OއPއPއPy{@މSވQZ]SUUU}@~VZZV~}BUS]`TUUUA߄J~?BB~?߄KހCUT_bVXXXDEXVbdWXXXHHXWdhY[[[ZPa``````````````````aPZ[YhiY\\\\[YYYYYYYYYYYYYYYYYYYY[\\YjnZ\\\\\\\\\\\\\\\\\\\\\\\\\\\\Zlv[____________________________\u}9`_``````````````````````````_`|9~\_aaaaaaaaaaaaaaaaaaaaaaaa_\~變c^______________________^dﮂ8}vuvvvvvvvvvvvvvvvvvvuv|ףּ8(0 ` GXLñPO O O O O O O O O O O O O O PLòGXO[-Z-Z,Z,Z,Z,Z,Z,Z,Z,Z,Z,Z,Z,Z,Z,Z,Z,Z-[-OPX^0]/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/]/^0OXY'Ĺa2a2a2a2a2a2a2a2a2^.W&W&W&Z(a2a2a2a2a2a2a2a2Y&ľ_.e5e5e5e5e5e5e5e5b1pE߶ޱ߳֟^+e5e5e5e5e5e5e5_.e2i8i7i7b.W W W W O߰F W W W W [%i7i8e2j7mo;}Q漥丞丞丞ⴘᰓ丞丞丞廡ݤk6r?ox8|>~BZ^XFF^b[[PUUUTTTTTUQZbf\^]\\\\\\\\\]\gtZ^^^^^^^^^^^^Z|ttmkkkkkkkkkkmtcutesdr-1.0.5/cmake_uninstall.cmake.in0000644000175000017500000000176411711274006017715 0ustar bottomsbottomsif (NOT EXISTS "@CMAKE_CURRENT_BINARY_DIR@/install_manifest.txt") message(FATAL_ERROR "Cannot find install manifest: \"@CMAKE_CURRENT_BINARY_DIR@/install_manifest.txt\"") endif(NOT EXISTS "@CMAKE_CURRENT_BINARY_DIR@/install_manifest.txt") file(READ "@CMAKE_CURRENT_BINARY_DIR@/install_manifest.txt" files) string(REGEX REPLACE "\n" ";" files "${files}") list(REVERSE files) foreach (file ${files}) message(STATUS "Uninstalling \"$ENV{DESTDIR}${file}\"") if (EXISTS "$ENV{DESTDIR}${file}") execute_process( COMMAND @CMAKE_COMMAND@ -E remove "$ENV{DESTDIR}${file}" OUTPUT_VARIABLE rm_out RESULT_VARIABLE rm_retval ) if(NOT ${rm_retval} EQUAL 0) message(FATAL_ERROR "Problem when removing \"$ENV{DESTDIR}${file}\"") endif (NOT ${rm_retval} EQUAL 0) else (EXISTS "$ENV{DESTDIR}${file}") message(STATUS "File \"$ENV{DESTDIR}${file}\" does not exist.") endif (EXISTS "$ENV{DESTDIR}${file}") endforeach(file) cutesdr-1.0.5/cutesdr1.ico0000644000175000017500000037442211711303214015361 0ustar bottomsbottoms jv (j@@ (Br00 %Ҵ  z " hPNG  IHDR\rfiIDATx}ue,{BIT{؉؎8-ٲ,7޻,{UX^\.Kno>.y*+q{2w P+@*y@*@*@*@*@*@*@*@*@*@*@*@*@*B (%TR9b0?A;TRb~Ȱ  %TR*mvOGJ!6"@),r;gJ`? Գz[lSI%eYz$?!j6N{/FN?d1|X9MCV+Q}JrB>t+%{6KgtӱNw7V7'Zͯ&,B~3K<;`FG? l ꧓2'𠤸=p۽WU׆HuëT!qy@Ii`A0`A}`J K}0iooVk `۶mG?\j!#/쒾w}4`׃<P) B(Cͣh!by A}` +᲋/Xb$M}'9̖]j%wܜt4䓏üy7#{3g΂oL9>[Ss3$x= lf }MH"A^{% c+!3GX@':\]8<>~>nӿY[E/\ <'Up s|Rի7vǷkH?ME 9sN3> ӥCt[a֭pgs:vIapZG`rp+!!C f'1@a~_/!HxP@?!Əw3ς't 4kUs`zC}-pG ֯^~N;rAt<a#QH&ML20B6,6(A/)M@JV!2:xST :TN$3` ~?>\N8Xm_y>΄P6mJ?'MO?[[Z%$PC}Г ZtӉRWW O=L6Noy{>\p%0a$i:wQ3 0!ذXz04FVBL=tH`lX2 Ϟǟ|$zο">"2!E0m|a-s J 2Ӊ%%%0xػફA+5sF\6ÆE&3w.#[g;/l`~s ![5tx ~λ0rpu$}! Z;~.%رw=S: `>޹~^1"@/~k`#?χoc G 6,S'Mz\6ņE"|^m-$A/&5!/u/_p"JAzy=@H{vO[/uut1( `Ko@.AQ#$<$>Ȓk_(,,R*ʸVN2ռ!q9pg([ T9coQW*2PLI lpKNlF'JM:Yd qttFAqZz9[$c`˔~#HbõZYBنLw? 0Qa~gud>d:A$+~ԃː ;["y)geA DK q6 Lq-.0@q?DVB=y!T`J_y"P@ PTX$07W[n\NkP@>[:c8S3aqe[jy-w]u ?vW&'!}~mCXۯL?ol$3d&H(l4GbY8yʭ`]$z|@F:LQ?h?g0z q-K.4>Op/|]&*|,M @7e\xz@([Sr;Cgs>KǤM`0M8zb|FhHM)p\c=6O ތCn D:p#UlWD :X6r{(^C1%?vMXn(rt<'tr8ō~iӦCaa5<<{,yf WS_At( d?޵{9BO.S!\ 3p@5]>PM :O"ZJp,^b7>\c 캰>ɧ"e+=0aD=6? T;+I>_^_4c9&$l{I blL _+/@u5d[3c#\~1 qH}i,:#إaց첰eW '̑ZlÆСCys=Q7ow.z!Hx҅?z`i*Z.Ѧp枖 Sm \1ў=6|٧]2оGp!Y W q_0\$ԭL/6$Ho_ܼe+?NC$h׾k v>So9Wy1]hE}e]smLfe>]7 e ӳ޳G%X GO=Jm]"ylu,c˸pyM-T'_JiXCBiC;>+`|=xldv>DV.Yב`VLKn7ܐMu'~QuHgnec6+W9Yo\ ԻOMD=w+7CѶK[ƗS : [ݳ@M#ԅz N=-'|"` %!rQ\\Hh\q1 [zbm .vuG @|_r59X] vb85țs^!67n, :r |W*K b/ >Lp>hfoFl*@~H^?::) pUW'x.kVŔO;aapId 'D蕫Vq# ᇡ0f|EAw"Yqciȶ=X_+W#B[== |=(gT`EW{BTDnQ8سޒ>^~jd@g첰UH#Ï;|p.8M;7OnN-įK-,J~tv7@ݻOiq%! 2ѾRSsX}%p[s_?@Nؼyb=XI.̔/O;U}T=՟`P`℉P\R"5MCa& cmzcLz|8]0ΏqPMI_,G?{WcQ:oMBr@k`|`!v)0a-V#s4$3-8=zh(5"⹿mc)cwN+>e'|@`.ЦSYNx}zP* 橴C ytM ֛!C=^WU0|`v)0Xz C <ѣ@޽ Pח9ms8s%ПBǑ!СC7W_h+tA@>|́V h}&~JA6:+*|%U=g8ԓvYO=Vc(p(0i$o8_׶m @Y qX-Buط o~ρՊ˟Ƀ\;D sϨdvȈu$0b7n |C|b~6ނSN.f 3!O>v0Z|Ĉ\X n۾}O R_.Al+ψPjWI-Q_I nVDmmg,-;0,DM7pM\8~V!X=4,!e^H ݠd H _U>yQFWNu#"ۉ]KS_w![{X.p<޳ u5߂t2+n8֑ >Uwp51 B^[!JptD`EKBQ'V>l͚ٳ"] %%%0qD^un17\Ga˻M r? 2d5"|nm] _mt$!7ߜn3@XB`ׅŴ!<א0YnͧGE.lvTPRnY>f cJ/odN@Źbj)Zo>{~,&^WWmx#/'%0ϧ~r-I`Ϧ ~VL'_<-^'[~9̞uTdAE E0n86 0 w4sWvCqm _CۑvE@ Z*0 1K<t{v5I<8vB4 V,8g/^ '47캰D}f͌vY~)Aq,&͎;09\x I!hbKz5,Mxy t,VOv7LeJjLe/ߤmc}}=0]+zyБ 5^ni$u`94LvY%KᤓN .̴gׅɈ3$f;(Fv}7"C;k 0SiSy_VHd[olw=HI6_TxUy{OxCn#U>bo<b_OZ 0.|a}uPd!8~xmɓ{ﻦ3ݨB$M8nXEhZ0Yr !@ϞR@?#O 澌,70JZt=#<+L Kd/{o]|~tЋexMaF*.b8{h3c{a:pRI}}$fM({vYW>sI|㷐;m@*ĿoHCj Y? b`Ԓ $&޽{B囑O-vm e,2@ X֖`r$H`"i²vYڵaCQ hbc iw8 `:\bˇ k?6jTW{t>%{k& {2M3 D+ Zh`"@^f!-锈_AB,]XuLʪ:cHg<ϙBϦC .("Y-y ~@-f"[ e.1he Dp3!h=Xd1_+8ڵs_s?o9N`ݺ 0!8N>Y7džɓ%hBcO& ė~6: t.Tց̑ ݺugO볂>뮿>&\G `z uwlg7P3 `z$)]OF D^IQ1k <%:.Cop^wXY|:):#+oXs|{6J"kx `!El @}6n;Af߈h]g7 [O`)xI.`a"&_Rotc|UjW t[>gW{x<,i:---̧>_u$# ֯_+_ ljKI0g_K``@yy&wyw8 -?[X+с/%8t e돟q2D(--͚ ໒!>B 8v-aFMX~'taw` uk,L Ua 7e)=}`X!Ui9'!=8L޷wuj_k`ggcٮK0;w%x~!!wV)ÀMpXm,U>ot/,l×a !s^\\*4zYL["o (jIcrsw3CهoTE")Izopq?%/(#ګ%"X Й2z׀]fqt_"LЅ)H_ճr@y;=fƲUqST@[;!0"eyhHBGk[[p%Sg$"y^&Qj>> /$Eszr. ý|*@3GqL&q,Fn?b1lM ?8>^kްR5^ E [ n~ʆZ?I! 0ϞE!\ CJLl+{箝 8ڏF@ӽמݔ6"LS†꿴c{} {@8UL/; 77ô)Ss?# KlWvc ɳ~6dpUX'/[;` ۵k!js)adg7 ۸i3L4QƶSq {gz}6.ZIc?zbԊeO.)NCgzesȏ' ,~9s|W \7$?-Z_|9sS~vPeS7 _"9X{Z3ΤwD%v{Y0S@¢[~f$ .-?fa_/00M7lKhAwn=mN,Obٮ:a#Iäu1})(!\7L4d,An[lf`wS$YGi@W>OW^YYi>t c 82c57&غm!C>s7Ho`m6dɧ. \#GBp0<~ ǴU_%(ڒ|W&:`Ŭgx=;GQ-ɶ$+ZB>SC\54N? VrN?jF$ƍ+<`C`X >!$WeZH>,M }$[w%!\5̿Pb(]s~X"sw}T˜|[m N0qjĈ޷hgLL(^3!nXgq^&-~'l̻Ƙpe^M,dx p(T{t\Ռߘy`ׄm#0vl"^vzvո_6vgx;ƀ' =^ @3H>wg}2VW 6\se~,%L/ s*߳g}4ܭ: 20e/*{o&]v(l;la.'֭qYt}1hA#싆蟕-KZ+@;[`Cm3#f>4l$,\~)g^GWY. nw`( CsL;qEӡ}4}{y7;'i4SQQ *>p5! `BXjemfH,Xb[oۺ-ho)0i:,m+irP҃ pm'A2J̭yj>}{@yx)7xMq + !5>.YF2ܾc` ֮s a;Ɩ7kr7CK{Gpg-\6l0K~d XkoM_R{vP&~1~tY@|'`^)xr1e=_`&@lAb"Y"p;v#Kv1ɽQ C 7Ox}O'[w-z"||WU^_YL `!b}~VN {*XwtpO@݋s.3ztN. ƍ-;hT#w+ Pppm,tStr"\D w%!\5tWtxs1Th5 ^`۶m֪piS;?`gxFlo `r *k!_NhC-trGpg}/}[Hp1{>!GS+ {Tp@'Zg,ubGxCϏG&ܵv?!jbb ߕDp!8Y+V8WLwOWB/ߙ l<k|?'v*mGp٣u[U AGM RV\k'GU^L& 3 OHrN`~AKz@eSy7:س"OdC:@$p"Ƴ83' IjCw]Oj(Ss_\ܓTtTӀtaaJ{w'/,hH{s'LiDP /|2UUtꒄ> =_L\v{axϒgT=;ų U!|]X>}Q;·- x|.a8e3fj.Qܽ fΒ?|%-{#KNtaѕ\9~xd.<$Wu-xݰ۱7)@~Yo7<\tg~C+}|'X6@a|Tt]& \8D,:j >{SiӀЦցN`q=|/gjr_чXFkg ;ZϤa>+2BS1fт)}@U#"Oǹc { #ƖAgɫUBۛ{ {:g tbi;t谷CPۅ3{2 0窠 t&kq%.R|oK_<8XCW:0_{!ːL"e2!\ H -WqhAZCzְaT}lZ@7JWZ}; mCq|W@l¯sC˖{[jA-dݗqsG}~!>w/u>NTvߩ#? uz|#;Ч}o-!= K $z|?[G-9-U|8 uێːBbbyzD_0Xty4bRop%PB>o(}SG;PǐpmC!IGfBY;/@4?F`ףk)t_$# 着URK;w,wdMZs7 XJP<~ >4ru;@E56֭G` * {gĕ|,ĉ:p6£7X2@%^: $ 0۝Φmt \N%~&bT: ;M/W7xoE˴TB(B߿_w{a7%ƛ^gw8Н@ @ |`4~QOr _^٣#N$C N<R>~%/wNLtDiT'v`4ȅ>8 TQXx}+:]et $7GYt{cF KUC@?z`BVM-<#\oOR(nL(OO `ϣ w 8^ʙXL @2a@އ|YgoE0fۀ+j/%oܡvN&<~; S j0{yn$<[y[nG`odx0zˮ Л!7[qT }?[ZXG9~>u^n8(iك]S9P"3 $:dNŋwP|eYv({?fw?\kL[-FmyZxZ}f9_vNn@kB`u+-Ao-Ƭ,ɕ.>\ID~\/Cj2@y's S7<:oKHnL p<6s0r!Q{M?~;- qpJ0(a0i3쇽6 q :%+Tw?~/_M:1"PҚ0 F-eB 4ۂ -?I{rݪ蝾|n C3 P} !{ @ w% qk'% > *$YWvw`|8vmAZpٔ'گ$"Ӎ%-$Qb( tVs8k0-(>8Hzk<g{`1,#,`/ߞ׍zwUM-=K2U~S~ "06:ے;Hh\<#^}сkPx %KU[04N߿X +@:. 1=^tt߽i }ثNԷCcGQCk!Tµ?(ozt}vs{@ !n=J{PK@-WXZ8z4 MIg d'IDp9gyR &܏xg&n /|wWCݳwYc~D^tVFb7Z.)խJ T,+i^C5T>>>8Q_2x %?7~6wWO}_$ h x$<{=NNidT‚BopBz|O5Jw% /|3~b,Z#a?ػuzr?8{aX܈hd` >w}B-BQTu/Ec_f {B\KLvTr#%MwQi6q rgrkAbAw4-fhj 2<6"콏Rsx%=n{ңA%=b_!/ T>u}nd"zm༳`"裏[Jiפ§sK';P.>L_.{joG8tx&U=Cmt+"u+du2^n?8] (MNPUl8^?axGy-'"V@&Vo!9{ZbZ֣9H%q^t%3OX;ʁ g̤s cMA1;VS(CՄprgC@ۗjp NRX(.tA0h1*k9@g.R2Wxaq9@f|v?nٮt ݋t]g߽㊕RS˕|a7,Bwu:m{峵ᛁ&!|uRK`}ׁn'lR%n284/-s j q*hKCλHg|w ^8~ Agqo90'[C[H: )0;}uPp6N>Q&" f:X|xДoqmKLgM% )9^c{ίQtC:-پPݺ$44W܂ ˬ pY$N\cL@*ޗ-Up:/Hpw=4u 0@ I^+5J۽M֯g~Quۏ{nCFM @GIF.N'`xI&"|xsσ8-gOetlNZɲ_V2uКi6.hSyO ;|/k  LVM۫u !:l hS&#Ng{-;fԌ" CwMD|eIC+ ?7@{wo$MEa`#@/VlhOv+vuk|eLt^o2d%d nYݘU@.d 'IpxW@:-4 g1qMւyw Cȓ{q{a T av.{WO/n= `Lt+-*%UaԄ¶i  q:ƍ?qɠL_dfSZsx 8}apywv=R!-+au;F){د5ձ}Y9w"]7Np`eK @`9Dwx@8 w,N }3%"ؽoԭZAu+(5q!OF\$ qi\Cwޖ݅ip|0&<[; sQ0t_3]ӄޡ>|=|"0 GV`MhC?@߈W0}WID4w1}GU? {_ϫAԱUl)kSl{PO;RW7ꆯI<>J %NxIuK?,MB8\xm֞6(|0sp wt`L+X&Q mV[ [@#!&,B/KR*T_B,|h+<1@qY4Թ' @5,#Cߞh >~9xe3h~v#AS7=qk~ҹӀVVPFLBgG۠ɍ9[x&2lx{|׏p%Â9{WfѓC`%jk z;z;|&;n5m@l?@2\oǐ6e}!PřC(x `5,'C.dww=ۯe`A:7 $` Xr 7G `~YY7JTP7DѭPMqf<0wֆQf'>+|惥A4_fw;'2U| IYDp s$bx7[p^~i>'c #w=4&pKƧ(l7v[l}7.Ws1[o_Oͥn*\,}^[ $nY$iL%"Y5,[{t{l/}Z..ރx]B \FhhUIhzibe5Aꟗ@Tݨq@E4dm].D'Nd08Ex7X5ۭyJ9uezP>(>y4tjPp>! R =J] `…!@RL!m.#D%IDpcKnO~pr>t_5sBp~ZG_E x(̺(g=' !9v艭R[ia6& ؂`my(;DN=] [hy+cy XK\ 7Nn("C Y=8+Ba|rcoi'ϯ]>Yg.Ԑ!.J#`ń2ܣ @iɴ@s{ap!d1B搓Hqt7K!0i<@`tL=WBwUJ"C?~AX 7G z (?/j Q[GwEݚIX'e:]050` 9[iq7#`y?-'~@t?,K$$#g{ǬV 5$fߡi`u@C;M'/h 뀹WKn@n.t)IjIM ]J.t+), 8nR@'7-InԱ[s-w%CEضp K!Z?]\!hk݈n`M቎-IlhK{ }KT>G=] >ٛI%I}r7hxz xc噍^Dw]IFgᬰ%Ch(`+[oO@Pݾ1;בHyuC"@2p%ce"mŋem@NKY# M°ˠ_F8ob<-k ==2dQ& P;7u-W`pmzV6x`f-Ȇ7ʠXtMnwF Wz@Qo< XRw%N~# B^f[JDXyƏϷ@J2y~R[k+eozzYW}U wÛݡ{ λ̈́&Lπg{?]{ɷK"ҭTI1 ^jhxFN'M] yO:ٞu'&pcÇaKw )zY.<i/@#y&γ"RxOWpVl@=C 6ِ\k 3M@ `H8ѥZl໒fo[6^~3AB ۿMqxh-}Z^ }a@{-d Wz*A6=0J7:RYc[HiGVpR>`!=Zg2=_~uV.QW!C{A{;{Ǐg(9E,m<87 &cY9ȎNMh\Jt BusDg{|yӘɦ{q`WAY+`4[n[v?85q^U@]@# -/P/쳵wt@}TY^ͅB7̧H=` lH: IYNOXGK/X7%#?/1S0_+^ɯ;{W;{蔑ur5ڻX 5pgTŒ򒼗pqr. .)DX[vνH/u8_bIO[7.Vnrs4L[uX^z%\ `UsͺW\NP-;ʯ뀞CtyA4 խ9 [s[)٬o0ӜI.u+e Z)moݞ)%85?SY~=SJ& ໲ްv| X8x5+[G`j"HW@!nםaDܡ_~JFANr[Ia32f?hzNN3ntK໲ ԓ)PWg/-ɭ5F\[dLwR($Cnb# @l,Mlj5GK݊L@ȉw+@͂鷙Scii=KJb1[¶]N $/L0\ uϪ.h@Mw/`ku@ԡhhTJM:SrNj !o0V3rV/-7喏_|FN)0 .d`M}dVTG.$ׂ@!W@UJv5 r>LɥnPELx=4=/l|&i)`ʭ{kDONgׁm=ސːd #?O?V~hupT'z e!V;4i =dYKp&!H{& @}&ݼO*Dh)`--l&"i'yǺw-`d4N>@{4(3w SK G}4iu{-햂l{B+4[ 82,zx 7VRFWtܞˍnvSnTWNh$ RcF@ktbEf=%ρ^o0_^Xjhvz)؍%;k0Bw& :y]nYӖ>.GMR=Yݘ:MD'gN%>Z+R`_n07 Z@%%@< nk` ڐs+ "}|2FGqvelHWQ^PBN@7!mw7_X^%qE 7RCA$qP4ZzCMJlMmA+mmH ݊ >CDI -@,{\imp=r$l&"II,2x0/Ezm 섿$Ӻ7X,o@1`mn :vM+ J>t+. !XVMI!%^ A 7ܶr{+zci0[`8 !0 ua Wŝ [/# !H/%=Q›;gxP!m,V9|/ á ) rkVFH6Z>h-{nL&"J-gyڛt)ıg>#! ;z]rJ5T.-A}Ԓ xq riD$0E-YKrpErݒT7U9nL&" c0@Z~io y)耯 pꗌ2B '!b: 3|p\wP`l \J>t+ׂG~!{޾ݲ`-Tۀ7AgN@~৾V\O=FAWC@ 1A0WWvE~qC5{lCBBNaSɇnf/"VCcr[.~}ԍD0\cnt, k3 _ qvQ"ϦeW(  P%%s9l( Ŭ_3܋A BIAU;ɋn/n6^'%r|ܖ& ԕ43x | -s@UP_}{uy./\H'"do%-~%JxtC7t¶tt[rԕ4!f BX@V0Em+CO( hrQhbF?=7eX%z\6<Jt ;$DVT7t\_:MDcGhl =_E No4WԶX+J^0@cOh oPew +_Kɗn2#\+R`u}*uFq:W9w ϫ(i^!+,(:S$}aqp7}۲qϡ3%_Q7 3`⏧)u ["(53} `<S.Hp>(h1A qW|x(:)|B2&L+b(KBgJt+-)uV۝Unq-!, eo  li'^CP}$MSG"0r}mMh^|f~R:.M-:i"3r_cG,:`@^'9 %8ukS#:ݭ-R!6> 5/p& TW}ݼ APssQn *uFp@SS#]cfƛc!3y{&v}'kF @V}z ̬LAP8_6F'a{t+3fXǝQn-1kXM t% 3#wq`qan !k}JŬc:JֶVDGV::44|H>ter[ܒꖈF np*-|GֲS~-e.]07ntlɥn0$Yf[l t:MLnGcMGDʒJ>Uh z6F[L5/ ~lh$00 p Fx (k39P-F YCRÙJ Z `78xJ ~$& P-pq*Hl@ |s†  ,g;/q*H?$& `oH]+aD7Yr% ?~47A徽|.W Ç+[+Ee~ۦJG } \wzrW`w@9'S[ߧH{l!ːgRI%[bFD ~zDg*|!&lw0H PGL OWMlx +rxa}w/\B\v?sϒ?9,m?`lxO_:t=DLt( bPL T_aZ{CR-!l?OlSI%HG{{֭[ Ƿp/:B+8Hg)/SI|i0W5w!4$`w#pORI%/,tܬIf6"@_|aCJ;SYh!&?ǂRWI,T/rO) J<" PFF@# )SI)" d+XO TX.XWVDO%Hɀq8'TRI忱!~*@*@*@*@*@*@*@*@*@*@*@*@*@*@*@*@*@*@*@*@*@*b1fIENDB`(                                                                                                                              !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! #2;?@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@?;2#$#":OMKONKONKONKONKONKONKONKONKONKONKONKONKONKONKONKONKONKONKONKONKONKONKONKONKONKONKONKONKONKONKONKONKONKONKONKONKONKONKONKONKONKONKONKONKONKONKONKONKONKONKONKONKONKONKONKONKONKONKONKONKONKONKONKONKONKONKONKONKONKONKONKONKONKONKONKONKONKONKONKONKONKONKONKONKONKONKONKONKONKONKONKONKONKONKONKONKONKONKONKONKONKONKONKONKONKONKONKONKONKONKONKONKONKONKONKONKONKONKONKONKONKNMJa7߹EMOJJJPAAA PBBB PBBB PCCC PDDD PEEE!**Z)\(\'['Z'Z'Z&Z&Z&Z&Z%Y %Y %Y %Y $Y "4 &F #X #W #W "W "W "W "W !V !V !V !V !V U USJ  PPPPPPPPPPPPPPPP* HPPPPPPPPPQQQQQQ K !QQQQQQQQQPPPPPPP* HPPPPPPPPPPPPPPPJ PFFF&2%G $j  +=v ,=v PFGG'3&H%k     +>x ,>x PHHH(4&H&m  +                >y            ,                >y                PIII)5'I'n  +                >z              ,                ?z                PJJJ*6 !!    (I)o   +                ?{          ,                ?{                 PKKK+6$#%$$$$$####"""!!)J*p     +                ? |         -@!}                 PLLL -8-!*#*")")")!(!(!(!' ' ' ' & &&+I/j$$$####"""""!"{  +=%s$| -=&t "{  PMMM!!!.93"0$0#0#0#/"/"/".!.!.!. - - - ,,H1g++****))))))'&v  +;)o """""!(w -!!!         <)o &v  PNNN"##/:*,&0&/&/%/%/%/%.%.$.$-$-$-$,#,#,,M/t"*!*!*!* ) ) ) )((((# +@# !!!!!!" -     A$ POOO$$%0;-0)4)3(3(3(2'2'2'2'1&1&1&1%1%0%0-N0u$/$.$.#.#.#-"-"-"-",!,!,$!!  +!!!!!!!!!!!!!!!!@$!!!!!!!!$%%$$$$$ .$$$$$$#########"A%"""""""!!!!!!!!  PPPP%%%1</3,7+7+7+6+6*6*5*5)5)4)4(4(4(3'3.O1v&2%2%2%2%1%1%1%0$0$0$0$/$$$! +$$$$$$$$$$$$$$$$@%$$$$$$$$(((((('% .''''''''&&&&&&&&B&%%%%%%%%%%$$$$$! PQQQ&''2= 26/:/:.:.:.:.9-9-9-9,8,8,8+7+7+7.P3w)5)5)5(4(4(4'4'4'4'4'3$1'''# +''''''''''''''''A&'''''''(,,,++++' .***********))))) B((((((((((((('''# PRRR(((3>"4:1?1>0>0>0=/=/=/0>0>1R5z".=.=.=.<-<-<-;,;,;,:+:!1...& +................A(.......!0$3#3#3#3#3#2"2*! /"1"1"1"1"1"1"1!0!0!0!0!0!0!0!0 0 C * 0 0 0 ////////////' PTTT++,5@$B'@(@(@(?'?'?'>'>'>&>&>&>%=%=%=$3K:e;#;#:#:#:":":":":!9!8 ,z,z,z,z.k  +,z,z,z,z,z,z,z,z,z,z,z,z,z,z,z,z 9/g ,z,z,z,z,z,z,z/|2~2~1}1}1}1}1}3o /0|0|/|/|/|/|/|/|/|.{.{.{.{.{.{.{";+^ (q(q(q(q(q'p'p'p'p'p'p'p'p'p'p)b  PUUU---6B%;C9I8H8H8H8H8G8G7G7F7F6F6E6E5E5E3T8|&3D3C3C2C2B2B2B2B2A2A,<#4#4#4#4)! +#4#4#4#4#4#4#4#4#4#4#4#4#4#4#4#4B+#4#4#4#4#4#4#4'9):):):):(:(9(9.& /'8'8'8'8&8&7&7&7&7&7&7&7&6&6&6&6!D PVVV...7C&=F+=+=+=+=+=*=/(  0)<)<)<)<)<)<(;(;(;(;(;(;(:(:(:(:!D PWWW///9D'@I>P>P>O=O=O=N*>*>*>*>*>*=*=!E PXXY0119E)AM@S@S@R@R?R?R?Q?Q>Q>Q=Q=P=P)>)>)>)>-' +)>)>)>)>)>)>)>)>)>)>)>)>)>)>)>)>B .)>)>)>)>)>)>+@1E0E0D0D0D0D/C/C3,  0.C.C.C.B.B-B-B-B-B-B,B,A,A,A,A,A"E PYYZ233;F*DOCVBVBVBVAUAUAUATAT@T@T@S?S?S?S7X#!""=-=Q=QS>S>S=S.E-D-D-D-D-D/+ +-D-D-D-D-D-D-D-D-D-D-D-D-D-D-D-D C 0"-D-D-D-D-D-D3I5K4K4K4K4K4J4J4J61 !13I3I3I2H2H2H2H2H2H1H1H1H1H1H1H0G#F P[[[566=H-HUG]G]G]G]F\F\F\F[E[E[E[DZDZDZCZ:Z%%%%@1BXBXBXAXAXAW@W@W:Q/H/H/H/H/H/H0- +/H/H/H/H/H/H/H/H/H/H/H/H/H/H/H/H C 1$/H/H/H/H/H/H7N7O7O7N6N6N6N6N6N63 !15M5M5M5M5L4L4L4L4L4L4L3L3L3L3L3K$G P\\\777:@2@O*@O*?N)>M(>M(=L&=K&X>X>X>X<: P```;<<:;;9::899788777677566455344233122011/00///.//-..---,,,"##**+F7LeKeKeKeKdKdKd;V8T8T8T8T8T8T8T64 -8T8T8T8T8T8T8T8T8T8T8T8T8T8T8T8T!D 5*8T8T8T8T8T?ZA\A\A\A[A[A[@[@[@[>< P`aa==><<=;;<:;;9::889788777677566445344233122011/00///../-..#$$+,,G:OhNhNhMgMgMgHc:W:W:W:W:W:W:W:W76.:W:W:W:W:W:W:W:W:W:W:W:W:W:W:W:W"E 6+:W:W:W:W:WD_D_C_C_C_C^B^B^B^B^?> Pabb>>?=>><==;<=:;;9::899888778777667555444334223112011//////%%%-..H;QkQkPkPkPkOjB_\FbFbFbEbEbEbEbDaDaDaA@ Pbcc?@@>??>>>==><==;<<:;;9::899888777677566455344333122111000%%&///I=SoSnRnRnRmPl>]>]>]>]>]>]>]>]>]:9 />]>]>]>]>]>]>]>]>]>]>]>]>]>]>]>]"E 8.>]>]>]>]CaIfHeHeHeHeGeGeGeGeFdBB PdddABB@AA?@@>??>>>===<==;<<:;;9::899888777677566455344233122'''/00I6R[Q[Q[Q[P[FP>?>>><==<<=;<<::;99:889888777666556445334(((122K8T^S^S^S^S]@L>J>J>J>J>J>J>J>J>J:. 0>J>J>J>J>J>J>J>J>J>J>J>J>J>J>J>J"A 8%>J>J>J>JISITITITHTHTHSHSHSHSHRC7  PeffDDECCDBCCABB@AA?@@>?@>>>=>><==;<<:;;9::899888788677566555)))333NBYwYwYwXvPoCeCeCeCeCeCeCeCeCeCe=?  0CeCeCeCeCeCeCeCeCeCeCeCeCeCeCeCe#F ;2CeCeCeFgOoOoNnNnNnMmMmMmMmMmMmGI  PfggEFFDEEDDDCCCBCCABB@AA?@@>??>>>===<==;<<:;;9::899888788677***455ND[z[z[z[yHjEhEhEhEhEhEhEhEhEhEh>@  1EhEhEhEhEhEhEhEhEhEhEhEhEhEhEhEh$G<4EhEhEhKmQqQqQqPqPqPqPqOpOpOpOpHJ PghhGHHFGGEFFDEEDDDCCCBCCABB@@A??@>>?>>>===<==;<<::;99:899888+,,667PE^}]}]}VvGkGkGkGkGkGkGkGkGkGkGk?B 1GkGkGkGkGkGkGkGkGkGkGkGkGkGkGkGk$G=5GkGkGkPrSuStRtRtRtRtRtRtRsQsQsJL PiiiIIIHIIFGHEFGEEFDDECDDCCCBBC@AB?@A??@>>>=>>===<<=:;;9::999,,-788QG```MqImImImImImImImImImImImAD!2ImImImImImImImImImImImImImImImIm$H>6ImImJnVwUwUwUwUwTvTvTvTvTvTvTuKN PijkJJJIJJHIIGHIFGGEFFEEEDDDCDDBCCABB@AA?@@???>>>=>><==;<<:;;-..899RHbbZ|KpKpKpKpKpKpKpKpKpKpKpKpBE!2KpKpKpKpKpKpKpKpKpKpKpKpKpKpKpKp%H?8KpKpOtXzXzXzWyWyWyVyVyVyVyVxVxMP PkllKLLJKKIJJIJJHIIGHHFGGEFFEEEDDDCDDBCCABB@AA?@@???>>>=>><==.//:;;TJdcPuLsLsLsLsLsLsLsLsLsLsLsLsCG "2 LsLsLsLsLsLsLsLsLsLsLsLsLsLsLsLs&I?9LsLsUyY}Y}Y}Y}X}X|X|X|X|W|W|W{NR PllmMNNLMMKLLJKKIJJIJJGHIGGHFGGEFFEEEDDDCCDBBCAAB@AA??@???>>>000<<=Ow3WHM<=+=+=+=+=+=+=+=+=+=+=+=+=+9v "3 =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+$>8o=+=+K:L;K;K;K;J:J:J:J9J9J9J9I9F) PmmnNOONNNMNNKMMJKLJKKIJJHIJGHIFGHEFGEEEDDECDDBCCABB@AA?@A??@111=>>WMgSzPxPxPxPxPxPxPxPxPxPxPxPxPxEJ #3 PxPxPxPxPxPxPxPxPxPxPxPxPxPxPxPx&JAS}`baaa```````__TZ PqqqSSTRSSQRRPQQOPPNOONOOMNNLMMKLLJKKIJJIIJGHIFGHFFGEEFDEEDDD455ABCH@UUUUUUUUUUUUUUUHO $4 UUUUUUUUUUUUUUUU(L D?XdddccccbbbbbbaU[  PqrrTUUSTTSSSRSSQRRPQQOPPNOOMNOLMNKLMJKLJJKIJJHIIGHIFGGEFFEEE666;<<>5WWWWWWWWWWWWWWWIQ $5 WWWWWWWWWWWWWWWW(L EA_feeeeeeedddcccV] PrttUWWTVVTUUSTTRSSQSSPQROPQNOPNOOMNNLMMKLLJKKJJJIIJHIIGHHFGG666(()?6XXXXXXXXXXXXXXXJR $5 XXXXXXXXXXXXXXXX)M EBfhhgggffffffeeeW_ PtttWXXVWXUVWTUVTTUSTTRSSQRSPQROPQNOONOOMNNLMMKLLJKKJJJIIIHII%%%"#$?6ZZZZZZZZZZZZZZZKS  %6 ZZZZZZZZZZZZZZZZ*M JFjiiiiihhhhhhgggY` PtuvXYZXXYWXXVWXUVWTUUSTTSTTRSSQRRPQQOPPNOONNOMNNLMMKKLJJKDDD#$%6dB3B3B3B3B3B3B3B3B3B3B3B3B3B3B3?123022012/11/00./0.//,..,-.+--+,,*+,*++)*+(**())'()'((&'(&''%&&$%&$%%#$%#$$"##!"" !" !!  +++777+,,566cexxxxxxwwvvvvvuudr*.$$$$###"""!!! P¿ijjhiighhfggeggdffdeecddbcc`abBCC244234133123022011/01/00-//-..,-.,--+,-+,,*++)++)**()*())'()'((&''%&'%&&$%&$%%#$$"##!"#!"" !! !!  888888,,,677cgyyxxxxxxwwvvvvvet+/% %&&$%%$$$"##!""!!! P¿jklijkhjjgiighhfggefgdffcdeIJJ456355345244234123122012/11.00.//-./-..,-.+--+,,*+,*++)*+)**())'))'((&'(&''%&'%&&$%%#$$"#$"##!""!"" !! ! ++,:;;9::-..888djyyyyxxxxxwwwvvvex,0'!!!'''&&&%&&$%%#$$""#!!" !! PlmmjlljkkijjhjjgiighhfggJLL577567466456355344234133123022/01/00./0-//-./,..,--+,-+,,*+,*++)**()*())'()'((&''&''%&&$%%#$$#$$"##""#!"" !" !!  ;;<<==;<<.//9::elzyyyyyxxxxxwwwvez-2("""((('''&''&&&%%%$$$###"""!!! PmnolmnkmmjlljkkijjhijLNN789688678577567466455345244234133012011/01.00.//-//-..,-.,--+,-*,,*++)*+)**()*())'((&'(&''%&&$%%$%%#$$"#$"##!""!"" !! !  ,-->??=>><==000;;>?@@>??>??112<==ZJepeoeodododncncncncmbmbmblblblYN04+$$$+++***)))(((''('''&&&%&&$%%##$"##!""!!! PpqropqnppmoojllJKL:<<9;;9:;8::89:799688678577567466456345234133123022011/01.00./0-//-..,-.,--+,-*,,*++)*+)**()*())'((&''%&'%&&$%&$%%#$$##$"##!"#!"" !! !!//0BCCABB@AA?@@222>>?fs{{{{{zzyyyyyxxxgƂ26-%&&,--+,,*++***)))((('''&''&&&%%%$$$###"""!!! ! PrssprrpqrjklGII;=>;<=:<<:;<9;;9:;8::799789688678577566456345244233123122012/11/01.00.//-./-..,-.+--+,,*+,*++)*+)**'()'((&'(&''%&&%&&$%%#$%#$$"#$"##!""#$$ABBDDECCDBBCAAB344??@gu||{{{{zzzyyyyyxgƅ37.&''.//-..,,-+,,*++)**())'(('''&&&%&&%%%$$$###"""!!!  PsturstghhCEE=??<>?<=>;==;<=:<<9;<9:;8::89:799789688577567456355344234133123022012/01/00./0-//-./,..,--+,-+,,*+,)++()*())'()'((&''%&'%&&$%&$%%#$$#$$"##455EFGEEFDEECDDBCD455@ABgw||||{{{{zzzyyyyhLj48/'''/00///...---,,,++,***))*())''''''&&&%&&$%%#$$###"""!!!Ptvv`bbABC>@A>?@=??=>?<>>;=>;==:<=:;<9;;9:;8::79:789688678567456455345244234133122012011/01.00.//-./-..,-.,--+,-*,,)*+)**()*())'((&'(&''%&'%&&$%%$%%''(FGGGHHFGGEFFEEEDDE566BCChz}||||{{{{{zzzyyhNJ6:0())011000//////...,--,,,+++***)))(((''''''&&&%%%$$%###"## !! PTUV@BC@BB?AB?@A>@@>?@=??<>?<=>;==;<=:<<:;<9;;8::89:799789678567466456355345234233123022012/11/00./0.//-./,..,--+,-*+,*++)*+(**())'()'((&'(&''%&&$%&:;@A>@@=?@=>?<>><=>;==:<=:;<9;;9:;8::89:688678577567466456345244234133123022011/01.00./0-//-..,-.+,-*,,*++)*+)**()*())'()&((&'',--KLMKLLJKKIJJIIJHIIGHH888EEFi~~~}}}||||{{{{{zjǏ9=4+++444333222112001000///...--.,,-++,*++)**())'(('''&&&%&&$$$###"""!!! P^_`CEEBDEBCDACCABC@BB?AB?AA>@A>?@=??=>?<>>;=>;<=:<<:;<9;;9:;799789688678577566456355345244233123122012/11/01.00.//-./,-.+--+,,*+,*++)*+)**())'()'((CDDNOOMNNLMMKLLJKKIJJIIJ99:FGHdjt۩s۩s۩s۩s۩rۨrۨqڨqڨqڨqڨqڧpڧpڧpڧdu:>5,,,566455444233122111000000///...---,,,+++***)))((('''''' %%&$%%#$$"##""" !! P_``DFFCEFCDEBDDACDACC@BC@AB?AA?@A>@@=?@=??<>><=>;==;<=:<<9;<8::89:799789688577567466456355344234133123022011/01/00./0-./,..,--+,-+,,*+,)++)**()*566OQQOPPNOOMNOLMNKLMJKLJJJ::;HIIectۛtۚsۚsۚsۚsۚsۚrۙrڙqڙqڙqڙqڙqژpژdl;?6---777677566455344233122011000/00.//-..---,,,+++)**)))(((!!!'''&&&%%%$$$###"""!!" P_`aEGGDFGCEFCEEBDEBDDACDABC@BB?AB?AA>@A>?@=??=>?<>>;=>;==:<<9;;9:;8::79:789688678577567456355345244234133122012/11/01.//-./-..,-.,--+,-*,,*+++,,MNNQRSPQROPQOOPNOOMNNLMMKLL;<@@>?@=??<>?<=>;==:<<:;<9;;8::89:799789688678567466456355345234233123022012/00./0.//-./,..,--+,-+,,ABBTTUSTTRSSQRSPQQOPPNOONOOMNN===KLLl{~~~~~}}|||lȊ>B9/009::99:888888677666556344333222111000000///...---,,-++,#$$)))(()'(('''&''%&&$$%$$$"##!""!!! P`bbFHIFHHEGHEGGDFGDEFCEEBDEBDDACDACC@BB@AB?AA>@A>@@=?@=>?<>>;==:<=:;<9;;9:;8::89:799688678577567466455345244234133123011/01.00./0-//-..,-.455UWWUVWTUUSTUSTTRSSQRRPQQOPPNOO>>>MNNl}~~~~~}}|lȍ@D;011;<<:;;9::899888788677566455344233122111000/00///...---%%%+++***)))(((''''''&&&%%%$$$##$"""!!" !! PabcGIJGIIFHIFHHEGHDFGDFFCEFCEEBDEBCDACC@BC@BB?AB?@A>@@>?@=??<>>;=>;<=:<<:;<9;;9:;89:799789688678577566456355345244233122012/11/01./0.//./0PQQXXYWXXVWWUVVTUUSTTRTTRSSPQRPQQ???NOOl~~~~~}mɏAE<222=>><==;<<:;;9::899888777677566455344233122111000/00.//&&&,--+,,*++)**())(((''''''&&&%%%$$$###"""!!! PbccHJKHJJGIJGHIFHIEGHEGGDFGDFFCEFCDEBDDACDACC@BC@AB?AA?@A>@@ =>?<>><=>;==;<=:<<9;<9:;8::89:799789678577567466456355344133123022011/01/00CDDZ[[YZZXYYXXXWWXUWWTVVTUUSTTRSTQRS@AAOPQm~~~~nɒBF=333>??>>?==><<=;;<::;99:889888777667555444333223112111000'''.//--.,,-++,**+)**())((('''&''%&&$%%$$$###"""!!! PbcdIKLIKKHJKGIJGIIFHIFHHEGHEFGDFGCEFCEEBDEBDDACDABC@BB?AB?AA >?@=??=>?<>>;=>;<=:<<:;<9;;9:;8::79978968867857756745635524423413312201289:[]][\\Z\\Z[[YZZXYYWXXVWWUVWTUVTTUSTTABBQRRn~~nɓDH?444?@@??@>??=>>==>;<<:;;9::999888788777666455344333222111(((000///...---,,,+++***))*(((((('''&&&%%&$$$#$$"##!""!!! PcddJLMILLIKLHJKHJJGIJGIIFHIFGHEGGDFGDFFCEFCDEBDEBCDACC@BC@BB !?@A>@@>?@=??<>?<=>;==;<=:<<:;<9;;8::89:799789688678567466355345234233244WXX^__]^^\]][\\Z[[Y[[XYZXXYWXXVWWUVVTUUCCCSTT^~Nbbabaa`````______^^]]]]]]]\\[[W{JEHB566ABB@AA?@@???>>?=>><==;<<:;;9::999888788677566455344233)))111000/00.//-..,--,,,*++***)))(((((('''&&&%%%$$$###""#!!!PceeKMNJLMJLMIKLIKKHJKHJJGIJFHIFHHEGHEGGDFGDEFCEEBDEBDDACDABC !!@AB?AA>@A>@@=?@=>?<>><=>;==:<=:;<9;;9:;8::89:799688678577466455345244MOO`aa_`a^_`]__\^^[\][\\Z[[YZ[XYZXXYWXXVWWDDETUUSTTRSTQRSPQRPPQOOPNOOMNNLMMKLMKKKJJJIJJHIIGHHFGGEFFEEE677CDDBCCABB@AA?@@???>>>=>><<=;;<:;;9::999888778677556455***233112111000/00.//--.,,-+,,*++)**)))((('((&''&&&%%%$$$###PdeeLNOKMNKMMJLMJLLIKLHJKHJKGIJGIIFHIFHHEGHDFGDFFCEFCEEBDEBCD !"@BC@BB?AB?@A>@@>?@=??<>?<>>;==;<=:<<:;<9;;8:;89:799789688577466456BDDcddbcc`bb`aa_``^_`]^_\]^[\][\\Z[[YZZXYYWXXEEEUWWTVVTTUSTTRSSQRSPQRPPPOOONOOMNNLMMKLLKKKJJJIJJHIIGHHFGG788EEEDDDCCDBBC@AB@@A??@>??>>>===;<<;;;::;99:999888777666++,444333222111111000///...---,,,++,***))*(((((('((&&'%&&$%%PdffMOPLNOLNNKMNJMMJLMIKLIKKHJKHJJGIJFHIFHIEGHEGGDFGDFFCEFCDE!""ACDACC@BC@AB?AA?@A>@@=?@=>?<>><=>;==;<=:<<9;<9:;8::89:799678577<=>bccdeecddbccacc`bb``a_``^__]^_\]^[\\[\\Z[[YZZFGGWXXVWWUVWTUVTTUSSTRSSQRRPQQPPPOOONOOMNNLMMKLLJKKIJJHIJHII999EFGEEFDDECDDBCCABB@AA?@@???>>?=>><==;<<:;;9::999888788---566455344233222111011000///...---,,,+++***)))(((((('''&&&PefgNPQMOPMOOLNOKNNKMNJLMJLLIKLIKKHJKGIJGIIFHIFHHEGHEFGDFGCEF!"#BDEBDDACDABC@BB?AB?AA>@A>?@=??=>?<>>;=>;<=:<<:;<9;;9:;8::7899:;^``fggeggdffceecddbccabc`ab``a__`^__]^^\]][\\Z[\GHHXZZWXYWXXVWWUVWTUUTTTSSSRSSQRRPQQOPPNOOMNOLMNKLMKKLJKKIJJ:::GHHFGGEFFEEEDDDCDDBCCABB@AA?@@???>>>=>><==;<<:;;9::999...788677566455344233122111011/00.//-..,--+,,+++***)))((((((PeggOQQNPQMPPMOPLNOLNOKMNKMMJLMILLIKLHJKHJJGIJGIIFHIEGHEGGDFGDFFCEFCDEBDDACDACC@BC@BB?AB?@A>@@>?@=??<>?<=>;==;<=:<<:;<9:;8::89:YZ[hjjgiighhfggeffdefcdecddbccabc`aa_``__`^__\^^\\][\\Z[\YZZXYYWXXWXXVWWUVVTUUSTTSSSRSSPQRPPQOOPNOOMNNLMMKLLKKKJKKIJJHIIGHHFGGEFFEEEDDDCDDBCCAAB@@A??@???>>>=>><<=;;<::;99:999888778667555445344222122111001///...--.,,-+,,*++)**())PfghPRROQRNQQNPQMOPMOOLNOLNNKMNJLMJLMIKLIKKHJKHIJGIJFHIFHHEGHEGGDFGDEFCEEBDEBDDACDABC@BB@AB?AA>@A>@@=?@=>?<>><=>;==:<=:;<9;;RTTklljkkijjhjjgiifhhfggeffdefcdecddbccabb`aa_``^__]^_\]^\\][\\Z[[YZZXYYWXXVXXVWWTUVTUUSTTRSSQRRPQRPPQOOONOOMNNLMMKLLJKKJKKIJJHIIGGHFFGEFFDEEDDDCCDBBC@AB@@@???>??>>>==><<<:;;:::999899888777666555444333122111011000///...---,,,+++**+PghhPSSPRSOQROQRNPQNPPMOPLOOLNOKMNKMMJLMJLLIKLHJKHJKGIJGIIFHIFGHEGHDFGDFFCEFCEEBDEBCDACC@BC@BB?AB?@A>@@>?@=??<>?<>>;==;<=MNOmnolmnkmmjlljkkijjhiighifghfggeffdefcddbddacc`bb_`a_``^__]^^\]]\\\Z[\Y[[XZZXYYWXXVWXUVWTUUSTTSTTRSSQRRPQQPPPOOONOOMNNLMMKKLJKKIJKHIJGHIFGGEFFEEEDEECDDBCCABB@AA?@@???>??=>><==;<<:;;9::999899788677566455344233122111011/00.//-..---,,,PgiiQTTQSTPRSPRSOQRNQQNPQMOPMOPLNOLNNKMNJMMJLMIKLIKKHJKHJJGIJFHIFHHEGHEGGDFGDEFCEFBDEBDDACDACC@BC@AB?AA>@A>@@=?@=>?<>>JKLnopoppmoomnnlmmkmmjlljkkijjhiighifghfggdffdeecddbcdabc`ab_``_``^__]^^\]][\\Z[[YZ[XYZXXYWXXVWWUVVTUUSTTSTTRSSQRRPQQOPPOOONOOLMNKLMKKLJKKIJJHIIGHHFGGEFFEEEDEECDDBCCABB@AA?@@???>??=>><==;<<:;;9::999899788667566455334223112011000/00../-..PhijRTURTUQSTQSSPRSORROQRNPQNPQMOPMOOLNOKMNKMNJLMJLLIKLIKKHJKGIJGIIFHIFHHEGHEFGDFFCEFCEEBDEBDDACDABC@BB?AB?AA>@A>?@IKLoppqrrpqropqoppmoomnnlmmklmjklijkijjgiighhfggefgdffddecddbccabb`aa_``^``]__\]^\\][\\Z[[YZZXYYXXXWWXVWWUVVTUUSTTRSTQRSPQRPPQOPPNOOMNNLMNKLLJKKJKKIJJHIIGHHFGGEFFDEEDDECCDBBCABB@AA??@>??>>?==><<<;;<9::99:899888777666556444333222111011000///PmnnZ\]Z\\Y[\X[[XZ[XZZWYZVXYVXXUWXUWXUVWTVVTUVSUUSUURTUQSTQSTQRSPRRPQROQQNPQNPPNOPMOOMNOLNNLMNKMMJLMJLLJKLIKKIJKOQQxzzz{|yz{yzzxyywxywwxuwwuvvtuusttrttrssqrrprropqoopnoomnolmnllmklljklijjiijhiighhfggeffeeeddecddbccabbaab`aa_``^__^^^]^^\]][\\Z[[ZZZYZZXYYWXXVWXUVVUVVUUUSTTRSTQRRQQRPPQOPPOOPNNNMMNLMMKKLKKKJKKIJJHIIGHHFGGFFFEFFDEEDDECDDBBCAAB@AA?@@>??>>>==>PO¿OJ?¿-vtq¿¿¿¿omj//.  yxwOONNNMLLLKKJKKJKJJJJJJJJJJIJJIJIIIIIIIHIIHIHHHHHHHHHHGHGGHGGGGGGGFGFFGFFFFFFFEFFEFEEEEEEEDEEDEDDEDDDDCDDCDCCDCCCCBCCBCBBCBABBABAABAAAA@AA@A@@@@?@@?@??@?>??>?>>?>=>>=>==>=<==<=<;=<;<<;<;:<;:;;:;:9;:9::9:98:98:98988987987987987988988:98:98:98:99;:9;:9;:9;::;;:<;:<;:=<;=<<=<;=<;<<;<;;<<<<<<=<<==<>>=>>=??>@@???>@@?@@@A@@@@?@???>>==<>==>>>>>>>>=>>>@@?BBAA@@;;:WWVqpommk(@  B)('n)('u)('u)('u)('u)('u)('u)('u)('u)('u)('u)('u)('u)('u)('u)('u)('u)('u)('u)('u)('u)('u)('u)('u)('u)('u)('u)('u)('u)('u)('u)('u)('u)('u)('u)('u)('u)('u)('u)('u)('u)('u)('u)('u)('u)('u)('u)('u)('u)('u)('u)('u)('u)('u)('u)('u)('u)('u)('u)('u)('r ;YXVeddeedfedfeeffegfeggfhgfhgghhgihhiihjiijiijjikjjkkjlkkkjjiihiihiihiihiihiihiihiihiihiihiihiihiihiihiihiihiihiihiihiihiihiihiihiihiihiihiihiihiihiihiihiihiihiihiihiihiihiihiih`_]zyx MMM`_]zyx MMM`_]zyx7655443 3   2 1 1 0 0 0 / )  ) ) ) ) ) ) ) ' ) ) ) )***  ****) ) ) )   ) ) ) ) ) ) )'NOM`_]{zy%* <    blG < QRM`_]}|{!'+> e       n      I       =        QRM`_]}}$*"""!! ,@g       q     K       ?       QRM`_]',.",","+!+!* * ),!=''&&%%  \ e ! D : QRM`_])/!*0'1'0&0&/%/%.$.0!D#,","+!+ * *!lv #""N""!!!!! !B    QRM`_]+1#/7-8,8,7+7*6*5)5!2$F'3'3&2&2&2%1%n%%%%%%%y%%%(*))P((((('''"C &&&&&&%QRM`_].4&4=2?1?0>0>0=/=/<#3&H-;,:,:+9+8&4,!$p,,,,,,,{"!,,,!0"1"1!0Q!/!/!/ . . . .."E.------"$QRM`_]17(=7<8;7;7:6:69595%3*E7363626251-('#)!c'#'#'#'#'#'#'##l*'#'#'#,(-),(,(J+'+'*&*&*&*%*%*%!: : : 9 9 9 9 6 OPN`_]39+>K,>U+>*=*=*=)<)<)<);$NNN`_]6<-BQAT@T@S?S?R>Q>Q)9/Q'1GWF[EZEZDYCYCXBX+;!1S*AV@V@U?U5L.F.F/9$z#.F.F.F.F.F.F.F'(/4.F.F1I6M5L5L5L"Y4K4J3J3J3J2J2J2I% NNN`_]896;C09A/8@-6>+5=*4<)2:'19&&)#4O)FGFGEFDE5635353+ 'j35353535353535*s3'35359:<=;=;<:<%P     OON`_]::;889677556344122/00../,--&''7X0KcJcJbC]7R7R7R6B *+7R7R7R7R7R7R7R,.6=7R7R>Y@Z@Y?Y?Y']# OOO`_]==>;<<9::788677455233011///))):[3OiOiNh@\;X;X;X9G ,.;X;X;X;X;X;X;X.29B;X>?<==:;;99:788666445223++,><==:;;899777566...?]8VjVjI_@W@W@W@W>G  0}/@W@W@W@W@W@W@W11=A@WG]LaKaJ`J`J`,]' PPP`_]EFGDDDBCC@AA???==>;<<9::888111Bc=\{ZzGjFiFiFiFiBU  38FiFiFiFiFiFiFi5<AOFiPqRrQrQrQrPq.d- PPP`_]IIIGHHEFFDDDBBC@AA>??===;<<344Ee@aTwJoJoJoJoJoFY 6;JoJoJoJoJoJoJo7?DSKpWyVxVxUxUwUw1f/ QQP`_]KLMJJKHIIFGGEEECDDABB?@@>>>666F`=[cEODODODODODOA@ 3u,DODODODODODODO4~-@;JTR\R\Q[Q[QZPZ0Z' QQQ`_]NOOMNNKLLIJJHIIFGGDEECCCABB899JkF[QyQyQyQyQyQyKb :AQyQyQyQyQyQyQyIL_cbbaaa`6j6 RRR`_]TUVSTTQRROPPMNOLMMJKKHIIFGH=>>8]4WWWWWWWPj>GWWWWWWW@L Tfgfeeedd8l9 SSR`_]WXYVWWTUURSSPQROPPMNNKLLJJK111/M%N]N]N]N]N]N]N]IK0b,?I?H>H>H>H>H>G0f, TQ_l^k^k]k]j]j\i7`/ SSS`_]Z[\XZZWXXUVWSTURSSPQQNOOHII3X/]]]]]]]Uqdwmmmllkk=q? TTT`_]]^_[\]Z[[XYYWWXTVVSTTQRR2334Z1```````Wtg{qppooon?sA TTT`_]`ab^``\^^[\\YZ[XXYVWW?@@*+, 6[3bbbbbbbYw&&&j~tssrrqqBuDUUU`_]cddabc`aa^__\]^[\\KLM-//,-. !!/?*>wA=v@=u@;s>7i5 ,--bbixhxhxgwgwfvfv@h9UUU`_]fggdefcddabb_`aUVV234/01./0"##,--*,,)++()*'()&''%&&$%%##$ !! ---/00mمxwwvvuuFyJ !!""" VVV`_]ijjghifggdee\\]789234123012#$$-//,..+,-*+,)**())'((%''$%&"##!"" !!  899222nڌxxxwwvvHzN###%%%###!!!WWW`_]lmmjklhjj`ab;==567456345233$%&/11./0-./,--+,,*++(**'()&'($%%#$$"## !" 112<==455oڒyyxxxwwJ|R%%&''(&&&$$$"##!!!XXW`_]oppmnobcd>?@89:789678566355&''123012/01.//-..+--*+,)*+())&''%&&#$%"#$!"" !!#$$@AA?@@788h{pڗoڗoڗnٖmٖmٕmٕIrJ(((*++())'''%&&$$$""" XXX`_]rssabc?@A;==:;<9:;79:688577'()345234122/11.00-./,-.+,-*++())&'(%&'$%%#$$"##789DDEBCC:::qڞ{{zzyyxMZ+++-..,,,***(((&&'%%%###!!"YYX`_]Z\\?AB>@@=>?<=>:<=9;;8::799()*567456244133022/01./0-..,-- !!)*+()*'((&''%&&*++HIIGHHEEF<==sۤ||{{zzyO^--.000///---+++))*'((&&&$%% !!!YYY`_]BDDACC@AB?@A=?@<>?;==:<<9:;*++789577466355234123012/00-//!""+,-*+,)**())'((BCCKLLJJKHII??@oΙxxwvvuuP|[000344122000.//,--*++)))'''###$$$""" ZZZ`_]DFFCDEACD@BC?AB>@@=??<=>;<=+,-8::799688567456345233022/11"$$-./,--+,,*++566PQQNOOMNNKLLBBBpΎyyxxwvvR~X233677455333111/00...,,,***%%%'''%%%#$$""" \\[`_]FHHDFGCEFBDEACC@BB?AA>?@<>?,..:<<9;;89:789678466355244123$%%/01.//-..-//OQQSTTQRSOPQNOODEEvۣ~~}}|Uc56699:788667445233011///-..((()**(((&''%%%###!!!]]]`_]GIJFHIEGHDFGCEEBDDABC?AB>@A./0<>>;==:;<9:;79:688577456345%&'122/11.00FHHXYYVWWTUVSTTQRRGHHwܩ~~}Wg888<==:;;999777566344122000+++---+++)))'((&&&$$$"## !!___`_]IKLHJKGIJFHHEGGDEFBDEACD@BB/11>@@=>?<=>:<=9;;8::799678567'((244133<>>\]^[\\YZZWXXVWWTUUJKKlqƛpƚpŚořnřnęmĘSuX:;;?@@>>><<=::;899777556334---000../,--*++())'''&&&$$$"""aaa`_]KMNJLMIKKHJJFHIEGHDFGCEFBDD122@AB>@A=?@<>>;==:<<9:;89:789())466789\]^_`a^__\]]Z[\YYZWXXLMMSTURSSPQQOOOMMNKKLIJJGHHEFF=>>BCC@AA???=>>;<<9::888677000233111/00-..,,,***((('''%%%ccb`_]MOPLNOKMMIKLHJKGIJFHIEGGDFF234ACD@BC?AB>@@=??<=>;<=9;<8::)*+789XZZdeebddabb_``]^_\]]Z[[OPPVWXUVVSTTQRRPPQNOOLMMJKKIJJ@@AEFFDDDBBC@@A>??===;;<999333666444222011///---++,))*(((eed`_]OQRNPPLOOKMNJLMIKLHJKGIIFHHDFGCEFBDEACC@BB?@A>?@<>?;==:<<9;;SUUijjghiefgdeebcd`ab__`]^^[\\YZ[WYYVWWTUUSSTQRROPPMNNLLMJKKHIIFGGEEECDDABB?@@>>><==:;;999788566344122000.//,--*++fff`_]QSTORRNPQMOPLNOKMNJLLIKKGIJFHIEGHDFFCEEBDDABC?AB>@A=??<>>PQRmnolmmjkkhijghhefgcdebcc`aa^__\]^[[\YZZWXYUVWTUURSSPQQOPPMNNKLLIJKHIIFGGDEECCDAAB??@=>>;<<:::899677555333112/00...hhh`_]VXYUWXTVVSUUQTTPRSOQRNPQMOPLNOKMNJLLIKKHJJGIIFGHEFGDEFTUVuvvtuurstqrroppmoolmmjklijjghhefgdeebcc`ab_``]^^\\]Z[[XYYWXXUVVSTTQRSPQQNOPLMNKLLIJJHHIFFGDEECDDABB?@@>??<==:;;999777nnm`_]`_]`_]KJItHHG ((( ''''&&&&&&&&&&&&&%%%%%%%%%%%%$%$$$$$$$$$$#$##########"#""""""""""!!!!!!!!! !        !!!!!!!!!!!!   !! ""! &&% @@?(0`                                            =^aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaK/.. HGGGFF&&&&&&%%%%%$$$$$$$######"""""""""!!!!!!}}|fed777 rqqkji::9GPOOON ( . L L K K K 4 E E E E E C @ E E E F F  , F F E E E . E E E E E Assqkji<<<HV o $     6` f . tuqkji???#! KZs %         8c     i /     tuqkjiCCC/"+%*%)$(#(" I"V% %$$k # 6] b -tuqkjiFFF/-*5)4(3'2'2"R#a%0$/#.#.&{ &""""" ""$&&< j%%$$$q2###""tuqkjiIII451>0=/=/<.;%U 'e$,9+8*8)7,~'+++++'  #++ /!0!/>m . .---t4 ,,,,, %tuqkjiLLL=5<=;<:;::9:*R"-`%68675612&(!s%&(&(&(&(&(($ ) &(&(,-,-,-;"e*,*+)+)+)*"k , , , + + )ssqkjiOOO?C?Q>P=OS/F-E&** -E-E-E-E-E.> .8-E0G5L4K4KD'x)3I2I2I1I1H&)rrrkjiVVV:?38>06<.4:,28*06((+$9k3GMFMAH4<4<*}%( 4<4<4<4<4<46 404<8@y?MfLe@[9U9U.5.9U9U9U9U9U8M 8E9U@[B]A\A\ I ssrkji\]]>>>;<<9::778555233,--AxAQfOd=T=T=T15.=T=T=T=T=T>;;<899677011E|FWnLeA[A[A[4:0A[A[A[A[A[@S ?JD]MeLeLdKd$J! tsskjicddEFGCDDABB???===:;;444JN]}JmHlHlHl8E3HlHlHlHlHlFb DXNpTuSuSuRt'O& ttskjifggIJKGHHEFFCCD@AA>??788L|JRbFWFWFWFW891FWFWFWFWFWDO BGPaScScRbRb(I# tttkjijjjNNOKLLIJJGGHDEEBCC;;;PVS{QzQzQzQz?N7QzQzQzQzQzOn Md__^^],S, uutkjimnnQRSOPPMNNJKLHIIFGG>??FNVVVVVBS8VVVVVRt Ulddccb.U. uuukjipqqUVWSTTQRRNOPLMMJKK9::8o7PfPfPfPfPf?C/EXEWEWEWDWBN W^at`t`s_s^r.O* vvukjitttYZ[WXXUVVRTTPQQLMM%&&>G^^^^^H[g݀nmmll3Z4 wvvkjiwxx]^_[\\YZZVWXTUU:;;!""@JaaaaaK^k߅rrqpp6]8 wwvkjiz{{abb_``\^^Z[\HII-..#$$6[6GUFUFTETES8n;***fplڂkڂjځiڀiڀ7W4xwwkji}~~efgcddaabSTT133/01%&&,--*+,)**'()&''$%%#$$ !"##122pxwvvudD'''%%%""# yyxkjimnn^``;=>789577455)**122/01.//,-.*,,)**')) $%&#$$!"# !!'()?@@899l͉rqppo?`A*++(()&&&$$$!""zyykji^_`>?@;<=9;;89:688+,,345133012.00-./+--*++!""'((%&'$%%"#$=>?CDD<==t{{zyyChM///,--***'((&&&#$$!!!zzykjimnn@BC?@A=??<=>:<<8:;-..577456244123/11.//,-.##$)*+())&'(123IJKGHH@@@u}|{{zFkR233001...,,,))*'''%%%!!! {{zkji`bbBDEACC?AB>@@<>?;<=/0089:688567355234022/00%&&,--*+,*+,JKKNNOKLLCDDpΑvuutsFfJ777455222000-..+++())$$$$%%""" ||{kjibddEGGCEFBDD@BC?AA=?@122:<<9:;799678466345133'((./0-..BCCTUUQRSOPPGHHx~~}|KoU:;;899667344112///---((((((&&&$$$!!!~~}kjidffGIJFHHDFGCEEACD@AB344=>?;==:;<8::789577455(**1229::YZZXYYUVWSTTKLLxޭ~}}|{MoW>??<==:::888566333111+,,,,,***'((%&&### !!kjifhhJLLHJKGIIEGHDFFBDE566?AA>?@<>>;<=9;;89:678*,,567YZZ^_`\]]YZ[WXXNOOU\SS[RQYOOWMMUKKSIAD@BCC@AA>>?;<<9::788555///000...+,,)))'''%%%kjihjjLNOKMMIKLHJJFHIEGG:;:<<8:;/11UWWdefbcc`aa]^_[\]TUUWXXTUVRSSPQQNNNKLLEFFFGGDEEBCC?@@=>>;;<999444445222000-..+++())kjijllOQRMOPLNNJLMHKKGIJEGHDFFBDEACC?AB>@@<>?;==RSTjklhijfggdeebcc_``]^^[[\XYYVWWTUUQRSOPPMNNKKLHIIFGGDDDABB??@===:;;899666344112///,--kjinppTVWRTUQSTOQRNPQLNOKMNJKLHJKGHIEGGDEFBDEUVVsttqrsoppmnnkllhijfghdeebcc``a]^_[\\YZZWXXUUVRSSPQQNOOLLMIJJGHHEEFCCC@AA>??<==:::788566kjiiih¿YXW }|{5UUSIKJIJJIHJJIHJIIGJIHGJHHGJHGFJHGFJGGEJGFEJFFEJFEDJFEDJEECJEDCJDDBJDCBJCCAJCBAJBA@JBA@JA@?JA@>J@?>J@?=J?>=J?>=J@>=J@?>J@?>JA@?JA@?JBA@JBB@JBB@JDCBJDCBJDCBJDCBJDCBJEDCJDCBJCBA3ZYX( @ 3FFFFFFFFFFFFFFFFFFFFFFFFFFFFFEcbaQ wvtSEkjjEJ h g g 0 Qbbb# ]b b c  b bbb bbb ^wvtS fis L    8   1    /    wvtS*h$))((''"i#r$$##K 71/wvtS/t//<.:-9'v('))6(5 .T)))> #),-5 ,+++3 **)%wvtS8t5BCWBVAU5:7>=R9N,C[(2,C,C,CC-:-D3I3I:2G1G0G/F wvtS9<67;248/15,*-'?AHT=K5CY/35C5C5C@5;8F>K=J1  wvtS=>>::;777344-..FMOf=VIG_G_G_GFTTjSiSi 8 wvtSOPPKLMHIIEEF>??PbS|S|S|/q9G_S|S|S|"Q'Uq`__#># wvtSUVVQRSNOOKKL@@@CNSpSpSp0k5APKfKeJe!F#[lb|a|`{%=$ wvtS[\\WXYTUUPQR-./I____6yCjonm+E, wvtSabb]^_Z[[CDE%&'ALOiOiNh0^6!!!j~mll/F.wvtSfhhcdeRSS123)**,-.*++'()%&& !!"122swvu4N7"""wvtSlnnZ[\89:466,--011-//+,-)**"##$%&"## !,--:::pܗtsr8O;(((%%%!!!wvtS[\]=??:<<89:/01355123/00,..%&&())&''%&&BCC?@@w{zy=VD...*++'''$$$wvtSBDE@BB>?@;=>234789567244012()*+--)*+;<=LMMEEFtݥxwvAWF444011---)**%%%###wvtSFHHDEFACD?AA577:<=8::678456+,-/01455TUURSSKKL{~}F_N:::677333/00***())%%%"""wvtSJLLGIJEGHCEE8:;>@@<=>:;<799.00456UVW[\]XYYPQQ`q`u]s[qDMF@@A<==999566000///+++((($$$wvtSMOPKMNIKKFHI@BBBDD?AB=??;==577STTeffabc^__XYYWXXTTUPQQMMNHHHFFGBCC??@;<<777455111-..***wvtSSUUPRSNPQLNOJLLHIJEGHCEEACCUVWpqqlnnijkfggcdd_``\]]XYZUVVRSSNOOKLLGHHDEEAAA=>>:;;677344wvtSsrpR{~}}}||{zzyxyxwwvuusrrqpponnmkljiihfgfdfechfejhgkjhomllkjrqpwvuyxwqqpvutyxwWVU(0 *5555555555555555555555rqptsrvuuxwwzzy||{}||{{z{{z{{z{{z{{z{{z{{z{{z{{z{{z{{z{{z{{z{{z}tsr./.  + * ###"#$$###=J9~xwuQ b  R  L g  A=~}|{,*),(*"T%(#&cS NiHD~795<4;)[#18.6 "i!)!)X#%$,&.S%-$,!"JqpFnD~BNAT?S1f/O>O*  ;;;~>??:;;5669T9Qg?X9H+~4?X?X"g)?PIaI`+ <<<~FGGBBC=>>@[APiGa@P1;GaGa'k/I\SlRk. >>=~NOOJKKEFFDcHT}T}Jg9KT}T}/y=Yxa`3 ???~VWWRSSMMN5T7WyWyMd$B(-a7,_56czfe#5" AAA~^__YZ[@BB.I2TsSrJ_lޅnm*;)CCB~fggQRS123)*++,,())%&& /00twv1D3!""EED~Z[[9;<678.///11,..)++#$$#$%*++>>?svt7H:))*%%% FFF~ABC=?@:<=244466133.00'(()**EFFEFFvxw=OB112,--(((""#III~EGHBDE?AB7899;;688355+--@AAUVVMNN{~|DVJ9::455000***'''"""NNN~JLMGIJDFG<=>>@@;==8::9:;_`a]^^UVVUYTQUPLQLDEDABB<==888222///***TTT~PSSMOPJLMGIJDFGBCDDEFhijjllfghbcc]^^YYZTUUPPQKLLFGGBBC=>>999455\\\~{[[YHHG%GFF%FFE%EED%EDC%DCB%CBA%BA@%A@?%@?>%?>=%=<;%<;:%<;9%<;:%=<;%>=<%??=%A@?%BA@%AA@%BA@%@@? AAAAAAAAAAAAAAAAAAAAAAAA(  """""""""""""""" """",;,;))),;,;,;,;,;,; =U=U011=U=U =U=U=U=U KLLEEF*++NpNpNpNpNpNp VWWFGG%&&dd'((###dd^__566+,-jۇo &''444-..jۇoCEE678234.//)*+%&&<==>??889tw%%%ACC=>?8::4560129::OPQIJJCCDy|/00)))###GIJCEF?AA;<=9;;^__Z[\TUUMNOyy:;;444-..]^_Z[\VXXSUUrstz{{uvvpqqkkkeff`aa[[[VVVPQQAAAAAAAAAAAAAAAAcutesdr-1.0.5/CuteSdr.pro.user0000644000175000017500000001741111561233432016202 0ustar bottomsbottoms ProjectExplorer.Project.ActiveTarget 0 ProjectExplorer.Project.EditorSettings System ProjectExplorer.Project.Target.0 Desktop Qt4ProjectManager.Target.DesktopTarget 1 0 qmake QtProjectManager.QMakeBuildStep Make Qt4ProjectManager.MakeStep false 2 Make Qt4ProjectManager.MakeStep true clean 1 false Debug Qt4ProjectManager.Qt4BuildConfiguration 2 /home/andrew/cutesdr/trunk 2 0 false qmake QtProjectManager.QMakeBuildStep Make Qt4ProjectManager.MakeStep false 2 Make Qt4ProjectManager.MakeStep true clean 1 false Release Qt4ProjectManager.Qt4BuildConfiguration 0 /home/andrew/cutesdr/trunk 2 0 false 2 CuteSdr Qt4ProjectManager.Qt4RunConfiguration 2 CuteSdr.pro false false false false 1 ProjectExplorer.Project.TargetCount 1 ProjectExplorer.Project.Updater.FileVersion 4 cutesdr-1.0.5/cutesdr.rc0000644000175000017500000000010111711303214015106 0ustar bottomsbottoms IDI_ICON1 ICON DISCARDABLE "cutesdr1.ico" cutesdr-1.0.5/interface/0000755000175000017500000000000011720506076015072 5ustar bottomsbottomscutesdr-1.0.5/interface/soundout.cpp0000644000175000017500000003756111711303214017457 0ustar bottomsbottoms///////////////////////////////////////////////////////////////////// // soundout.cpp: implementation of the CSoundOut class. // // This class implements a class to output data to a soundcard. // A fractional resampler is used to convert the users input rate to // the sound card rate and also perform frequency lock between the // two clock domains. // // History: // 2010-09-15 Initial creation MSW // 2011-03-27 Initial release // 2011-08-07 Changed some debug output ///////////////////////////////////////////////////////////////////// //========================================================================================== // + + + This Software is released under the "Simplified BSD License" + + + //Copyright 2010 Moe Wheatley. All rights reserved. // //Redistribution and use in source and binary forms, with or without modification, are //permitted provided that the following conditions are met: // // 1. Redistributions of source code must retain the above copyright notice, this list of // conditions and the following disclaimer. // // 2. Redistributions in binary form must reproduce the above copyright notice, this list // of conditions and the following disclaimer in the documentation and/or other materials // provided with the distribution. // //THIS SOFTWARE IS PROVIDED BY Moe Wheatley ``AS IS'' AND ANY EXPRESS OR IMPLIED //WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND //FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL Moe Wheatley OR //CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR //CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR //SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON //ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING //NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF //ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // //The views and conclusions contained in the software and documentation are those of the //authors and should not be interpreted as representing official policies, either expressed //or implied, of Moe Wheatley. //========================================================================================== #include "interface/soundout.h" #include "gui/testbench.h" #include "interface/sdrinterface.h" #include #include #define SOUNDCARD_RATE 48000 //output soundcard sample rate //#define SOUNDCARD_RATE 44100 #define FILTERQLEVEL_ALPHA 0.001 #define P_GAIN 2.38e-7 //Proportional gain #define TEST_ERROR 1.0 //#define TEST_ERROR 1.001 //use to force fixed sample rate error for testing //#define TEST_ERROR 0.999 ///////////////////////////////////////////////////////////////////// // constructor/destructor ///////////////////////////////////////////////////////////////////// CSoundOut::CSoundOut(QObject *parent) : QThread(parent) { m_pParent = parent; m_pAudioOutput = NULL; m_pOutput = NULL; m_ThreadQuit = true; m_UserDataRate = SOUNDCARD_RATE; m_OutRatio = 1.0; m_OutAudioFormat.setFrequency(SOUNDCARD_RATE); m_OutResampler.Init(8192); m_RateCorrection = 0.0; m_Gain = 1.0; m_Startup = true; m_BlockingMode = false; } CSoundOut::~CSoundOut() { Stop(); } ///////////////////////////////////////////////////////////////////// // Starts up soundcard output thread using soundcard at list OutDevIndx ///////////////////////////////////////////////////////////////////// bool CSoundOut::Start(int OutDevIndx, bool StereoOut, double UsrDataRate, bool BlockingMode) { QAudioDeviceInfo DeviceInfo; m_StereoOut = StereoOut; m_BlockingMode = BlockingMode; //Get required soundcard from list m_OutDevices = DeviceInfo.availableDevices(QAudio::AudioOutput); m_OutDeviceInfo = m_OutDevices.at(OutDevIndx); //Setup fixed format for sound ouput m_OutAudioFormat.setCodec("audio/pcm"); m_OutAudioFormat.setFrequency(SOUNDCARD_RATE); m_OutAudioFormat.setSampleSize(16); m_OutAudioFormat.setSampleType(QAudioFormat::SignedInt); m_OutAudioFormat.setByteOrder(QAudioFormat::LittleEndian); if(m_StereoOut) m_OutAudioFormat.setChannels(2); else m_OutAudioFormat.setChannels(1); m_pAudioOutput = new QAudioOutput(m_OutDeviceInfo, m_OutAudioFormat, this); if(!m_pAudioOutput) { qDebug()<<"Soundcard output error"; return false; } if(QAudio::NoError == m_pAudioOutput->error() ) { //initialize the data queue variables m_UserDataRate = 1; //force user data rate to be changed ChangeUserDataRate(UsrDataRate); m_pOutput = m_pAudioOutput->start(); //start QT AudioOutput //determine how long to sleep between low level reads based on samplerate and period size m_BlockTime = ( 250*m_pAudioOutput->periodSize() )/ ( SOUNDCARD_RATE*m_OutAudioFormat.channels() ); //qDebug()<<"periodSize "<periodSize(); //qDebug()<<"BlockTime "<stop(); wait(500); } if(NULL != m_pAudioOutput) { delete m_pAudioOutput; m_pAudioOutput = NULL; } } ///////////////////////////////////////////////////////////////////// // Sets/changes user data input rate ///////////////////////////////////////////////////////////////////// void CSoundOut::ChangeUserDataRate(double UsrDataRate) { if(m_UserDataRate != UsrDataRate) { m_UserDataRate = UsrDataRate; for(int i=0; iDisplayData(numsamples, RData, SOUNDCARD_RATE, PROFILE_5); if(m_BlockingMode) //if in Blocking Mode then wait for soundcard queue to be available { for( i=0; iSendDebugTxt("Snd Overflow"); m_AveOutQLevel = m_OutQLevel; } //calculate average Queue fill level m_AveOutQLevel = (1.0-FILTERQLEVEL_ALPHA)*m_AveOutQLevel + FILTERQLEVEL_ALPHA*(double)m_OutQLevel; m_Mutex.unlock(); } } //////////////////////////////////////////////////////////////// //Called by application to put REAL soundcard output samples //into MONO soundcard queue //////////////////////////////////////////////////////////////// void CSoundOut::PutOutQueue(int numsamples, TYPEREAL* pData ) { TYPEMONO16 RData[OUTQSIZE]; //buffer to hold resampled data int i; bool overflow = false; if(( 0==numsamples) || m_ThreadQuit) return; //Call Resampler to match sample rates between radio and sound card numsamples = m_OutResampler.Resample(numsamples, TEST_ERROR*m_OutRatio *(1.0+m_RateCorrection), pData, RData, m_Gain); g_pTestBench->DisplayData(numsamples, RData, SOUNDCARD_RATE, PROFILE_5); if(m_BlockingMode) //if in Blocking Mode then wait for soundcard queue to be available { for( i=0; iSendDebugTxt("Snd Overflow"); m_AveOutQLevel = m_OutQLevel; } //calculate average Queue fill level m_AveOutQLevel = (1.0-FILTERQLEVEL_ALPHA)*m_AveOutQLevel + FILTERQLEVEL_ALPHA*(double)m_OutQLevel; m_Mutex.unlock(); } } //////////////////////////////////////////////////////////////// //Called by CSoundOut worker thread to get new samples from queue // This routine is called from a worker thread so must be careful. // MONO version //////////////////////////////////////////////////////////////// void CSoundOut::GetOutQueue(int numsamples, TYPEMONO16* pData ) { int i; bool underflow = false; m_Mutex.lock(); if(m_Startup) { //if no data in queue yet just stuff in silence until something is put in queue for( i=0; iOUTQSIZE/2) { m_Startup = false; m_RateUpdateCount = -5*SOUNDCARD_RATE; //delay first error update to let settle m_PpmError = 0; m_AveOutQLevel = m_OutQLevel; m_UpdateToggle = true; } else { m_Mutex.unlock(); return; } } for( i=0; iSendDebugTxt("Snd Underflow"); m_AveOutQLevel = m_OutQLevel; } // See if time to update rate error calculation routine m_RateUpdateCount += numsamples; if(m_RateUpdateCount >= SOUNDCARD_RATE) //every second { CalcError(); m_RateUpdateCount = 0; } m_Mutex.unlock(); } //////////////////////////////////////////////////////////////// //Called by CSoundOut worker thread to get new samples from queue // This routine is called from a worker thread so must be careful. // STEREO version //////////////////////////////////////////////////////////////// void CSoundOut::GetOutQueue(int numsamples, TYPESTEREO16* pData ) { int i; bool underflow = false; m_Mutex.lock(); if(m_Startup) { //if no data in queue yet just stuff in silence until something is put in queue for( i=0; iOUTQSIZE/2) { m_Startup = false; m_RateUpdateCount = -5*SOUNDCARD_RATE; //delay first error update to let settle m_PpmError = 0; m_AveOutQLevel = m_OutQLevel; m_UpdateToggle = true; } else { m_Mutex.unlock(); return; } } for( i=0; iSendDebugTxt("Snd Underflow"); m_AveOutQLevel = m_OutQLevel; } // See if time to update rate error calculation routine m_RateUpdateCount += numsamples; if(m_RateUpdateCount >= SOUNDCARD_RATE) //every second { CalcError(); m_RateUpdateCount = 0; } m_Mutex.unlock(); } //////////////////////////////////////////////////////////////// // Called alternately from the Get routines to update the // error correction process //////////////////////////////////////////////////////////////// void CSoundOut::CalcError() { double error; error = (double)(m_AveOutQLevel - OUTQSIZE/2 ); //neg==level is too low pos == level is to high error = error * P_GAIN; m_RateCorrection = error; m_PpmError = (int)( m_RateCorrection*1e6 ); if( abs(m_PpmError) > 500) { // qDebug()<<"SoundOut "<SendDebugTxt("Snd error>500ppm"); } } ////////////////////////////////////////////////////////////////////////// // Worker thread polls QAudioOutput device to see if there is room // to put more data into it and then calls GetOutQueue(..) to get more data. // This thread was needed because the normal "pull" mechanism of Qt does // not work very well since it depends on the main process signal-slot event // queue and you get dropouts if the GUI gets busy. ////////////////////////////////////////////////////////////////////////// void CSoundOut::run() { while(!m_ThreadQuit ) //execute loop until quit flag set { if( (QAudio::IdleState == m_pAudioOutput->state() ) || (QAudio::ActiveState == m_pAudioOutput->state() ) ) { //Process sound data while soundcard is active and no errors unsigned int len = m_pAudioOutput->bytesFree(); //in bytes if( len>0 ) { //limit size to SOUND_WRITEBUFSIZE if(len > SOUND_WRITEBUFSIZE) len = SOUND_WRITEBUFSIZE; if(m_StereoOut) { len &= ~(0x03); //keep on 4 byte chunks GetOutQueue( len/4, (TYPESTEREO16*)m_pData ); } else { len &= ~(0x01); //keep on 2 byte chunks GetOutQueue( len/2, (TYPEMONO16*)m_pData ); } m_pOutput->write((char*)m_pData,len); } else //no room in sound card output buffer so wait { //not good but no other wait or blocking mechanism available msleep(m_BlockTime); } } else { //bail out if error occurs qDebug()<<"SoundOut Error"; if(m_pParent) ((CSdrInterface*)m_pParent)->SendIOStatus(CSdrInterface::ERROR); m_ThreadQuit = true; } } qDebug()<<"sound thread exit"; } cutesdr-1.0.5/interface/perform.h0000644000175000017500000000127011546075374016725 0ustar bottomsbottoms/////////////////////////////////////////////////////// // Perform.h : Global interface for the performance functions // History: // 2010-11-10 Initial creation MSW // 2011-03-27 Initial release ////////////////////////////////////////////////////////////////////// // #if !defined(_INCLUDE_PERFORMXXX_H_) #define _INCLUDE_PERFORMXXX_H_ #include "math.h" ///////////////////////////////////////////////////////////////////////////// extern void InitPerformance(); extern void StartPerformance(); extern void StopPerformance(int n); extern void ReadPerformance(); extern void SamplePerformance(); extern int GetDeltaPerformance(); #endif //#if !defined(_INCLUDE_PERFORMXXX_H_) cutesdr-1.0.5/interface/soundout.h0000644000175000017500000000403411546075374017134 0ustar bottomsbottoms////////////////////////////////////////////////////////////////////// // soundout.h: interface for the CSoundOut class. // // History: // 2010-09-15 Initial creation MSW // 2011-03-27 Initial release ///////////////////////////////////////////////////////////////////// #ifndef SOUNDOUT_H #define SOUNDOUT_H #include #include #include #include #include #include "dsp/fractresampler.h" #define OUTQSIZE 16384 //max samples (keep power of 2 for ptr wrap around) #define SOUND_WRITEBUFSIZE 8192 class CSoundOut : public QThread { Q_OBJECT public: explicit CSoundOut(QObject *parent = 0); virtual ~CSoundOut(); //Exposed functions bool Start(int OutDevIndx, bool StereoOut, double UsrDataRate, bool BlockingMode); //starts soundcard output void Stop(); //stops soundcard output void PutOutQueue(int numsamples, TYPEREAL* pData ); void PutOutQueue(int numsamples, TYPECPX* pData ); void ChangeUserDataRate(double UsrDataRate); void SetVolume(qint32 vol); int GetRateError(){return (int)m_PpmError;} protected: void run(); //implements worker thread loop int m_BlockTime; private: void GetOutQueue(int numsamples, TYPEMONO16* pData ); void GetOutQueue(int numsamples, TYPESTEREO16* pData ); void CalcError(); QList m_OutDevices; QAudioDeviceInfo m_OutDeviceInfo; QAudioFormat m_OutAudioFormat; QAudioOutput* m_pAudioOutput; QIODevice* m_pOutput; // ptr to internal soundout IODevice QObject* m_pParent; QMutex m_Mutex; CFractResampler m_OutResampler; TYPEMONO16 m_OutQueueMono[OUTQSIZE]; TYPESTEREO16 m_OutQueueStereo[OUTQSIZE]; bool m_BlockingMode; bool m_ThreadQuit; bool m_Startup; bool m_StereoOut; bool m_UpdateToggle; quint32 m_OutQHead; quint32 m_OutQTail; char m_pData[SOUND_WRITEBUFSIZE]; int m_RateUpdateCount; int m_OutQLevel; int m_PpmError; double m_Gain; double m_UserDataRate; double m_OutRatio; double m_RateCorrection; double m_AveOutQLevel; }; #endif // SOUNDOUT_H cutesdr-1.0.5/interface/netiobase.cpp0000644000175000017500000004717411711303214017551 0ustar bottomsbottoms////////////////////////////////////////////////////////////////////// // netiobase.cpp: implementation of the CNetIOBase class. // // Network I/O module base class( Only used to derive one's own class) // // History: // 2010-09-15 Initial creation MSW // 2011-03-27 Initial release // 2011-04-26 Added types.h include(Thanks Ben AA7AS) // 2011-08-07 Fixed some issues with the UDP thread mutex //////////////////////////////////////////////////////////////////////////////////////////// //========================================================================================== // + + + This Software is released under the "Simplified BSD License" + + + //Copyright 2010 Moe Wheatley. All rights reserved. // //Redistribution and use in source and binary forms, with or without modification, are //permitted provided that the following conditions are met: // // 1. Redistributions of source code must retain the above copyright notice, this list of // conditions and the following disclaimer. // // 2. Redistributions in binary form must reproduce the above copyright notice, this list // of conditions and the following disclaimer in the documentation and/or other materials // provided with the distribution. // //THIS SOFTWARE IS PROVIDED BY Moe Wheatley ``AS IS'' AND ANY EXPRESS OR IMPLIED //WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND //FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL Moe Wheatley OR //CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR //CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR //SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON //ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING //NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF //ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // //The views and conclusions contained in the software and documentation are those of the //authors and should not be interpreted as representing official policies, either expressed //or implied, of Moe Wheatley. //========================================================================================== /*---------------------------------------------------------------------------*/ /*------------------------> I N C L U D E S <--------------------------------*/ /*---------------------------------------------------------------------------*/ #include "interface/netiobase.h" #include #ifdef Q_WS_WIN #include "winsock2.h" #else #include #include #include #endif /*---------------------------------------------------------------------------*/ /*--------------------> L O C A L D E F I N E S <--------------------------*/ /*---------------------------------------------------------------------------*/ #define PKT_LENGTH_24 1444 #define PKT_LENGTH_16 1028 #define RXQUEUE_SIZE 1024 //queue size(keep power of 2) /*---------------------------------------------------------------------------*/ /*----------------> L O C A L S T R U C T U R E S <-----------------------*/ /*---------------------------------------------------------------------------*/ typedef union { struct bs { unsigned char b0; unsigned char b1; unsigned char b2; unsigned char b3; }bytes; int all; }tBtoL; typedef union { struct bs { unsigned char b0; unsigned char b1; }bytes; signed short sall; unsigned short all; }tBtoS; //&+&+&+&+&+&+&+&+&+&+&+&+&+&+&+&+&+&+&+&+&+&+&+&+&+&+&+&+&+&+&+&+&+&+&+&+ // NetIOBase class implementation. //&+&+&+&+&+&+&+&+&+&+&+&+&+&+&+&+&+&+&+&+&+&+&+&+&+&+&+&+&+&+&+&+&+&+&+&+ CNetIOBase::CNetIOBase() { m_pUdpRxQueue = NULL; m_pTcpThread = NULL; m_pIQDataThread = NULL; m_RxQueueHead = 0; m_RxQueueTail = 0; m_TcpThreadQuit = FALSE; m_IQDataThreadQuit = FALSE; m_UdpThreadQuit = FALSE; m_SampleSize24 = FALSE; } CNetIOBase::~CNetIOBase() { StopIO(); } /*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/ /* ------------------------- */ /* | S e t u p N e t w o r k | */ /* ------------------------- */ /* Set client network parameters (call before starting IO) */ /*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/ void CNetIOBase::SetupNetwork(QHostAddress ip4Addr, quint16 port) { m_IPAdr = ip4Addr; m_Port = port; qDebug()<start(); m_IQDataThreadQuit = FALSE; if(NULL != m_pIQDataThread) { delete m_pIQDataThread; m_pIQDataThread = NULL; } m_pIQDataThread = new CIQDataThread(this); m_pIQDataThread->start(QThread::TimeCriticalPriority); //this thread does all the DSP processing m_MissedPackets = 0; } /*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/ /* ------------- */ /* | S t o p I O | */ /* ------------- */ /* Stops IO threads */ /*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/ void CNetIOBase::StopIO() { m_TcpThreadQuit = TRUE; if(NULL != m_pTcpThread) { m_pTcpThread->wait(5000); //wait for tcp thread to exit then destroy delete m_pTcpThread; m_pTcpThread = NULL; qDebug()<<"tcpthread stopped"; } m_IQDataThreadQuit = TRUE; if(NULL != m_pIQDataThread) { m_RxQueueHead = 0; m_RxQueueTail = 0; m_pIQDataThread->wait(1000); //wait for Fifo thread to exit then destroy delete m_pIQDataThread; m_pIQDataThread = NULL; qDebug()<<"fifothread stopped"; } if(NULL != m_pUdpRxQueue) //delete FIFO memory { for(int i=0; im_TcpState == CTcpThread::TCP_CONNECTED) { //qDebug()<<"send"; qint64 txbytes = pMsg->Buf16[0] & LENGTH_MASK; m_TcpMutex.lock(); qint64 sent = m_pTcpThread->m_pTcpSocket->write((const char*)pMsg->Buf8, txbytes); m_TcpMutex.unlock(); if(txbytes!=sent) qDebug()<<"tcp write error"; } } //&+&+&+&+&+&+&+&+&+&+&+&+&+&+&+&+&+&+&+&+&+&+&+&+&+&+&+&+&+&+&+&+&+&+&+&+ // TCP thread class implementation. //&+&+&+&+&+&+&+&+&+&+&+&+&+&+&+&+&+&+&+&+&+&+&+&+&+&+&+&+&+&+&+&+&+&+&+&+ CTcpThread::CTcpThread(QObject* pParent) : m_pParent(pParent) { m_pTcpSocket = NULL; m_pUdpThread = NULL; m_TcpState = TCP_NOT_CONNECTED; m_RxMsgLength = 0; m_RxMsgIndex = 0; m_MsgState = MSGSTATE_HDR1; } ////////////////////////////////////////////////////////////////////////// //TCP Client operation thread. manages connection state and Rx msgs ////////////////////////////////////////////////////////////////////////// void CTcpThread::run() { CNetIOBase* pParent = (CNetIOBase*)m_pParent; m_pTcpSocket = new QTcpSocket; m_TcpState = TCP_NOT_CONNECTED; while(!pParent->m_TcpThreadQuit) { ManageTcpClientConnection(); } delete m_pTcpSocket; m_pTcpSocket = NULL; StopUdp(); } void CTcpThread::StartUdp() { CNetIOBase* pParent = (CNetIOBase*)m_pParent; pParent->m_UdpThreadQuit = FALSE; if(NULL != m_pUdpThread) { qDebug()<<"udpthread already running"; delete m_pUdpThread; m_pUdpThread = NULL; } m_pUdpThread = new CUdpThread(m_pParent); // m_pUdpThread->start(QThread::TimeCriticalPriority); m_pUdpThread->start(QThread::HighestPriority); } void CTcpThread::StopUdp() { CNetIOBase* pParent = (CNetIOBase*)m_pParent; pParent->m_UdpThreadQuit = TRUE; if(NULL != m_pUdpThread) { m_pUdpThread->wait(1000); //wait for udp thread to exit then destroy delete m_pUdpThread; m_pUdpThread = NULL; qDebug()<<"udpthread stopped"; } } /*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/ /* --------------------------------------------------- */ /* | M a n a g e T c p C l i e n t C o n n e c t i o n | */ /* --------------------------------------------------- */ /* Helper function manages TCP client connect logic */ /*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/ void CTcpThread::ManageTcpClientConnection() { int Timeout; char Buf[2048]; qint64 bytesavailable; CNetIOBase* pParent = (CNetIOBase*)m_pParent; switch(m_TcpState) { case TCP_NOT_CONNECTED: qDebug()<<"Try to connect"; m_pTcpSocket->connectToHost(pParent->m_IPAdr, pParent->m_Port); if (m_pTcpSocket->waitForConnected(500)) { qDebug()<<"Connected"; StartUdp(); m_TcpState = TCP_CONNECTED; m_RxMsgLength = 0; m_RxMsgIndex = 0; m_MsgState = MSGSTATE_HDR1; pParent->SendIOStatus(CNetIOBase::CONNECTED); } else { //wait 2 seconds before trying again yet break out sooner if thread is stopped pParent->SendIOStatus(CNetIOBase::NOT_CONNECTED); Timeout = 2*10; while( (!pParent->m_TcpThreadQuit) && Timeout--) msleep(100); } break; case TCP_CONNECTED: if(m_pTcpSocket->waitForReadyRead(100) ) { pParent->m_TcpMutex.lock(); bytesavailable = m_pTcpSocket->bytesAvailable(); pParent->m_TcpMutex.unlock(); if(bytesavailable) { pParent->m_TcpMutex.lock(); m_pTcpSocket->read(Buf, bytesavailable); pParent->m_TcpMutex.unlock(); AssembleAscpMsg(Buf, (int)bytesavailable); } } if(m_pTcpSocket->state() != QAbstractSocket::ConnectedState) { m_pTcpSocket->close(); qDebug()<<"connect error"; pParent->SendIOStatus(CNetIOBase::NOT_CONNECTED); m_TcpState = TCP_NOT_CONNECTED; StopUdp(); } break; case TCP_DISCONNECT: msleep(100); //wait for any pending msgs to get sent if(m_pTcpSocket->state() == QAbstractSocket::ConnectedState) { m_pTcpSocket->disconnectFromHost(); } m_pTcpSocket->close(); qDebug()<<"Not Connected"; pParent->SendIOStatus(CNetIOBase::NOT_CONNECTED); m_TcpState = TCP_NOT_CONNECTED; StopUdp(); break; default: break; } if(pParent->m_TcpThreadQuit && (m_pTcpSocket->state() != QAbstractSocket::UnconnectedState) ) { //if thread stopped and is connected then disconnect TCP socket msleep(100); //wait for any pending msgs to get sent m_pTcpSocket->disconnectFromHost(); m_pTcpSocket->waitForDisconnected(5000); m_pTcpSocket->close(); qDebug()<<"Not Connected"; pParent->SendIOStatus(CNetIOBase::NOT_CONNECTED); } } /*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/ /* ------------------------------- */ /* | A s s e m b l e A s c p M s g | */ /* ------------------------------- */ /* Helper function to assemble TCP data stream into ASCP formatted messages */ /*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/ void CTcpThread::AssembleAscpMsg(char* pBuf, int length) { CNetIOBase* pParent = (CNetIOBase*)m_pParent; for( int i=0; iParseAscpMsg( &m_RxAscpMsg ); } else //there are data bytes to fetch { if( 0 == m_RxMsgLength) m_RxMsgLength = 8192+2; //handle special case of 8192 byte data message m_MsgState = MSGSTATE_DATA; //go to data byte reading state } break; case MSGSTATE_DATA: //try to read the rest of the message m_RxAscpMsg.Buf8[m_RxMsgIndex++] = data; if( m_RxMsgIndex >= m_RxMsgLength ) { m_MsgState = MSGSTATE_HDR1; //go back to first stage m_RxMsgIndex = 0; pParent->ParseAscpMsg( &m_RxAscpMsg ); } break; } //end switch statement } } //&+&+&+&+&+&+&+&+&+&+&+&+&+&+&+&+&+&+&+&+&+&+&+&+&+&+&+&+&+&+&+&+&+&+&+&+ // UDP thread class implementation. //&+&+&+&+&+&+&+&+&+&+&+&+&+&+&+&+&+&+&+&+&+&+&+&+&+&+&+&+&+&+&+&+&+&+&+&+ CUdpThread::CUdpThread(QObject* pParent) : m_pParent(pParent) { m_pUdpSocket = NULL; m_LastSeqNum = 0; } ////////////////////////////////////////////////////////////////////////// // UDP thread waits for UDP Rx data ////////////////////////////////////////////////////////////////////////// void CUdpThread::run() { CNetIOBase* pParent = (CNetIOBase*)m_pParent; m_pUdpSocket = new QUdpSocket; if(!m_pUdpSocket->bind(pParent->m_Port,QUdpSocket::ShareAddress) ) { qDebug()<<"UDP Bind Failed"; } int v = 2000000; ::setsockopt(m_pUdpSocket->socketDescriptor(), SOL_SOCKET, SO_RCVBUF, (char *)&v, sizeof(v)); while(!pParent->m_UdpThreadQuit) { if(m_pUdpSocket->waitForReadyRead(100) ) { OnreadyRead(); } } m_pUdpSocket->close(); delete m_pUdpSocket; m_pUdpSocket = NULL; } ////////////////////////////////////////////////////////////////////////// // Called when UDP Rx data is available then converts to float and puts in FIFO ////////////////////////////////////////////////////////////////////////// void CUdpThread::OnreadyRead() { char Buf[8192]; tBtoL data; qint64 size; int i,j; tBtoS seq; CNetIOBase* pParent = (CNetIOBase*)m_pParent; //Q_ASSERT(QThread::currentThread() == m_pUdpSocket->thread()); //just see if really is a direct call by the socket thread data.all = 0; seq.all = 0; pParent->m_UdpRxMutex.lock(); while( m_pUdpSocket->hasPendingDatagrams() && !pParent->m_UdpThreadQuit) { size = m_pUdpSocket->pendingDatagramSize(); if(PKT_LENGTH_24 == size) { //24 bit I/Q data pParent->m_SampleSize24 = TRUE; m_pUdpSocket->readDatagram( Buf, 8192, 0, 0 ); seq.bytes.b0 = Buf[2]; seq.bytes.b1 = Buf[3]; if(0==seq.all) //is first packet after started m_LastSeqNum = 0; if(seq.all != m_LastSeqNum) { //qDebug()<m_MissedPackets += ((qint16)seq.all - (qint16)m_LastSeqNum); m_LastSeqNum = seq.all; } m_LastSeqNum++; if(0==m_LastSeqNum) m_LastSeqNum = 1; for( i=4,j=0; im_pUdpRxQueue[pParent->m_RxQueueHead][j] = (double)data.all/65536.0; //scale to be +-32768 range same as 16 bit data } } else if(PKT_LENGTH_16 == size) { //16 bit I/Q data pParent->m_SampleSize24 = FALSE; m_pUdpSocket->readDatagram( Buf, 2048, 0, 0 ); seq.bytes.b0 = Buf[2]; seq.bytes.b1 = Buf[3]; if(0==seq.all) //is first packet after started m_LastSeqNum = 0; if(seq.all != m_LastSeqNum) { //qDebug()<m_MissedPackets += ((qint16)seq.all - (qint16)m_LastSeqNum); m_LastSeqNum = seq.all; } m_LastSeqNum++; if(0==m_LastSeqNum) m_LastSeqNum = 1; for( i=4,j=0; im_pUdpRxQueue[pParent->m_RxQueueHead][j] = (double)seq.sall; } } pParent->m_RxQueueHead++; pParent->m_RxQueueHead &= (RXQUEUE_SIZE-1); //if(pParent->m_RxQueueHead==pParent->m_RxQueueTail) // qDebug()<<"Overflo"; } pParent->m_QWaitFifoData.wakeAll(); pParent->m_UdpRxMutex.unlock(); } #define FILE_NAME "SSB-7210000Hz_001.wav" //&+&+&+&+&+&+&+&+&+&+&+&+&+&+&+&+&+&+&+&+&+&+&+&+&+&+&+&+&+&+&+&+&+&+&+&+ // CIQDataThread class implementation. //&+&+&+&+&+&+&+&+&+&+&+&+&+&+&+&+&+&+&+&+&+&+&+&+&+&+&+&+&+&+&+&+&+&+&+&+ CIQDataThread::CIQDataThread(QObject* pParent) : m_pParent(pParent) { #if 0 QDir::setCurrent("d:/"); m_File.setFileName(FILE_NAME); if(m_File.open(QIODevice::ReadOnly)) { qDebug()<<"file Opend OK"; m_File.seek(0x7e); //SV } else qDebug()<<"file Failed to Open"; #endif } CIQDataThread::~CIQDataThread() { #if 0 if(m_File.isOpen()) { m_File.close(); qDebug()<<"file closed"; } #endif } ////////////////////////////////////////////////////////////////////////// // IQData thread waits for UDP Rx data available in Queue // then sends it to parent via virtual function call ////////////////////////////////////////////////////////////////////////// void CIQDataThread::run() { CNetIOBase* pParent; pParent = (CNetIOBase*)m_pParent; while(!pParent->m_IQDataThreadQuit) { pParent->m_UdpRxMutex.lock(); pParent->m_QWaitFifoData.wait( &pParent->m_UdpRxMutex, 100); pParent->m_UdpRxMutex.unlock(); if(pParent->m_SampleSize24) { while(pParent->m_RxQueueHead != pParent->m_RxQueueTail) { pParent->ProcessIQData( &pParent->m_pUdpRxQueue[pParent->m_RxQueueTail][0], (PKT_LENGTH_24-4)/3 ); pParent->m_RxQueueTail++; pParent->m_RxQueueTail &= (RXQUEUE_SIZE-1); } //FileTest(); } else { while(pParent->m_RxQueueHead != pParent->m_RxQueueTail) { pParent->ProcessIQData( &pParent->m_pUdpRxQueue[pParent->m_RxQueueTail][0], (PKT_LENGTH_16-4)/2 ); pParent->m_RxQueueTail++; pParent->m_RxQueueTail &= (RXQUEUE_SIZE-1); } } // pParent->m_UdpRxMutex.unlock(); } } void CIQDataThread::FileTest() { tBtoL4 tmp; char buf[16384]; double fBuf[1024]; CNetIOBase* pParent; pParent = (CNetIOBase*)m_pParent; while(!m_File.atEnd()) { if(m_File.read(buf,3*1024)<=0) return; int j=0; for(int i=0; i<1024; i++) { tmp.bytes.b0 = 0; tmp.bytes.b1 = buf[j++]; tmp.bytes.b2 = buf[j++]; tmp.bytes.b3 = buf[j++]; fBuf[i] = (double)(tmp.all/65536); } pParent->ProcessIQData( fBuf, 1024 ); } } cutesdr-1.0.5/interface/sdrinterface.h0000644000175000017500000001357411711303214017713 0ustar bottomsbottoms////////////////////////////////////////////////////////////////////// // sdrinterface.h: interface for the CSdrInterface class. // // History: // 2010-09-15 Initial creation MSW // 2011-03-27 Initial release // 2011-04-16 Added Frequency range logic for optional down converter modules // 2011-08-07 Added WFM Support ///////////////////////////////////////////////////////////////////// #ifndef SDRINTERFACE_H #define SDRINTERFACE_H #include "interface/netiobase.h" #include "dsp/fft.h" #include "interface/ad6620.h" #include "dsp/demodulator.h" #include "dsp/noiseproc.h" #include "interface/soundout.h" #include "interface/protocoldefs.h" ///////////////////////////////////////////////////////////////////// // Derived class from NetIOBase for all the custom SDR msg processing ///////////////////////////////////////////////////////////////////// class CSdrInterface : public CNetIOBase { Q_OBJECT public: CSdrInterface(); ~CSdrInterface(); enum eStatus { NOT_CONNECTED, CONNECTED, RUNNING, ADOVR, ERROR }; enum eRadioType { SDR14, SDRIQ, SDRIP, NETSDR }; //NCO spur management commands for ManageNCOSpurOffsets(...) enum eNCOSPURCMD { NCOSPUR_CMD_SET, NCOSPUR_CMD_STARTCAL, NCOSPUR_CMD_READ }; void StartIO(); //starts IO threads void StopIO(); //stops IO threads // virtual function called by worker thread with complete ascp rx msg to decode virtual void SendIOStatus(int iostatus); //called by TCP thread with new msg from radio to parse void ParseAscpMsg(CAscpMsg *pMsg); //called by IQData thread with new I/Q data to process virtual void ProcessIQData( double* pIQData, int Length); void StartSdr(); void StopSdr(); bool GetScreenIntegerFFTData(qint32 MaxHeight, qint32 MaxWidth, double MaxdB, double MindB, qint32 StartFreq, qint32 StopFreq, qint32* OutBuf ); void ScreenUpdateDone(){m_ScreenUpateFinished = TRUE;} void KeepAlive(); void ManageNCOSpurOffsets( eNCOSPURCMD cmd, double* pNCONullValueI, double* pNCONullValueQ); void SetRx2Parameters(double Rx2Gain, double Rx2Phase); //calls to these functions promt a signal in response void GetSdrInfo(); void ReqStatus(); quint64 SetRxFreq(quint64 freq); //bunch of public members containing sdr related information and data QString m_DeviceName; QString m_SerialNum; float m_BootRev; float m_AppRev; //GUI Public settings and access functions void SetRadioType(qint32 radiotype ){m_RadioType = (eRadioType)radiotype;} qint32 GetRadioType(){return (qint32)m_RadioType;} void SetSoundCardSelection(qint32 SoundInIndex, qint32 SoundOutIndex,bool StereoOut) { m_pSoundCardOut->Stop(); m_SoundInIndex = SoundInIndex; m_SoundOutIndex = SoundOutIndex; m_StereoOut = StereoOut;} int GetRateError(){return m_pSoundCardOut->GetRateError();} void SetVolume(qint32 vol){ m_pSoundCardOut->SetVolume(vol); } void SetChannelMode(qint32 channelmode); void SetSdrRfGain(qint32 gain); qint32 GetSdrRfGain(){return m_RfGain;} void SetSdrBandwidthIndex(qint32 bwindex); double GetSdrSampleRate(){return m_SampleRate;} quint32 GetSdrMaxBandwidth(){return m_SampleRate;} void SetFftSize(qint32 size); quint32 GetSdrFftSize(){return m_FftSize;} void SetFftAve(qint32 ave); quint32 GetSdrFftAve(){return m_FftAve;} qint32 GetMaxBWFromIndex(qint32 index); double GetSampleRateFromIndex(qint32 index); void SetMaxDisplayRate(int updatespersec){m_MaxDisplayRate = updatespersec; m_DisplaySkipValue = m_SampleRate/(m_FftSize*m_MaxDisplayRate); m_DisplaySkipCounter = 0; } void SetDemod(int Mode, tDemodInfo CurrentDemodInfo); void SetDemodFreq(qint64 Freq){m_Demodulator.SetDemodFreq((TYPEREAL)Freq);} void SetupNoiseProc(tNoiseProcdInfo* pNoiseProcSettings); double GetSMeterPeak(){return m_Demodulator.GetSMeterPeak() + m_GainCalibrationOffset - m_RfGain;} double GetSMeterAve(){return m_Demodulator.GetSMeterAve() + m_GainCalibrationOffset - m_RfGain;} void SetSpectrumInversion(bool Invert){m_InvertSpectrum = Invert;} void SetUSFmVersion(bool USFm){m_USFm = USFm;} bool GetUSFmVersion(){return m_USFm;} //access to WFM mode status/data int GetStereoLock(int* pPilotLock){ return m_Demodulator.GetStereoLock(pPilotLock);} int GetNextRdsGroupData(tRDS_GROUPS* pGroupData){return m_Demodulator.GetNextRdsGroupData(pGroupData);} signals: void NewStatus(int status); //emitted when sdr status changes void NewInfoData(); //emitted when sdr information is received after GetSdrInfo() void NewFftData(); //emitted when new FFT data is available void FreqChange(int freq); //emitted if requested frequency has been clamped by radio private: void SendAck(quint8 chan); void Start6620Download(); void NcoSpurCalibrate(double* pData, qint32 length); bool m_Running; bool m_ScreenUpateFinished; bool m_StereoOut; bool m_InvertSpectrum; bool m_USFm; qint32 m_BandwidthIndex; qint32 m_DisplaySkipCounter; qint32 m_DisplaySkipValue; qint32 m_FftSize; qint32 m_FftAve; qint32 m_FftBufPos; qint32 m_KeepAliveCounter; qint32 m_MaxBandwidth; qint32 m_MaxDisplayRate; qint32 m_RfGain; qint32 m_SoundInIndex; qint32 m_SoundOutIndex; qint32 m_ChannelMode; quint8 m_Channel; quint64 m_CurrentFrequency; quint64 m_BaseFrequencyRangeMin; quint64 m_BaseFrequencyRangeMax; quint64 m_OptionFrequencyRangeMin; quint64 m_OptionFrequencyRangeMax; double m_SampleRate; double m_GainCalibrationOffset; double m_DataBuf[MAX_FFT_SIZE*2]; CAscpMsg m_TxMsg; Cad6620 m_AD6620; eRadioType m_RadioType; eStatus m_Status; bool m_NcoSpurCalActive; //NCO spur reduction variables qint32 m_NcoSpurCalCount; double m_NCOSpurOffsetI; double m_NCOSpurOffsetQ; CFft m_Fft; CDemodulator m_Demodulator; CNoiseProc m_NoiseProc; CSoundOut* m_pSoundCardOut; CIir m_Iir; }; #endif // SDRINTERFACE_H cutesdr-1.0.5/interface/ad6620.cpp0000644000175000017500000006631111711303214016474 0ustar bottomsbottoms//////////////////////////////////////////////////////////////////////// // ad6620.cpp: implementation of the Cad6620 class. // // Creates the messages required by the sdriq and sdr14 to load the // internal AD6620 digital downconverter. // // History: // 2010-09-15 Initial creation MSW // 2011-03-27 Initial release // 2011-06-16 Fixed bug in AD6620 gain scaling //////////////////////////////////////////////////////////////////////// //========================================================================================== // + + + This Software is released under the "Simplified BSD License" + + + //Copyright 2010 Moe Wheatley. All rights reserved. // //Redistribution and use in source and binary forms, with or without modification, are //permitted provided that the following conditions are met: // // 1. Redistributions of source code must retain the above copyright notice, this list of // conditions and the following disclaimer. // // 2. Redistributions in binary form must reproduce the above copyright notice, this list // of conditions and the following disclaimer in the documentation and/or other materials // provided with the distribution. // //THIS SOFTWARE IS PROVIDED BY Moe Wheatley ``AS IS'' AND ANY EXPRESS OR IMPLIED //WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND //FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL Moe Wheatley OR //CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR //CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR //SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON //ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING //NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF //ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // //The views and conclusions contained in the software and documentation are those of the //authors and should not be interpreted as representing official policies, either expressed //or implied, of Moe Wheatley. //========================================================================================== #include "ad6620.h" #include "protocoldefs.h" #include // Defines for all the AD6620 registers #define ADR_MODECTRL 0x300 //8 bits #define MODECTRL_RESET (1<<0) //pwrs up in soft reset with this bit set #define MODECTRL_SREAL (0<<1) #define MODECTRL_DREAL (1<<1) #define MODECTRL_SCOMPLEX (1<<2) #define MODECTRL_SYNCMASTER (1<<3) #define ADR_NCOCTRL 0x301 //8 bits #define NCOCTRL_BYPASS (1<<0) #define NCOCTRL_PHZDITHER (1<<1) #define NCOCTRL_AMPDITHER (1<<2) #define ADR_NCOSYNCMASK 0x302 //32 bits (write all 1's) #define ADR_NCOFREQ 0x303 #define ADR_NCOPHZOFFSET 0x304 #define ADR_CIC2SCALE 0x305 //8 bits, range 0 to 6 each count==6dB atten #define ADR_CIC2M 0x306 //8 bits, 0 to 15( decimation of 1 to 16 ) #define ADR_CIC5SCALE 0x307 //8 bits, range 0 to 20 each count == 6dB #define ADR_CIC5M 0x308 //0 to 31(decimation 1 to 32) #define ADR_RCFCTRL 0x309 // 8 bits #define ADR_RCFM 0x30A //8 bits 0 to 31 (decimation 1 to 32) #define ADR_RCFOFFSET 0x30B //8 bit filter coef offset for RCF filter #define ADR_TAPS 0x30C //8 bits 0 to 255(number of RCF taps 1 to 256) #define ADR_RESERVED 0x30D //must be zero //These tables are used to calculate the decimation stage gains // which are a function of the decimation rate of each stage const int CIC2_SCALE_TBL[17] = { 0, 0,0,2,2, //1-16 3,4,4,4, 5,5,5,6, 6,6,6,6 }; const int CIC5_SCALE_TBL[33] = { 0, 0, 0, 3, 5, //1-32 7, 8,10,10, 11,12,13,13, 14,15,15,15, 16,16,17,17, 17,18,18,18, 19,19,19,20, 20,20,20,20 }; // Parameters for the 6620 filter ////////////////////// 5 KHz ////////////////////////////////////// // Pass bandwidth .0025 .001 dB // Stop bandwidth .004069 -90 dB const int FIL5KHZ_CIC2RATE = 16; const int FIL5KHZ_CIC5RATE = 32; const int FIL5KHZ_RCFRATE = 16; const int FIL5KHZ_USEABLEBW = 5000; const int FIL5KHZ_TAPS = 256; const int FIL5KHZ_COEF[FIL5KHZ_TAPS] = { 672, -502, 442, -232, 228, -117, 112, -89, 32, -103, -38, -138, -105, -184, -166, -229, -217, -263, -251, -280, -260, -270, -238, -229, -183, -155, -95, -50, 22, 80, 159, 224, 303, 367, 439, 493, 549, 583, 615, 622, 621, 595, 559, 496, 423, 325, 217, 89, -46, -195, -344, -501, -650, -798, -930, -1052, -1149, -1226, -1272, -1291, -1272, -1222, -1132, -1009, -847, -654, -428, -177, 98, 387, 687, 988, 1285, 1565, 1826, 2053, 2244, 2385, 2475, 2504, 2469, 2365, 2193, 1948, 1636, 1257, 819, 326, -210, -782, -1374, -1975, -2569, -3141, -3674, -4153, -4559, -4879, -5094, -5193, -5162, -4991, -4670, -4194, -3558, -2763, -1810, -704, 547, 1931, 3436, 5043, 6735, 8490, 10285, 12097, 13900, 15668, 17376, 18999, 20512, 21891, 23116, 24168, 25031, 25690, 26135, 26359, 26359, 26135, 25690, 25031, 24168, 23116, 21891, 20512, 18999, 17376, 15668, 13900, 12097, 10285, 8490, 6735, 5043, 3436, 1931, 547, -704, -1810, -2763, -3558, -4194, -4670, -4991, -5162, -5193, -5094, -4879, -4559, -4153, -3674, -3141, -2569, -1975, -1374, -782, -210, 326, 819, 1257, 1636, 1948, 2193, 2365, 2469, 2504, 2475, 2385, 2244, 2053, 1826, 1565, 1285, 988, 687, 387, 98, -177, -428, -654, -847, -1009, -1132, -1222, -1272, -1291, -1272, -1226, -1149, -1052, -930, -798, -650, -501, -344, -195, -46, 89, 217, 325, 423, 496, 559, 595, 621, 622, 615, 583, 549, 493, 439, 367, 303, 224, 159, 80, 22, -50, -95, -155, -183, -229, -238, -270, -260, -280, -251, -263, -217, -229, -166, -184, -105, -138, -38, -103, 32, -89, 112, -117, 228, -232, 442, -502, 672 }; ////////////////////// 10 KHz ////////////////////////////////////// // Pass bandwidth .005 .001 dB // Stop bandwidth .008138 -90 dB const int FIL10KHZ_CIC2RATE = 8; const int FIL10KHZ_CIC5RATE = 32; const int FIL10KHZ_RCFRATE = 16; const int FIL10KHZ_USEABLEBW = 10000; const int FIL10KHZ_TAPS = 256; const int FIL10KHZ_COEF[FIL10KHZ_TAPS] = { 672, -502, 442, -232, 228, -117, 112, -89, 32, -103, -38, -138, -105, -184, -166, -229, -217, -263, -251, -280, -260, -270, -238, -229, -183, -155, -95, -50, 22, 80, 159, 224, 303, 367, 439, 493, 549, 583, 615, 622, 621, 595, 559, 496, 423, 325, 217, 89, -46, -195, -344, -501, -650, -798, -930, -1052, -1149, -1226, -1272, -1291, -1272, -1222, -1132, -1009, -847, -654, -428, -177, 98, 387, 687, 988, 1285, 1565, 1826, 2053, 2244, 2385, 2475, 2504, 2469, 2365, 2193, 1948, 1636, 1257, 819, 326, -210, -782, -1374, -1975, -2569, -3141, -3674, -4153, -4559, -4879, -5094, -5193, -5162, -4991, -4670, -4194, -3558, -2763, -1810, -704, 547, 1931, 3436, 5043, 6735, 8490, 10285, 12097, 13900, 15668, 17376, 18999, 20512, 21891, 23116, 24168, 25031, 25690, 26135, 26359, 26359, 26135, 25690, 25031, 24168, 23116, 21891, 20512, 18999, 17376, 15668, 13900, 12097, 10285, 8490, 6735, 5043, 3436, 1931, 547, -704, -1810, -2763, -3558, -4194, -4670, -4991, -5162, -5193, -5094, -4879, -4559, -4153, -3674, -3141, -2569, -1975, -1374, -782, -210, 326, 819, 1257, 1636, 1948, 2193, 2365, 2469, 2504, 2475, 2385, 2244, 2053, 1826, 1565, 1285, 988, 687, 387, 98, -177, -428, -654, -847, -1009, -1132, -1222, -1272, -1291, -1272, -1226, -1149, -1052, -930, -798, -650, -501, -344, -195, -46, 89, 217, 325, 423, 496, 559, 595, 621, 622, 615, 583, 549, 493, 439, 367, 303, 224, 159, 80, 22, -50, -95, -155, -183, -229, -238, -270, -260, -280, -251, -263, -217, -229, -166, -184, -105, -138, -38, -103, 32, -89, 112, -117, 228, -232, 442, -502, 672, }; ////////////////////// 25 KHz ////////////////////////////////////// // Pass bandwidth .0125 .001 dB // Stop bandwidth .018896 -90 dB const int FIL25KHZ_CIC2RATE = 7; const int FIL25KHZ_CIC5RATE = 21; const int FIL25KHZ_RCFRATE = 12; const int FIL25KHZ_USEABLEBW = 25000; const int FIL25KHZ_TAPS = 256; const int FIL25KHZ_COEF[FIL25KHZ_TAPS] = { -81, 279, -99, 176, -6, 131, 37, 105, 44, 75, 25, 31, -17, -26, -72, -89, -130, -147, -178, -185, -199, -190, -183, -153, -122, -72, -21, 44, 108, 179, 242, 305, 353, 392, 411, 414, 392, 352, 286, 202, 97, -20, -149, -280, -410, -529, -633, -712, -763, -778, -757, -693, -590, -448, -272, -67, 156, 391, 623, 844, 1037, 1195, 1303, 1355, 1342, 1261, 1110, 892, 612, 281, -90, -483, -881, -1265, -1614, -1908, -2128, -2257, -2283, -2197, -1994, -1676, -1250, -730, -134, 514, 1185, 1849, 2471, 3018, 3457, 3757, 3893, 3845, 3600, 3155, 2513, 1689, 709, -395, -1579, -2795, -3986, -5093, -6055, -6810, -7302, -7479, -7296, -6720, -5728, -4311, -2476, -242, 2356, 5268, 8433, 11778, 15221, 18674, 22045, 25242, 28177, 30765, 32933, 34617, 35768, 36352, 36352, 35768, 34617, 32933, 30765, 28177, 25242, 22045, 18674, 15221, 11778, 8433, 5268, 2356, -242, -2476, -4311, -5728, -6720, -7296, -7479, -7302, -6810, -6055, -5093, -3986, -2795, -1579, -395, 709, 1689, 2513, 3155, 3600, 3845, 3893, 3757, 3457, 3018, 2471, 1849, 1185, 514, -134, -730, -1250, -1676, -1994, -2197, -2283, -2257, -2128, -1908, -1614, -1265, -881, -483, -90, 281, 612, 892, 1110, 1261, 1342, 1355, 1303, 1195, 1037, 844, 623, 391, 156, -67, -272, -448, -590, -693, -757, -778, -763, -712, -633, -529, -410, -280, -149, -20, 97, 202, 286, 352, 392, 414, 411, 392, 353, 305, 242, 179, 108, 44, -21, -72, -122, -153, -183, -190, -199, -185, -178, -147, -130, -89, -72, -26, -17, 31, 25, 75, 44, 105, 37, 131, -6, 176, -99, 279, -81, }; ////////////////////// 50 KHz ////////////////////////////////////// // Pass bandwidth .025 .001 dB // Stop bandwidth .037792 -90 dB const int FIL50KHZ_CIC2RATE = 8; const int FIL50KHZ_CIC5RATE = 30; const int FIL50KHZ_RCFRATE = 5; const int FIL50KHZ_USEABLEBW = 50000; const int FIL50KHZ_TAPS = 256; const int FIL50KHZ_COEF[FIL50KHZ_TAPS] = { -115, 572, 286, 894, 855, 1164, 1031, 971, 601, 278,-156,-424,-607,-546,-363,-34 , 269, 511, 560, 445, 158,-174,-474,-610,-558,-300, 62, 431, 663, 689, 471, 88 , -355,-694,-821,-668,-282, 228, 686, 938, 880, 519,-44,-622,-1023,-1093,-794,-202 , 491, 1058, 1291, 1097, 511,-282,-1025,-1457,-1414,-880,-14, 908, 1568, 1727, 1299, 401 , -689,-1602,-2017,-1759,-880, 353, 1536, 2258, 2242, 1452, 113,-1343,-2423,-2728,-2110,-724 , 997, 2479, 3195, 2849, 1494,-465,-2390,-3612,-3661,-2442,-292, 2109, 3946, 4541, 3597, 1330 , -1574,-4154,-5488,-5011,-2740, 688, 4177, 6519, 6787, 4690, 724,-3918,-7684,-9155,-7544,-3033 , 3174, 9131, 12703, 12256, 7260,-1366,-11320,-19399,-22305,-17564,-4306, 16319, 41233, 66039, 85987, 97095 , 97095, 85987, 66039, 41233, 16319,-4306,-17564,-22305,-19399,-11320,-1366, 7260, 12256, 12703, 9131, 3174 , -3033,-7544,-9155,-7684,-3918, 724, 4690, 6787, 6519, 4177, 688,-2740,-5011,-5488,-4154,-1574 , 1330, 3597, 4541, 3946, 2109,-292,-2442,-3661,-3612,-2390,-465, 1494, 2849, 3195, 2479, 997 , -724,-2110,-2728,-2423,-1343, 113, 1452, 2242, 2258, 1536, 353,-880,-1759,-2017,-1602,-689 , 401, 1299, 1727, 1568, 908,-14,-880,-1414,-1457,-1025,-282, 511, 1097, 1291, 1058, 491 , -202,-794,-1093,-1023,-622,-44, 519, 880, 938, 686, 228,-282,-668,-821,-694,-355 , 88, 471, 689, 663, 431, 62,-300,-558,-610,-474,-174, 158, 445, 560, 511, 269 , -34,-363,-546,-607,-424,-156, 278, 601, 971, 1031, 1164, 855, 894, 286, 572,-115 }; ////////////////////// 100 KHz ////////////////////////////////////// // Pass bandwidth .0125 .001 dB // Stop bandwidth .018896 -90 dB const int FIL100KHZ_CIC2RATE = 5; const int FIL100KHZ_CIC5RATE = 30; const int FIL100KHZ_RCFRATE = 4; const int FIL100KHZ_USEABLEBW = 100000; const int FIL100KHZ_TAPS = 256; const int FIL100KHZ_COEF[FIL100KHZ_TAPS] = { 111, 108, 197, 150, 69,-150,-405,-679,-847,-866,-689,-380,-23, 244, 337, 222, -27,-290,-429,-371,-131, 176, 402, 426, 227,-107,-406,-515,-361,-8, 374, 586 , 504, 149,-309,-639,-655,-326, 199, 657, 801, 531,-39,-630,-929,-758,-173, 545 , 1024, 994, 435,-394,-1071,-1227,-742, 170, 1054, 1438, 1085, 131,-956,-1608,-1449,-509, 765 , 1716, 1818, 959,-467,-1739,-2170,-1474, 53, 1653, 2479, 2038, 481,-1436,-2717,-2634,-1140 , 1064, 2851, 3238, 1922,-515,-2847,-3823,-2822,-237, 2665, 4354, 3835, 1218,-2256,-4793,-4956 , -2465, 1563, 5093, 6183, 4030,-501,-5191,-7525,-6007,-1062, 5000, 9018, 8574, 3371,-4365,-10755 , -12118,-6953, 2955, 12982, 17627, 13228, 184,-16444,-28453,-27679,-9595, 24220, 65916, 103759, 126224, 126224 , 103759, 65916, 24220,-9595,-27679,-28453,-16444, 184, 13228, 17627, 12982, 2955,-6953,-12118,-10755,-4365 , 3371, 8574, 9018, 5000,-1062,-6007,-7525,-5191,-501, 4030, 6183, 5093, 1563,-2465,-4956,-4793 , -2256, 1218, 3835, 4354, 2665,-237,-2822,-3823,-2847,-515, 1922, 3238, 2851, 1064,-1140,-2634 , -2717,-1436, 481, 2038, 2479, 1653, 53,-1474,-2170,-1739,-467, 959, 1818, 1716, 765,-509 , -1449,-1608,-956, 131, 1085, 1438, 1054, 170,-742,-1227,-1071,-394, 435, 994, 1024, 545 , -173,-758,-929,-630,-39, 531, 801, 657, 199,-326,-655,-639,-309, 149, 504, 586 , 374,-8,-361,-515,-406,-107, 227, 426, 402, 176,-131,-371,-429,-290,-27, 222 , 337, 244,-23,-380,-689,-866,-847,-679,-405,-150, 69, 150, 197, 108, 111 }; ////////////////////// 150 KHz ////////////////////////////////////// const int FIL150KHZ_CIC2RATE = 5; const int FIL150KHZ_CIC5RATE = 28; const int FIL150KHZ_RCFRATE = 3; const int FIL150KHZ_USEABLEBW = 150000; const int FIL150KHZ_TAPS = 256; const int FIL150KHZ_COEF[FIL150KHZ_TAPS] = { 80,-1272,-1501,-2506,-1995,-1237, 225, 985, 1065, 218,-585,-897,-355, 403, 811, 432 , -300,-777,-504, 221, 771, 585,-145,-776,-676, 61, 783, 777, 36,-784,-884,-149 , 774, 996, 279,-749,-1108,-427, 706, 1217, 591,-641,-1321,-772, 553, 1415, 968,-437 , -1496,-1177, 293, 1560, 1398,-119,-1602,-1628,-87, 1620, 1863, 325,-1608,-2101,-598, 1562 , 2339, 905,-1478,-2571,-1247, 1350, 2793, 1624,-1175,-3001,-2036, 947, 3190, 2484,-660,-3353 , -2967, 307, 3484, 3486, 118,-3575,-4042,-626, 3619, 4637, 1227,-3606,-5273,-1936, 3523, 5956 , 2775,-3354,-6694,-3770, 3080, 7501, 4965,-2669,-8398,-6426, 2076, 9422, 8256,-1227,-10635,-10642 , -11, 12151, 13929, 1901,-14192,-18856,-5052, 17262, 27312, 11251,-22720,-45866,-28628 , 35237, 119172, 178193, 178193, 119172, 35237,-28628,-45866,-22720, 11251, 27312, 17262,-5052,-18856,-14192, 1901 , 13929, 12151,-11,-10642,-10635,-1227, 8256, 9422, 2076,-6426,-8398,-2669, 4965, 7501, 3080,-3770 , -6694,-3354, 2775, 5956, 3523,-1936,-5273,-3606, 1227, 4637, 3619,-626,-4042,-3575, 118, 3486 , 3484, 307,-2967,-3353,-660, 2484, 3190, 947,-2036,-3001,-1175, 1624, 2793, 1350,-1247,-2571 , -1478, 905, 2339, 1562,-598,-2101,-1608, 325, 1863, 1620,-87,-1628,-1602,-119, 1398, 1560 , 293,-1177,-1496,-437, 968, 1415, 553,-772,-1321,-641, 591, 1217, 706,-427,-1108,-749 , 279, 996, 774,-149,-884,-784, 36, 777, 783, 61,-676,-776,-145, 585, 771, 221 , -504,-777,-300, 432, 811, 403,-355,-897,-585, 218, 1065, 985, 225,-1237,-1995,-2506 , -1501,-1272, 80 }; ////////////////////// 190 KHz ////////////////////////////////////// const int FIL190KHZ_CIC2RATE = 10; const int FIL190KHZ_CIC5RATE = 17; const int FIL190KHZ_RCFRATE = 2; const int FIL190KHZ_USEABLEBW = 190000; const int FIL190KHZ_TAPS = 256; const int FIL190KHZ_COEF[FIL190KHZ_TAPS] = { 3447 ,-2833 ,-1187 ,-2836 , 784 ,-262 ,-551 ,-1155 , 655 , 341 ,-484 ,-824 , 627 , 573 ,-493 ,-814 , 597 , 739 ,-495 ,-905 , 556 , 899 ,-472 ,-1036 , 498 , 1065 ,-419 ,-1189 , 417 , 1238 ,-335 ,-1355 , 308 , 1416 ,-219 ,-1527 , 167 , 1595 ,-66 ,-1700 ,-8 , 1771 , 124 ,-1870 ,-221 , 1939 , 355 ,-2031 , -475 , 2096 , 628 ,-2177 ,-772 , 2235 , 946 ,-2304 ,-1114 , 2350 , 1310 ,-2404 ,-1504 , 2435 , 1723 ,-2470 , -1944 , 2482 , 2188 ,-2494 ,-2436 , 2483 , 2707 ,-2468 ,-2984 , 2429 , 3285 ,-2381 ,-3593 , 2308 , 3925 ,-2221 , -4268 , 2107 , 4635 ,-1974 ,-5017 , 1808 , 5426 ,-1618 ,-5854 , 1390 , 6314 ,-1128 ,-6799 , 819 , 7322 ,-465 , -7882 , 50 , 8492 , 429 ,-9154 ,-993 , 9888 , 1653 ,-10702 ,-2438 , 11626 , 3378 ,-12684 ,-4524 , 13926 , 5947 , -15415 ,-7763 , 17256 , 10157 ,-19614 ,-13468 , 22786 , 18349 ,-27322 ,-26280 , 34392 , 41334 ,-46742 ,-79378 , 69315 , 263870 , 263870 , 69315 ,-79378 ,-46742 , 41334 , 34392 ,-26280 ,-27322 , 18349 , 22786 ,-13468 ,-19614 , 10157 , 17256 ,-7763 ,-15415 , 5947 , 13926 ,-4524 ,-12684 , 3378 , 11626 ,-2438 ,-10702 , 1653 , 9888 ,-993 ,-9154 , 429 , 8492 , 50 ,-7882 , -465 , 7322 , 819 ,-6799 ,-1128 , 6314 , 1390 ,-5854 ,-1618 , 5426 , 1808 ,-5017 ,-1974 , 4635 , 2107 ,-4268 , -2221 , 3925 , 2308 ,-3593 ,-2381 , 3285 , 2429 ,-2984 ,-2468 , 2707 , 2483 ,-2436 ,-2494 , 2188 , 2482 ,-1944 , -2470 , 1723 , 2435 ,-1504 ,-2404 , 1310 , 2350 ,-1114 ,-2304 , 946 , 2235 ,-772 ,-2177 , 628 , 2096 ,-475 , -2031 , 355 , 1939 ,-221 ,-1870 , 124 , 1771 ,-8 ,-1700 ,-66 , 1595 , 167 ,-1527 ,-219 , 1416 , 308 , -1355 ,-335 , 1238 , 417 ,-1189 ,-419 , 1065 , 498 ,-1036 ,-472 , 899 , 556 ,-905 ,-495 , 739 , 597 , -814 ,-493 , 573 , 627 ,-824 ,-484 , 341 , 655 ,-1155 ,-551 ,-262 , 784 ,-2836 ,-1187 ,-2833 , 3447 }; ////////////////////// 250 KHz ////////////////////////////////////// const int FIL250KHZ_CIC2RATE = 5; const int FIL250KHZ_CIC5RATE = 11; const int FIL250KHZ_RCFRATE = 4; const int FIL250KHZ_USEABLEBW = 250000; const int FIL250KHZ_TAPS = 220; const int FIL250KHZ_COEF[FIL250KHZ_TAPS] = { 12, -10, -7, -31, -35, -41, -25, -3, 29, 48, 50, 23, -20, -64, -83, -63, -6, 66, 118, 119, 59, -44, -142, -185, -139, -14, 140, 247, 244, 115, -93, -286, -360, -261, -12, 278, 466, 442, 188, -197, -532, -639, -434, 20, 520, 817, 734, 266, -394, -931, -1057, -659, 119, 925, 1351, 1138, 322, -746, -1551, -1655, -929, 344, 1577, 2139, 1674, 312, -1348, -2495, -2494, -1228, 789, 2612, 3296, 2378, 160, -2366, -3951, -3694, -1535, 1630, 4301, 5070, 3343, -275, -4155, -6356, -5566, -1839, 3280, 7353, 8174, 4900, -1348, -7792, -11163, -9262, -2215, 7235, 14660, 15836, 8827, -4646, -19277, -27834, -23877, -4514, 27947, 66209, 100118, 120005, 120005, 100118, 66209, 27947, -4514, -23877, -27834, -19277, -4646, 8827, 15836, 14660, 7235, -2215, -9262, -11163, -7792, -1348, 4900, 8174, 7353, 3280, -1839, -5566, -6356, -4155, -275, 3343, 5070, 4301, 1630, -1535, -3694, -3951, -2366, 160, 2378, 3296, 2612, 789, -1228, -2494, -2495, -1348, 312, 1674, 2139, 1577, 344, -929, -1655, -1551, -746, 322, 1138, 1351, 925, 119, -659, -1057, -931, -394, 266, 734, 817, 520, 20, -434, -639, -532, -197, 188, 442, 466, 278, -12, -261, -360, -286, -93, 115, 244, 247, 140, -14, -139, -185, -142, -44, 59, 119, 118, 66, -6, -63, -83, -64, -20, 23, 50, 48, 29, -3, -25, -41, -35, -31, -7, -10, 12, }; ////////////////////// 500 KHz ////////////////////////////////////// const int FIL500KHZ_CIC2RATE = 2; const int FIL500KHZ_CIC5RATE = 29; const int FIL500KHZ_RCFRATE = 2; const int FIL500KHZ_USEABLEBW = 500000; const int FIL500KHZ_TAPS = 116; const int FIL500KHZ_COEF[FIL500KHZ_TAPS] = { -124, -118, 211, 746, 770, 52, -573, -174, 643, 459, -609, -758, 494, 1096, -247, -1419, -154, 1675, 710, -1801, -1407, 1732, 2204, -1405, -3037, 771, 3818, 202, -4440, -1516, 4779, 3142, -4706, -5014, 4091, 7022, -2814, -9019, 768, 10812, 2133, -12169, -5958, 12807, 10771, -12368, -16660, 10361, 23800, -5990, -32604, -2353, 44095, 18918, -60919, -60113, 87967, 253562, 253562, 87967, -60113, -60919, 18918, 44095, -2353, -32604, -5990, 23800, 10361, -16660, -12368, 10771, 12807, -5958, -12169, 2133, 10812, 768, -9019, -2814, 7022, 4091, -5014, -4706, 3142, 4779, -1516, -4440, 202, 3818, 771, -3037, -1405, 2204, 1732, -1407, -1801, 710, 1675, -154, -1419, -247, 1096, 494, -758, -609, 459, 643, -174, -573, 52, 770, 746, 211, -118, -124 }; ////////////////////// 1000 KHz ////////////////////////////////////// const int FIL1000KHZ_CIC2RATE = 2; const int FIL1000KHZ_CIC5RATE = 13; const int FIL1000KHZ_RCFRATE = 2; const int FIL1000KHZ_USEABLEBW = 1000000; const int FIL1000KHZ_TAPS = 52; const int FIL1000KHZ_COEF[FIL1000KHZ_TAPS] = { 155, -946, -3137, -4394, -1611, 3281, 3993, -1821, -6300, -1205, 7795, 5988, -7134, -11977, 3025, 17757, 5598, -21134, -19379, 18859, 38850, -5161, -65511, -37716, 102796, 241472, 241472, 102796, -37716, -65511, -5161, 38850, 18859, -19379, -21134, 5598, 17757, 3025, -11977, -7134, 5988, 7795, -1205, -6300, -1821, 3993, 3281, -1611, -4394, -3137, -946, 155 }; ////////////////////// 1500 KHz ////////////////////////////////////// const int FIL1500KHZ_CIC2RATE = 2; const int FIL1500KHZ_CIC5RATE = 8; const int FIL1500KHZ_RCFRATE = 2; const int FIL1500KHZ_USEABLEBW = 1500000; const int FIL1500KHZ_TAPS = 32; const int FIL1500KHZ_COEF[FIL1500KHZ_TAPS] = { -2838, -7081, -7827, 591, 11843, 11249, -5268, -17456, -2992, 26223, 27127, -17480, -57306, -18005, 104558, 216808, 216808, 104558, -18005, -57306, -17480, 27127, 26223, -2992, -17456, -5268, 11249, 11843, 591, -7827, -7081, -2838, }; ////////////////////// 2000 KHz /////////////////////////////////// const int FIL2000KHZ_CIC2RATE = 2; const int FIL2000KHZ_CIC5RATE = 5; const int FIL2000KHZ_RCFRATE = 2; const int FIL2000KHZ_USEABLEBW = 2000000; const int FIL2000KHZ_TAPS = 20; const int FIL2000KHZ_COEF[FIL2000KHZ_TAPS] = { 1045 , 8235 , 18078 , 19813 , 148 ,-31087, -38801 , 9369 , 100534, 174811 , 174811 , 100534 , 9369 ,-38801 ,-31087 , 148 , 19813, 18078 , 8235 , 1045 }; ////////////////////// 4000 KHz /////////////////////////////////// const int FIL4000KHZ_CIC2RATE = 2; const int FIL4000KHZ_CIC5RATE = 4; const int FIL4000KHZ_RCFRATE = 2; const int FIL4000KHZ_USEABLEBW = 4000000; const int FIL4000KHZ_TAPS = 16; const int FIL4000KHZ_COEF[FIL4000KHZ_TAPS] = { -30,-9364, 2158, 31085,-12771,-83607, 42342, 292332, 292332, 42342,-83607,-12771, 31085, 2158,-9364,-30 }; ////////////////////////////////////////////////////////////////////// // Construction/Destruction ////////////////////////////////////////////////////////////////////// Cad6620::Cad6620() { m_NCOphzDither = true; m_NCOampDither = true; m_CIC2Rate = FIL190KHZ_CIC2RATE; m_CIC5Rate = FIL190KHZ_CIC5RATE; m_RCFRate = FIL190KHZ_RCFRATE; m_UseableBW = FIL190KHZ_USEABLEBW; for(int i=0; i= MAX_MSGS) m_BufIndx = 0; } ///////////////////////////////////////////////////////////////////////////////////// //Called to get the next formatted message from the messge buffer to send directly // to the radio for loading the AD6620 tegisters // returns false when end of all msgs is reached and load is done ///////////////////////////////////////////////////////////////////////////////////// bool Cad6620::GetNext6620Msg(CAscpMsg &pAscpMsg) { if(m_BufIndx < m_BufLength) { pAscpMsg.InitTxMsg(TYPE_HOST_DATA_ITEM1); pAscpMsg.AddParm16(m_MsgBuffer[m_BufIndx].adr); pAscpMsg.AddParm32(m_MsgBuffer[m_BufIndx].data); pAscpMsg.AddParm8(m_MsgBuffer[m_BufIndx++].datah); return true; } else return false; } cutesdr-1.0.5/interface/ad6620.h0000644000175000017500000000253511546075374016162 0ustar bottomsbottoms////////////////////////////////////////////////////////////////////// // ad6620.h: interface for the Cad6620 class. // Needed only for sdr-14 and sdr-iq radios // // History: // 2010-09-15 Initial creation MSW // 2011-03-27 Initial release ///////////////////////////////////////////////////////////////////// #ifndef AD6620_H #define AD6620_H #include "interface/netiobase.h" #define MAX_COEF 256 #define MAX_MSGS 512 class Cad6620 { public: Cad6620(); virtual ~Cad6620(); enum eSAMPLERATE { BWKHZ_5, BWKHZ_10, BWKHZ_25, BWKHZ_50, BWKHZ_100, BWKHZ_150, BWKHZ_190, BWKHZ_250, BWKHZ_500, BWKHZ_1000, BWKHZ_1500, BWKHZ_2000, BWKHZ_4000 }; void CreateLoad6620Msgs(int filter); //called to fill up msg buffer with all msgs needed to load 'filter' bool GetNext6620Msg(CAscpMsg &pAscpMsg); //call to get formatted msg to send to radio private: void PutInMsgBuffer(int adr, quint32 data); struct mq { quint16 adr; quint32 data; quint8 datah; }m_MsgBuffer[MAX_MSGS]; int m_BufIndx; int m_BufLength; bool m_NCOphzDither; bool m_NCOampDither; int m_UseableBW; int m_CIC2Rate; int m_CIC2Scale; int m_CIC5Rate; int m_CIC5Scale; int m_RCFRate; int m_RCFScale; int m_RCFTaps; int m_FIRcoef[MAX_COEF]; int m_TotalDecimationRate; }; #endif // AD6620_H cutesdr-1.0.5/interface/netiobase.h0000644000175000017500000001013111546075374017220 0ustar bottomsbottoms////////////////////////////////////////////////////////////////////// // netiobase.h: interface for the CNetIOBase, // CAscpMsg, CUdpThread, CTcpThread, and CIQDataThread classes. // // History: // 2010-09-15 Initial creation MSW // 2011-03-27 Initial release ///////////////////////////////////////////////////////////////////// #ifndef NETIOBASE_H #define NETIOBASE_H #include #include #include #include #include #include #include "interface/ascpmsg.h" #include #include typedef union { struct bs { unsigned char b0; unsigned char b1; unsigned char b2; unsigned char b3; }bytes; int all; }tBtoL4; ////////////////////////////////////////////////////////////////////// // Low level thread for reading received data bytes from // UDP Rx Thread class ////////////////////////////////////////////////////////////////////// class CUdpThread : public QThread { Q_OBJECT public: CUdpThread(QObject* pParent = 0); protected: void run(); private: void OnreadyRead(); quint16 m_LastSeqNum; QUdpSocket* m_pUdpSocket; QObject* m_pParent; }; ////////////////////////////////////////////////////////////////////// // Low level TCP Client thread for connecting, and reading received // data bytes. ////////////////////////////////////////////////////////////////////// class CTcpThread : public QThread { Q_OBJECT public: CTcpThread(QObject* pParent = 0); QTcpSocket* m_pTcpSocket; enum eTcpStates { TCP_NOT_CONNECTED, TCP_CONNECTED, TCP_DISCONNECT }; enum eMsgStates {//ASCP msg assembly states MSGSTATE_HDR1, MSGSTATE_HDR2, MSGSTATE_DATA }; eTcpStates m_TcpState; signals: protected: void run(); private: void ManageTcpClientConnection(); void AssembleAscpMsg(char* pBuf, int length); void StartUdp(); void StopUdp(); int m_RxMsgLength; int m_RxMsgIndex; eMsgStates m_MsgState; CAscpMsg m_RxAscpMsg; CUdpThread* m_pUdpThread; QObject* m_pParent; }; ////////////////////////////////////////////////////////////////////// // Low level thread for reading received data bytes from // UDP data FIFO and calling IQ processing routine // This thread performs all the I/Q data DSP processing. ////////////////////////////////////////////////////////////////////// class CIQDataThread : public QThread { Q_OBJECT public: CIQDataThread(QObject* pParent = 0); ~CIQDataThread(); protected: void run(); private: QObject* m_pParent; void FileTest(); QFile m_File; }; ////////////////////////////////////////////////////////////////////// //Base Class to derive from a radio specific interface. Provides only // low level message assembly and I/Q data message buffering. // User would derive from this class to process all the radio messages ////////////////////////////////////////////////////////////////////// class CNetIOBase : public QObject { Q_OBJECT public: CNetIOBase(); virtual ~CNetIOBase(); enum eStatus { NOT_CONNECTED, CONNECTED, RUNNING, ERROR }; //stub virtual function gets implemented by specific device sub class implementation virtual void ParseAscpMsg( CAscpMsg*){} //implement to decode all the command/status messages virtual void SendIOStatus(int ){} //implement to process IO status/error changes virtual void ProcessIQData( double* , int ){}//implement to process the IQ data messages from the radio void StartIO(); //starts IO threads void StopIO(); //stops IO threads void SendAscpMsg(CAscpMsg* pMsg); //sends msg to radio void SetupNetwork(QHostAddress ip4Addr, quint16 port); //set network parameters public: bool m_TcpThreadQuit; bool m_UdpThreadQuit; bool m_IQDataThreadQuit; bool m_SampleSize24; int m_RxQueueHead; int m_RxQueueTail; int m_MissedPackets; quint16 m_Port; double **m_pUdpRxQueue; QHostAddress m_IPAdr; QWaitCondition m_QWaitFifoData; QMutex m_TcpMutex; QMutex m_UdpRxMutex; CTcpThread* m_pTcpThread; CIQDataThread* m_pIQDataThread; protected: private: }; #endif // NETIOBASE_H cutesdr-1.0.5/interface/sdrinterface.cpp0000644000175000017500000006657511716016005020263 0ustar bottomsbottoms///////////////////////////////////////////////////////////////////// // sdrinterface.cpp: implementation of the CSdrInterface class. // // This class implements the interface between the upper GUI and //the specific SDR. It parses messages/data received by the radio // and sends messages to the radio for setup and control. // // // History: // 2010-09-15 Initial creation MSW // 2011-03-27 Initial release // 2011-04-16 Added Frequency range logic for optional down converter modules // 2011-07-21 Modified Frequency range logic by adding VCO frequency and number of ranges parameters // 2011-08-07 Added WFM Support ///////////////////////////////////////////////////////////////////// //========================================================================================== // + + + This Software is released under the "Simplified BSD License" + + + //Copyright 2010 Moe Wheatley. All rights reserved. // //Redistribution and use in source and binary forms, with or without modification, are //permitted provided that the following conditions are met: // // 1. Redistributions of source code must retain the above copyright notice, this list of // conditions and the following disclaimer. // // 2. Redistributions in binary form must reproduce the above copyright notice, this list // of conditions and the following disclaimer in the documentation and/or other materials // provided with the distribution. // //THIS SOFTWARE IS PROVIDED BY Moe Wheatley ``AS IS'' AND ANY EXPRESS OR IMPLIED //WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND //FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL Moe Wheatley OR //CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR //CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR //SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON //ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING //NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF //ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // //The views and conclusions contained in the software and documentation are those of the //authors and should not be interpreted as representing official policies, either expressed //or implied, of Moe Wheatley. //========================================================================================== #include "interface/sdrinterface.h" #include "gui/testbench.h" #include #define SPUR_CAL_MAXSAMPLES 300000 #define MAX_SAMPLERATES 4 //Tables to get various parameters based on the gui sdrsetup samplerate index value const quint32 SDRIQ_MAXBW[MAX_SAMPLERATES] = { 50000, 100000, 150000, 190000 }; const quint32 SDRIQ_6620FILTERS[MAX_SAMPLERATES] = { Cad6620::BWKHZ_50, Cad6620::BWKHZ_100, Cad6620::BWKHZ_150, Cad6620::BWKHZ_190 }; const double SDRIQ_6620FILTERGAIN[MAX_SAMPLERATES] = { 0.0, 8.0, 11.0, 22.0 }; const double SDRIQ_SAMPLERATE[MAX_SAMPLERATES] = { (66666666.6667/1200.0), (66666666.6667/600.0), (66666666.6667/420.0), (66666666.6667/340.0) }; const quint32 NETSDR_MAXBW[MAX_SAMPLERATES] = { 50000, // 100000, 200000, 500000, 1600000 }; const double NETSDR_SAMPLERATE[MAX_SAMPLERATES] = { (80.0e6/1280.0), (80.0e6/320.0), (80.0e6/128.0), (80.0e6/40.0) }; const quint32 SDRIP_MAXBW[MAX_SAMPLERATES] = { 50000, 200000, 500000, 1800000 }; const double SDRIP_SAMPLERATE[MAX_SAMPLERATES] = { (80.0e6/1280.0), (80.0e6/320.0), (80.0e6/130.0), (80.0e6/40.0) }; ///////////////////////////////////////////////////////////////////// // Constructor/Destructor ///////////////////////////////////////////////////////////////////// CSdrInterface::CSdrInterface() { m_Running = false; m_BootRev = 0.0; m_AppRev = 0.0; m_FftBufPos = 0; m_RfGain = 0; m_KeepAliveCounter = 0; m_GainCalibrationOffset = 0.0; m_SampleRate = 1.0; m_MaxBandwidth = 1; m_SerialNum = ""; m_DeviceName = ""; m_BandwidthIndex = -1; m_RadioType = SDR14; m_FftSize = 4096; m_DisplaySkipCounter = 0; m_NCOSpurOffsetI = 0.0; m_NCOSpurOffsetQ = 0.0; m_MaxDisplayRate = 10; m_CurrentFrequency = 0; m_BaseFrequencyRangeMin = 0; //load default frequency ranges m_BaseFrequencyRangeMax = 30000000; m_OptionFrequencyRangeMin = 0; m_OptionFrequencyRangeMax = 30000000; SetMaxDisplayRate(m_MaxDisplayRate); m_ScreenUpateFinished = TRUE; SetFftSize(4096); SetFftAve(1); m_pSoundCardOut = new CSoundOut(this); m_Status = NOT_CONNECTED; m_ChannelMode = CI_RX_CHAN_SETUP_SINGLE_1; //default channel settings for NetSDR m_Channel = CI_RX_CHAN_1; } CSdrInterface::~CSdrInterface() { if(m_pSoundCardOut) delete m_pSoundCardOut; } ///////////////////////////////////////////////////////////////////// // returns the maximum bandwidth based on radio and gui bw index ///////////////////////////////////////////////////////////////////// qint32 CSdrInterface::GetMaxBWFromIndex(qint32 index) { qint32 ret = 0; if(index >= MAX_SAMPLERATES) return 100000; switch(m_RadioType) { case SDR14: case SDRIQ: ret = SDRIQ_MAXBW[index]; break; case SDRIP: ret = SDRIP_MAXBW[index]; break; case NETSDR: ret = NETSDR_MAXBW[index]; break; default: break; } return ret; } ///////////////////////////////////////////////////////////////////// // returns the maximum bandwidth based on radio and gui bw index ///////////////////////////////////////////////////////////////////// double CSdrInterface::GetSampleRateFromIndex(qint32 index) { double ret = 1.0; if(index >= MAX_SAMPLERATES) return 100000; switch(m_RadioType) { case SDR14: case SDRIQ: ret = SDRIQ_SAMPLERATE[index]; break; case SDRIP: ret = SDRIP_SAMPLERATE[index]; break; case NETSDR: ret = NETSDR_SAMPLERATE[index]; break; default: break; } return ret; } ///////////////////////////////////////////////////////////////////// // called to start up io device threads and open device. ///////////////////////////////////////////////////////////////////// void CSdrInterface::StartIO() { CNetIOBase::StartIO(); } ///////////////////////////////////////////////////////////////////// // called to stop io device threads and close device. ///////////////////////////////////////////////////////////////////// void CSdrInterface::StopIO() { StopSdr(); CNetIOBase::StopIO(); } /////////////////////////////////////////////////////////////////////////////// // sends signal to indicate network/radio connection status /////////////////////////////////////////////////////////////////////////////// void CSdrInterface::SendIOStatus(int iostatus) { m_Status = (eStatus)iostatus; emit NewStatus( iostatus ); } //////////////////////////////////////////////////////////////////////// // Called from worker thread with new ASCP message to parse from radio // Cannot call any QT functions since is a thread so use signals // to inform the GUI. //////////////////////////////////////////////////////////////////////// void CSdrInterface::ParseAscpMsg(CAscpMsg *pMsg) { quint16 Length; pMsg->InitRxMsg(); //initialize receive msg object for read back if( pMsg->GetType() == TYPE_TARG_RESP_CITEM ) { // Is a message from SDR in response to a request //qDebug()<<"Msg "<GetCItem(); switch(pMsg->GetCItem()) { case CI_GENERAL_INTERFACE_NAME: m_DeviceName = (const char*)(&pMsg->Buf8[4]); // create radio type value from connected device string if("SDR-14" == m_DeviceName) m_RadioType = SDR14; else if("SDR-IQ" == m_DeviceName) m_RadioType = SDRIQ; else if("SDR-IP" == m_DeviceName) m_RadioType = SDRIP; if("NetSDR" == m_DeviceName) m_RadioType = NETSDR; if( (SDRIP==m_RadioType) || (NETSDR==m_RadioType) ) { m_TxMsg.InitTxMsg(TYPE_HOST_REQ_CITEM_RANGE); m_TxMsg.AddCItem(CI_RX_FREQUENCY); m_TxMsg.AddParm8(CI_RX_CHAN_1); SendAscpMsg(&m_TxMsg); } break; case CI_GENERAL_INTERFACE_SERIALNUM: m_SerialNum = (const char*)(&pMsg->Buf8[4]); break; case CI_GENERAL_INTERFACE_VERSION: break; case CI_GENERAL_HARDFIRM_VERSION: if(pMsg->GetParm8() == 0) { m_BootRev = (float)pMsg->GetParm16()/100.0; } else { m_AppRev =(float)pMsg->GetParm16()/100.0; m_BandwidthIndex = -1; //force update of sample rate logic emit NewInfoData(); } break; case CI_RX_STATE: pMsg->GetParm8(); if(RX_STATE_ON == pMsg->GetParm8()) { emit NewStatus( RUNNING ); m_Running = true; } else { emit NewStatus( CONNECTED ); m_Running = false; } break; case CI_GENERAL_OPTIONS: break; case CI_GENERAL_SECURITY_CODE: //qDebug()<<"security = "<GetParm16(); break; case CI_GENERAL_STATUS_CODE: //used as keepalive ack m_KeepAliveCounter = 0; break; case CI_RX_FREQUENCY: pMsg->GetParm8(); // tmp32 = pMsg->GetParm32(); break; case CI_RX_OUT_SAMPLE_RATE: pMsg->GetParm8(); m_SampleRate = (double)pMsg->GetParm32(); break; case CI_GENERAL_PRODUCT_ID: break; case CI_UPDATE_MODE_PARAMS: break; default: break; } } else if( pMsg->GetType() == TYPE_TARG_RESP_CITEM_RANGE ) { // Is a range request message from SDR switch( pMsg->GetCItem() ) { case CI_RX_FREQUENCY: pMsg->GetParm8(); //ignor channel Length = pMsg->GetParm8(); //qDebug()<<"range Length"<GetParm32(); pMsg->GetParm8();//ignor msb m_BaseFrequencyRangeMax = (quint64)pMsg->GetParm32(); pMsg->GetParm8(); //ignor msb pMsg->GetParm32(); //ignor VCO frequency pMsg->GetParm8(); //ignor msb m_OptionFrequencyRangeMin = m_BaseFrequencyRangeMin; //set option range to base range m_OptionFrequencyRangeMax = m_BaseFrequencyRangeMax; if(Length>1) { m_OptionFrequencyRangeMin = (quint64)pMsg->GetParm32(); pMsg->GetParm8();//ignor msb m_OptionFrequencyRangeMax = (quint64)pMsg->GetParm32(); pMsg->GetParm8();//ignor msb pMsg->GetParm32(); //ignor VCO frequency } //qDebug()<<"Base range"<GetType() == TYPE_TARG_UNSOLICITED_CITEM ) { // Is an unsolicited message from SDR switch( pMsg->GetCItem() ) { case CI_GENERAL_STATUS_CODE: if( GENERAL_STATUS_ADOVERLOAD == pMsg->GetParm8() ) SendIOStatus(ADOVR); break; default: break; } } else if( pMsg->GetType() == TYPE_TARG_DATA_ITEM0 ) { } else if( pMsg->GetType() == TYPE_TARG_DATA_ITEM1 ) { } else if( pMsg->GetType() == TYPE_TARG_DATA_ITEM2 ) { } else if( pMsg->GetType() == TYPE_TARG_DATA_ITEM3 ) { } else if(pMsg->GetType() == TYPE_DATA_ITEM_ACK) { switch(pMsg->Buf8[2]) { //decode Data acks case 0: //NetSDR and SDRIP keepalive ack break; case 1: //ack of AD6620 load so ok to send next msg if any left to send if( m_AD6620.GetNext6620Msg(m_TxMsg) ) SendAscpMsg(&m_TxMsg); else qDebug()<<"AD6620 Load Complete"; break; case 2: break; case 3: break; } } } ///////////////////////////////////////////////////////////////////// // Called to ack a data item ///////////////////////////////////////////////////////////////////// void CSdrInterface::SendAck(quint8 chan) { m_TxMsg.InitTxMsg(TYPE_DATA_ITEM_ACK); m_TxMsg.AddParm8(chan); SendAscpMsg(&m_TxMsg); } void CSdrInterface::SetRx2Parameters(double Rx2Gain, double Rx2Phase) { quint16 gain; quint32 phase; // qDebug()<<"Rx2Gain = "<ChangeUserDataRate( m_Demodulator.GetOutputRate()); qDebug()<<"UsrDataRate="<< m_Demodulator.GetOutputRate(); } /////////////////////////////////////////////////////////////////////////////// //called to change demodulation parameters /////////////////////////////////////////////////////////////////////////////// void CSdrInterface::SetDemod(int Mode, tDemodInfo CurrentDemodInfo) { m_Demodulator.SetDemod(Mode, CurrentDemodInfo ); m_pSoundCardOut->ChangeUserDataRate( m_Demodulator.GetOutputRate()); qDebug()<<"UsrDataRate="<< m_Demodulator.GetOutputRate(); } /////////////////////////////////////////////////////////////////////////////// //called to change Noise Processing parameters /////////////////////////////////////////////////////////////////////////////// void CSdrInterface::SetupNoiseProc(tNoiseProcdInfo* pNoiseProcSettings) { m_NoiseProc.SetupBlanker( pNoiseProcSettings->NBOn, pNoiseProcSettings->NBThreshold, pNoiseProcSettings->NBWidth, m_SampleRate); } /////////////////////////////////////////////////////////////////////////////// //Set Display FFT average value /////////////////////////////////////////////////////////////////////////////// void CSdrInterface::SetFftAve(qint32 ave) { m_FftAve = ave; m_Fft.SetFFTAve(ave); } //////////////////////////////////////////////////////////////////////// // Called to read/set/start calibration of the NCO Spur Offset value. //////////////////////////////////////////////////////////////////////// void CSdrInterface::ManageNCOSpurOffsets( eNCOSPURCMD cmd, double* pNCONullValueI, double* pNCONullValueQ) { m_NcoSpurCalActive = FALSE; switch(cmd) { case NCOSPUR_CMD_SET: if( (NULL!=pNCONullValueI) && (NULL!=pNCONullValueQ) ) { m_NCOSpurOffsetI = *pNCONullValueI; m_NCOSpurOffsetQ = *pNCONullValueQ; //qDebug()<<"Cal Set"<< m_NCOSpurOffsetI << m_NCOSpurOffsetQ; } break; case NCOSPUR_CMD_STARTCAL: if((m_NCOSpurOffsetI>10.0) || (m_NCOSpurOffsetI<-10.0)) m_NCOSpurOffsetI = 0.0; if((m_NCOSpurOffsetQ>10.0) || (m_NCOSpurOffsetQ<-10.0)) m_NCOSpurOffsetQ = 0.0; m_NcoSpurCalCount = 0; m_NcoSpurCalActive = TRUE; //qDebug()<<"Start NCO Cal"; break; case NCOSPUR_CMD_READ: if( (NULL!=pNCONullValueI) && (NULL!=pNCONullValueQ) ) { *pNCONullValueI = m_NCOSpurOffsetI; *pNCONullValueQ = m_NCOSpurOffsetQ; //qDebug()<<"Cal Read"<< m_NCOSpurOffsetI << m_NCOSpurOffsetQ; } break; default: break; } } //////////////////////////////////////////////////////////////////////// // Called to calculate the NCO Spur Offset value from the incoming m_DataBuf. //////////////////////////////////////////////////////////////////////// void CSdrInterface::NcoSpurCalibrate(double* pData, qint32 length) { if( m_NcoSpurCalCount < SPUR_CAL_MAXSAMPLES) { for( int i=0; iCreateGeneratorSamples(Length/2, (TYPECPX*)pIQData, m_SampleRate); m_NoiseProc.ProcessBlanker(Length/2, (TYPECPX*)pIQData, (TYPECPX*)pIQData); if(m_NcoSpurCalActive) //if performing NCO spur calibration NcoSpurCalibrate(pIQData, Length); //accumulate samples into m_DataBuf until have enough to perform an FFT for(int i=0; i= (m_FftSize*2) ) { m_FftBufPos = 0; if(++m_DisplaySkipCounter >= m_DisplaySkipValue ) { m_DisplaySkipCounter = 0; if(m_ScreenUpateFinished) { m_Fft.PutInDisplayFFT(m_FftSize, (TYPECPX*)m_DataBuf); m_ScreenUpateFinished = FALSE; emit NewFftData(); } } } } TYPECPX SoundBuf[8192]; int n; if(m_StereoOut) { n = m_Demodulator.ProcessData(Length/2, (TYPECPX*)pIQData, SoundBuf); m_pSoundCardOut->PutOutQueue(n, SoundBuf); } else { n = m_Demodulator.ProcessData(Length/2, (TYPECPX*)pIQData, (TYPEREAL*)SoundBuf); m_pSoundCardOut->PutOutQueue(n, (TYPEREAL*)SoundBuf); } } cutesdr-1.0.5/interface/ascpmsg.h0000644000175000017500000000436311546075374016716 0ustar bottomsbottoms////////////////////////////////////////////////////////////////////// // ascpmsg.h: interface/implementation for the CAscpMsg class. // // Helper Class to aid in creating and decoding ASCP format msgs // // History: // 2010-09-15 Initial creation MSW // 2011-03-27 Initial release ///////////////////////////////////////////////////////////////////// #ifndef ASCPMSG_H #define ASCPMSG_H #include #include "interface/protocoldefs.h" #define MAX_ASCPMSG_LENGTH (8192+2) class CAscpMsg { public: CAscpMsg(){Length=0;} // For creating msgs to Tx // Call InitTxMsg with msg type to start creation // Add CItem and parameters, msg length is automatically created in hdr void InitTxMsg(quint8 type){Buf16[0] = type<<8; Buf16[0]+=2;} void AddCItem(quint16 item){Buf16[1] = item; Buf16[0]+=2;} void AddParm8(quint8 parm8){Buf8[ Buf16[0]&LENGTH_MASK ] = parm8; Buf16[0]++;} void AddParm16(quint16 parm16){Buf8[ (Buf16[0]&LENGTH_MASK) ] = parm16&0xFF; Buf16[0]++; Buf8[ (Buf16[0]&LENGTH_MASK) ] = (parm16>>8)&0xFF;Buf16[0]++;} void AddParm32(quint32 parm32){Buf8[ (Buf16[0]&LENGTH_MASK) ] = parm32&0xFF; Buf16[0]++; Buf8[ (Buf16[0]&LENGTH_MASK) ] = (parm32>>8)&0xFF;Buf16[0]++; Buf8[ (Buf16[0]&LENGTH_MASK) ] = (parm32>>16)&0xFF;Buf16[0]++; Buf8[ (Buf16[0]&LENGTH_MASK) ] = (parm32>>24)&0xFF;Buf16[0]++;} // For reading msgs // Put raw received bytes in Buf8[] // Call InitRxMsg before reading msg // Call Get type,CItem, Length as needed // Call Getparmx() in sequence to read msg parameters in msg order. void InitRxMsg(){Length = 4;} quint8 GetType(){return Buf8[1]&TYPE_MASK;} quint16 GetLength(){return Buf16[0]&LENGTH_MASK;} quint16 GetCItem(){return Buf16[1];} quint8 GetParm8(){return Buf8[Length++];} quint16 GetParm16(){tmp16 = Buf8[Length++]; tmp16 |= (quint16)(Buf8[Length++])<<8; return tmp16; } quint32 GetParm32(){tmp32 = Buf8[Length++]; tmp32 |= (quint32)(Buf8[Length++])<<8; tmp32 |= (quint32)(Buf8[Length++])<<16; tmp32 |= (quint32)(Buf8[Length++])<<24; return tmp32; } union { quint8 Buf8[MAX_ASCPMSG_LENGTH]; quint16 Buf16[MAX_ASCPMSG_LENGTH/2]; }; private: quint16 Length; quint16 tmp16; quint32 tmp32; }; #endif // ASCPMSG_H cutesdr-1.0.5/interface/perform.cpp0000644000175000017500000001334711711303214017245 0ustar bottomsbottoms/////////////////////////////////////////////////////// // Perform.cpp : implementation file // // NOTE:: these functions depend on the 64 bit performance timer // found in Pentium processors. This timer may not be present // in some of the non-Intel versions. // Also it may not work with multi core CPU's across threads. // // History: // 2010-11-10 Initial creation MSW // 2011-03-27 Initial release // 2011-04-26 Added define to remove assembly from compile //////////////////////////////////////////////////////////////////////// #define USE_PERFORMANCE //uncomment to use //========================================================================================== // + + + This Software is released under the "Simplified BSD License" + + + //Copyright 2010 Moe Wheatley. All rights reserved. // //Redistribution and use in source and binary forms, with or without modification, are //permitted provided that the following conditions are met: // // 1. Redistributions of source code must retain the above copyright notice, this list of // conditions and the following disclaimer. // // 2. Redistributions in binary form must reproduce the above copyright notice, this list // of conditions and the following disclaimer in the documentation and/or other materials // provided with the distribution. // //THIS SOFTWARE IS PROVIDED BY Moe Wheatley ``AS IS'' AND ANY EXPRESS OR IMPLIED //WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND //FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL Moe Wheatley OR //CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR //CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR //SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON //ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING //NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF //ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // //The views and conclusions contained in the software and documentation are those of the //authors and should not be interpreted as representing official policies, either expressed //or implied, of Moe Wheatley. //========================================================================================== #include "interface/perform.h" #include #include #define CPUCLOCKRATEGHZ 3 //manually set to CPU core clock speed ///////////////////////////////////////////////////////////////////////////// static quint64 StartTime; static quint64 StopTime; static quint64 DeltaTime; static quint64 CountFreq; static quint64 DeltaTimeMax; static quint64 DeltaTimeMin; static quint64 DeltaTimeAve; static quint64 DeltaSamples; static quint64 Length; //////////////// Time measuring routine using Pentium timer static quint64 QueryPerformanceCounter() { quint64 val=0; #ifdef USE_PERFORMANCE quint32 eax, edx; __asm__ __volatile__("cpuid": : : "ax", "bx", "cx", "dx"); __asm__ __volatile__("rdtsc":"=a"(eax), "=d"(edx)); val = edx; val = val << 32; val += eax; #endif return val; } // call to initialize the prformance timer void InitPerformance() { Length = 0; DeltaTimeMax = 0; DeltaTimeAve = 0; DeltaSamples = 0; DeltaTimeMin = 0x7FFFFFF; CountFreq = CPUCLOCKRATEGHZ; } // Starts the performance timer void StartPerformance() { StartTime = QueryPerformanceCounter(); } // Stop performance timer and calculate timing values void StopPerformance(int n) { StopTime = QueryPerformanceCounter(); DeltaTime = (StopTime-StartTime); DeltaTimeAve += DeltaTime; DeltaSamples++; if( DeltaTime>DeltaTimeMax ) { DeltaTimeMax = DeltaTime; } if( DeltaTimeDeltaTimeMax ) { DeltaTimeMax = DeltaTime; } if( DeltaTime1e6) { qDebug()<<"Delta Time Max mSec = "< #define DC_ALPHA 0.99 //ALPHA for DC removal filter ~20Hz Fcut with 15625Hz Sample Rate #define PLL_BW 100.0 //natural frequency ~ loop bandwidth #define PLL_ZETA .707 //PLL Loop damping factor #define PLL_LIMIT 1000.0 //+- frequency limit in Hz ///////////////////////////////////////////////////////////////////////////////// // Construct SAM demod object ///////////////////////////////////////////////////////////////////////////////// CSamDemod::CSamDemod(TYPEREAL samplerate) : m_SampleRate(samplerate) { m_y1 = 0.0; m_z1 = 0.0; m_NcoPhase = 0.0; m_NcoFreq = 0.0; TYPEREAL norm = K_2PI/m_SampleRate; m_NcoLLimit = -PLL_LIMIT * norm; //clamp SAM NCO to +-1KHz m_NcoHLimit = PLL_LIMIT * norm; m_PllAlpha = 2.0*PLL_ZETA*PLL_BW * norm; m_PllBeta = (m_PllAlpha * m_PllAlpha)/(4.0*PLL_ZETA*PLL_ZETA); //create complex lowpass filter pair with LP cuttoff of 5000Hz and transition width of 1000Hz // 40dB stop band attenuation m_Fir.InitLPFilter(0, 1.0, 40.0, 4500, 5500,m_SampleRate); //apply transform to shift the LP filter 5000Hz so now the filter is // a 0 to 10000Hz Hilbert bandpass filter with 90 degree phase shift m_Fir.GenerateHBFilter(5000.0); } ///////////////////////////////////////////////////////////////////////////////// // Process SAM demod MONO version ///////////////////////////////////////////////////////////////////////////////// int CSamDemod::ProcessData(int InLength, TYPECPX* pInData, TYPEREAL* pOutData) { TYPECPX tmp; for(int i=0; iDisplayData(1, &test, m_SampleRate,PROFILE_6); m_NcoFreq += (m_PllBeta * phzerror); // radians per sampletime //clamp NCO frequency so doesn't drift out of lock range if(m_NcoFreq > m_NcoHLimit) m_NcoFreq = m_NcoHLimit; else if(m_NcoFreq < m_NcoLLimit) m_NcoFreq = m_NcoLLimit; //update NCO phase with new value m_NcoPhase += (m_NcoFreq + m_PllAlpha * phzerror); //High pass filter(DC removal) with IIR filter // H(z) = (1 - z^-1)/(1 - ALPHA*z^-1) TYPEREAL z0 = tmp.re + (m_z1 * DC_ALPHA); //just use real output for mono pOutData[i] = (z0 - m_z1); m_z1 = z0; } m_NcoPhase = fmod(m_NcoPhase, K_2PI); //keep radian counter bounded return InLength; } ///////////////////////////////////////////////////////////////////////////////// // Process SAM demod STEREO version ///////////////////////////////////////////////////////////////////////////////// int CSamDemod::ProcessData(int InLength, TYPECPX* pInData, TYPECPX* pOutData) { TYPECPX tmp; for(int i=0; iDisplayData(1, &test, m_SampleRate,PROFILE_6); m_NcoFreq += (m_PllBeta * phzerror); // radians per sampletime //clamp NCO frequency so doesn't drift out of lock range if(m_NcoFreq > m_NcoHLimit) m_NcoFreq = m_NcoHLimit; else if(m_NcoFreq < m_NcoLLimit) m_NcoFreq = m_NcoLLimit; //update NCO phase with new value m_NcoPhase += (m_NcoFreq + m_PllAlpha * phzerror); //High pass filter(DC removal) with IIR filter // H(z) = (1 - z^-1)/(1 - ALPHA*z^-1) TYPEREAL z0 = tmp.re + (m_z1 * DC_ALPHA); TYPEREAL y0 = tmp.im + (m_y1 * DC_ALPHA); pOutData[i].re = (z0 - m_z1); pOutData[i].im = (y0 - m_y1); m_y1 = y0; m_z1 = z0; } m_NcoPhase = fmod(m_NcoPhase, K_2PI); //keep radian counter bounded //process I/Q with bandpass filter with 90deg phase shift between the I and Q filters m_Fir.ProcessFilter(InLength, pOutData, pOutData); for(int i=0; iGetStereoLock(pPilotLock); else return false;} int GetNextRdsGroupData(tRDS_GROUPS* pGroupData) { if(m_pWFmDemod) return m_pWFmDemod->GetNextRdsGroupData(pGroupData); else return false; } private: void DeleteAllDemods(); CDownConvert m_DownConvert; CFastFIR m_FastFIR; CAgc m_Agc; CSMeter m_SMeter; QMutex m_Mutex; //for keeping threads from stomping on each other tDemodInfo m_DemodInfo; TYPEREAL m_InputRate; TYPEREAL m_DownConverterOutputRate; TYPEREAL m_DemodOutputRate; TYPEREAL m_DesiredMaxOutputBandwidth; TYPECPX* m_pDemodInBuf; TYPECPX* m_pDemodTmpBuf; TYPEREAL m_CW_Offset; bool m_USFm; int m_DemodMode; int m_InBufPos; int m_InBufLimit; int m_AGClength; int m_AGChang; int m_AGChangTimer; //pointers to all the various implemented demodulator classes CAmDemod* m_pAmDemod; CSamDemod* m_pSamDemod; CFmDemod* m_pFmDemod; CWFmDemod* m_pWFmDemod; CSsbDemod* m_pSsbDemod; //includes CW modes }; #endif // DEMODULATOR_H cutesdr-1.0.5/dsp/datatypes.h0000644000175000017500000000207011711303214016053 0ustar bottomsbottoms////////////////////////////////////////////////////////////////////// // datatypes.h: Common data type declarations // // History: // 2010-09-15 Initial creation MSW // 2011-03-27 Initial release ////////////////////////////////////////////////////////////////////// #ifndef DATATYPES_H #define DATATYPES_H #include #include //define single or double precision reals and complex types typedef float tSReal; typedef double tDReal; typedef struct _sCplx { tSReal re; tSReal im; }tSComplex; typedef struct _dCplx { tDReal re; tDReal im; }tDComplex; typedef struct _isCplx { qint16 re; qint16 im; }tStereo16; #define TYPEREAL tDReal #define TYPECPX tDComplex #define TYPESTEREO16 tStereo16 #define TYPEMONO16 qint16 //#define K_2PI (8.0*atan(1)) //maybe some compilers are't too smart to optimize out #define K_2PI (2.0 * 3.14159265358979323846) #define K_PI (3.14159265358979323846) #define K_PI4 (K_PI/4.0) #define K_PI2 (K_PI/2.0) #define K_3PI4 (3.0*K_PI4) #endif // DATATYPES_H cutesdr-1.0.5/dsp/fft.cpp0000644000175000017500000010111511546075374015212 0ustar bottomsbottoms// fft.cpp: implementation of the CFft class. // This is a somewhat modified version of Takuya OOURA's // original radix 4 FFT package. //Copyright(C) 1996-1998 Takuya OOURA // (email: ooura@mmm.t.u-tokyo.ac.jp). // // History: // 2010-09-15 Initial creation MSW // 2011-03-27 Initial release ////////////////////////////////////////////////////////////////////// #include #include "dsp/fft.h" #include ////////////////////////////////////////////////////////////////////// // Local Defines ////////////////////////////////////////////////////////////////////// #define K_AMPMAX 32767.0 //maximum sin wave Pk for 16 bit input data #define K_MAXDB 0.0 //specifies total range of FFT #define K_MINDB -220.0 #define OVER_LIMIT 32000.0 //limit for detecting over ranging inputs ////////////////////////////////////////////////////////////////////// // Construction/Destruction ////////////////////////////////////////////////////////////////////// CFft::CFft() { m_Overload = FALSE; m_Invert = FALSE; m_AveSize = 1; m_LastFFTSize = 0; m_AveCount = 0; m_TotalCount = 0; m_FFTSize = 1024; m_pWorkArea = NULL; m_pSinCosTbl = NULL; m_pWindowTbl = NULL; m_pFFTPwrAveBuf = NULL; m_pFFTAveBuf = NULL; m_pFFTInBuf = NULL; m_pFFTSumBuf = NULL; m_pTranslateTbl = NULL; m_dBCompensation = K_MAXDB; SetFFTParams( 2048, FALSE ,0.0, 1000); SetFFTAve( 1); } CFft::~CFft() { // free all resources FreeMemory(); } void CFft::FreeMemory() { if(m_pWorkArea) { delete m_pWorkArea; m_pWorkArea = NULL; } if(m_pSinCosTbl) { delete m_pSinCosTbl; m_pSinCosTbl = NULL; } if(m_pWindowTbl) { delete m_pWindowTbl; m_pWindowTbl = NULL; } if(m_pFFTPwrAveBuf) { delete m_pFFTPwrAveBuf; m_pFFTPwrAveBuf = NULL; } if(m_pFFTAveBuf) { delete m_pFFTAveBuf; m_pFFTAveBuf = NULL; } if(m_pFFTSumBuf) { delete m_pFFTSumBuf; m_pFFTSumBuf = NULL; } if(m_pFFTInBuf) { delete m_pFFTInBuf; m_pFFTInBuf = NULL; } if(m_pTranslateTbl) { delete m_pTranslateTbl; m_pTranslateTbl = NULL; } } /////////////////////////////////////////////////////////////////// //FFT initialization and parameter setup function /////////////////////////////////////////////////////////////////// void CFft::SetFFTAve( qint32 ave) { if(m_AveSize != ave) { if(ave>0) m_AveSize = ave; else m_AveSize = 1; } ResetFFT(); } /////////////////////////////////////////////////////////////////// //FFT initialization and parameter setup function /////////////////////////////////////////////////////////////////// void CFft::SetFFTParams( qint32 size, bool invert, double dBCompensation, double SampleFreq) { qint32 i; if(size==0) return; m_Mutex.lock(); m_BinMin = 0; //force recalculation of plot variables m_BinMax = 0; m_StartFreq = 0; m_StopFreq = 0; m_PlotWidth = 0; m_Invert = invert; m_SampleFreq = SampleFreq; if( m_dBCompensation != dBCompensation ) { //reset of FFT params m_LastFFTSize = 0; m_dBCompensation = dBCompensation; } if( sizeMAX_FFT_SIZE ) m_FFTSize = MAX_FFT_SIZE; else m_FFTSize = size; if(m_LastFFTSize != m_FFTSize) { m_LastFFTSize = m_FFTSize; FreeMemory(); m_pWindowTbl = new double[m_FFTSize]; m_pSinCosTbl = new double[m_FFTSize/2]; m_pWorkArea = new qint32[ (qint32)sqrt((double)m_FFTSize)+2]; m_pFFTPwrAveBuf = new double[m_FFTSize]; m_pFFTAveBuf = new double[m_FFTSize]; m_pFFTSumBuf = new double[m_FFTSize]; for(i=0; i> K_C // Then K_B = PdBmax - 20*log10( N*A/Kx ) // K_C = 10 ^ ( (PdBmin-K_B)/10 ) // for power range of 0 to 100 dB with input(A) of 32767 and N=262144 // K_B = -86.63833 and K_C = 4.6114145e8 // To eliminate the multiply by 10, divide by 10 so for an output // range of 0 to -120dB the stored value is 0.0 to -12.0 // so final constant K_B = -8.663833 /////////////////////////////////////////////////////////////////////// m_K_B = m_dBCompensation - 20*log10( (double)m_FFTSize*K_AMPMAX/2.0 ); m_K_C = pow( 10.0, (K_MINDB-m_K_B)/10.0 ); m_K_B = m_K_B/10.0; double WindowGain; #if 0 WindowGain = 1.0; for(i=0; i OVER_LIMIT ) //flag overload if within OVLimit of max m_Overload = TRUE; dtmp1 = m_pWindowTbl[i]; //NOTE: For some reason I and Q are swapped(demod I/Q does not apear to be swapped) //possibly an issue with the FFT ? ((TYPECPX*)m_pFFTInBuf)[i].im = dtmp1 * (InBuf[i].re);//window the I data ((TYPECPX*)m_pFFTInBuf)[i].re = dtmp1 * (InBuf[i].im); //window the Q data } //Calculate the complex FFT bitrv2(m_FFTSize*2, m_pWorkArea + 2, m_pFFTInBuf); CpxFFT(m_FFTSize*2, m_pFFTInBuf, m_pSinCosTbl); m_Mutex.unlock(); return m_TotalCount; } ////////////////////////////////////////////////////////////////////// // The bin range is "start" to "stop" Hz. // The range of start to stop frequencies are mapped to the users // plot screen size so the users buffer will be filled with an array // of integers whos value is the pixel height and the index of the // array is the x pixel coordinate. // The function returns TRUE if the input is overloaded // This routine converts the data to 32 bit integers and is useful // when displaying fft data on the screen. // MaxHeight = Plot height in pixels(zero is top and increases down) // MaxWidth = Plot width in pixels // StartFreq = freq in Hz // StopFreq = freq in Hz // MaxdB = FFT dB level corresponding to output value == 0 // must be <= to K_MAXDB // MindB = FFT dB level corresponding to output value == MaxHeight // must be >= to K_MINDB ////////////////////////////////////////////////////////////////////// bool CFft::GetScreenIntegerFFTData(qint32 MaxHeight, qint32 MaxWidth, double MaxdB, double MindB, qint32 StartFreq, qint32 StopFreq, qint32* OutBuf ) { qint32 i; qint32 y; qint32 x; qint32 m; qint32 ymax = 10000; qint32 xprev = -1; qint32 maxbin; double dBmaxOffset = MaxdB/10.0; double dBGainFactor = -10.0/(MaxdB-MindB); m_Mutex.lock(); if( (m_StartFreq != StartFreq) || (m_StopFreq != StopFreq) || (m_PlotWidth != MaxWidth) ) { //if something has changed need to redo translate table m_StartFreq = StartFreq; m_StopFreq = StopFreq; m_PlotWidth = MaxWidth; maxbin = m_FFTSize - 1; m_BinMin = (qint32)((double)StartFreq*(double)m_FFTSize/m_SampleFreq); m_BinMin += (m_FFTSize/2); m_BinMax = (qint32)((double)StopFreq*(double)m_FFTSize/m_SampleFreq); m_BinMax += (m_FFTSize/2); if(m_BinMin < 0) //don't allow these go outside the translate table m_BinMin = 0; if(m_BinMin >= maxbin) m_BinMin = maxbin; if(m_BinMax < 0) m_BinMax = 0; if(m_BinMax >= maxbin) m_BinMax = maxbin; if( (m_BinMax-m_BinMin) > m_PlotWidth ) { //if more FFT points than plot points for( i=m_BinMin; i<=m_BinMax; i++) m_pTranslateTbl[i] = ( (i-m_BinMin)*m_PlotWidth )/(m_BinMax - m_BinMin); } else { //if more plot points than FFT points for( i=0; i m_PlotWidth ) { //if more FFT points than plot points for( i=m_BinMin; i<=m_BinMax; i++ ) { if(m_Invert) y = (qint32)((double)MaxHeight*dBGainFactor*(m_pFFTAveBuf[(m-i)] - dBmaxOffset)); else y = (qint32)((double)MaxHeight*dBGainFactor*(m_pFFTAveBuf[i] - dBmaxOffset)); if(y<0) y = 0; if(y > MaxHeight) y = MaxHeight; x = m_pTranslateTbl[i]; //get fft bin to plot x coordinate transform if( x==xprev ) // still mappped to same fft bin coordinate { if(y < ymax) //store only the max value { OutBuf[x] = y; ymax = y; } } else { OutBuf[x] = y; xprev = x; ymax = y; } } } else { //if more plot points than FFT points for( x=0; x MaxHeight) y = MaxHeight; OutBuf[x] = y; } } m_Mutex.unlock(); return m_Overload; } /////////////////////////////////////////////////////////////////// //Interface for doing fast convolution filters. Takes complex data // in pInOutBuf and does fwd or rev FFT and places back in same buffer. /////////////////////////////////////////////////////////////////// void CFft::FwdFFT( TYPECPX* pInOutBuf) { bitrv2(m_FFTSize*2, m_pWorkArea + 2, (TYPEREAL*)pInOutBuf); CpxFFT(m_FFTSize*2, (TYPEREAL*)pInOutBuf, m_pSinCosTbl); } void CFft::RevFFT( TYPECPX* pInOutBuf) { bitrv2conj(m_FFTSize*2, m_pWorkArea + 2, (TYPEREAL*)pInOutBuf); cftbsub(m_FFTSize*2, (TYPEREAL*)pInOutBuf, m_pSinCosTbl); } /////////////////////////////////////////////////////////////////// // Nitty gritty fft routines by Takuya OOURA(Updated to his new version 4-18-02) // Routine calculates real FFT /////////////////////////////////////////////////////////////////// void CFft::rftfsub(qint32 n, double *a, qint32 nc, double *c) { qint32 j, k, kk, ks, m; double wkr, wki, xr, xi, yr, yi; m_TotalCount++; if(m_AveCount < m_AveSize) m_AveCount++; m = n >> 1; ks = 2 * nc/m; kk = 0; for (j = 2; j < m; j += 2 ) { k = n - j; kk += ks; wkr = 0.5 - c[nc - kk]; wki = c[kk]; xr = a[j] - a[k]; xi = a[j + 1] + a[k + 1]; yr = wkr * xr - wki * xi; yi = wkr * xi + wki * xr; a[j] -= yr; xi = a[j]*a[j]; a[j+1] -= yi; xi += ( a[j+1]*a[j+1]); a[k] += yr; xr = a[k]*a[k]; a[k+1] -= yi; xr += (a[k+1]*a[k+1]); //xr is real power xi is imag power terms //perform moving average on power up to m_AveSize then do exponential averaging after that if(m_TotalCount <= m_AveSize) { m_pFFTSumBuf[j] = m_pFFTSumBuf[j] + xi; m_pFFTSumBuf[k] = m_pFFTSumBuf[k] + xr; } else { m_pFFTSumBuf[j] = m_pFFTSumBuf[j] - m_pFFTPwrAveBuf[j] + xi; m_pFFTSumBuf[k] = m_pFFTSumBuf[k] - m_pFFTPwrAveBuf[k] + xr; } m_pFFTPwrAveBuf[j] = m_pFFTSumBuf[j]/(double)m_AveCount; m_pFFTPwrAveBuf[k] = m_pFFTSumBuf[k]/(double)m_AveCount; m_pFFTAveBuf[j] = log10(m_pFFTPwrAveBuf[j] + m_K_C) + m_K_B; m_pFFTAveBuf[k] = log10(m_pFFTPwrAveBuf[k] + m_K_C) + m_K_B; } a[0] *= a[0]; //calc DC term xr = a[m]*a[m]+a[m+1]*a[m+1]; //calculate N/4(middle) term //xr is real power a[0] is imag power terms //perform moving average on power up to m_AveSize then do exponential averaging after that if(m_TotalCount <= m_AveSize) { m_pFFTSumBuf[0] = m_pFFTSumBuf[0] + a[0]; m_pFFTSumBuf[n/2] = m_pFFTSumBuf[n/2] + xr; } else { m_pFFTSumBuf[0] = m_pFFTSumBuf[0] - m_pFFTPwrAveBuf[0] + a[0]; m_pFFTSumBuf[n/2] = m_pFFTSumBuf[n/2] - m_pFFTPwrAveBuf[n/2] + xr; } m_pFFTPwrAveBuf[0] = m_pFFTSumBuf[0]/(double)m_AveCount; m_pFFTPwrAveBuf[n/2] = m_pFFTSumBuf[n/2]/(double)m_AveCount; m_pFFTAveBuf[0] = log10(m_pFFTPwrAveBuf[0] + m_K_C) + m_K_B; m_pFFTAveBuf[n/2] = log10(m_pFFTPwrAveBuf[n/2] + m_K_C) + m_K_B; } /////////////////////////////////////////////////////////////////// // Routine calculates complex FFT /////////////////////////////////////////////////////////////////// void CFft::CpxFFT(qint32 n, double *a, double *w) { qint32 j, j1, j2, j3, l; double x0r, x0i, x1r, x1i, x2r, x2i, x3r, x3i; m_TotalCount++; if(m_AveCount < m_AveSize) m_AveCount++; l = 2; if (n > 8) { cft1st(n, a, w); l = 8; while ((l << 2) < n) { cftmdl(n, l, a, w); l <<= 2; } } if ((l << 2) == n) { for (j = 0; j < l; j += 2) { j1 = j + l; j2 = j1 + l; j3 = j2 + l; x0r = a[j] + a[j1]; x0i = a[j + 1] + a[j1 + 1]; x1r = a[j] - a[j1]; x1i = a[j + 1] - a[j1 + 1]; x2r = a[j2] + a[j3]; x2i = a[j2 + 1] + a[j3 + 1]; x3r = a[j2] - a[j3]; x3i = a[j2 + 1] - a[j3 + 1]; a[j] = x0r + x2r; a[j + 1] = x0i + x2i; a[j2] = x0r - x2r; a[j2 + 1] = x0i - x2i; a[j1] = x1r - x3i; a[j1 + 1] = x1i + x3r; a[j3] = x1r + x3i; a[j3 + 1] = x1i - x3r; } } else { for (j = 0; j < l; j += 2) { j1 = j + l; x0r = a[j] - a[j1]; x0i = a[j + 1] - a[j1 + 1]; a[j] += a[j1]; a[j + 1] += a[j1 + 1]; a[j1] = x0r; a[j1 + 1] = x0i; } } //n = 2*FFTSIZE n = n>>1; //now n = FFTSIZE // FFT output index 0 to N/2-1 // is frequency output 0 to +Fs/2 Hz ( 0 Hz DC term ) for( l=0,j=n/2; j 2) { nwh = nw >> 1; delta = atan(1.0) / nwh; w[0] = 1; w[1] = 0; w[nwh] = cos(delta * nwh); w[nwh + 1] = w[nwh]; if (nwh > 2) { for (j = 2; j < nwh; j += 2) { x = cos(delta * j); y = sin(delta * j); w[j] = x; w[j + 1] = y; w[nw - j] = y; w[nw - j + 1] = x; } bitrv2(nw, ip + 2, w); } } } /////////////////////////////////////////////////////////////////// void CFft::makect(qint32 nc, qint32 *ip, double *c) { qint32 j, nch; double delta; ip[1] = nc; if (nc > 1) { nch = nc >> 1; delta = atan(1.0) / nch; c[0] = cos(delta * nch); c[nch] = 0.5 * c[0]; for (j = 1; j < nch; j++) { c[j] = 0.5 * cos(delta * j); c[nc - j] = 0.5 * sin(delta * j); } } } /////////////////////////////////////////////////////////////////// /* -------- child routines -------- */ /////////////////////////////////////////////////////////////////// void CFft::bitrv2(qint32 n, qint32 *ip, double *a) { qint32 j, j1, k, k1, l, m, m2; double xr, xi, yr, yi; ip[0] = 0; l = n; m = 1; while ((m << 3) < l) { l >>= 1; for (j = 0; j < m; j++) { ip[m + j] = ip[j] + l; } m <<= 1; } m2 = 2 * m; if ((m << 3) == l) { for (k = 0; k < m; k++) { for (j = 0; j < k; j++) { j1 = 2 * j + ip[k]; k1 = 2 * k + ip[j]; xr = a[j1]; xi = a[j1 + 1]; yr = a[k1]; yi = a[k1 + 1]; a[j1] = yr; a[j1 + 1] = yi; a[k1] = xr; a[k1 + 1] = xi; j1 += m2; k1 += 2 * m2; xr = a[j1]; xi = a[j1 + 1]; yr = a[k1]; yi = a[k1 + 1]; a[j1] = yr; a[j1 + 1] = yi; a[k1] = xr; a[k1 + 1] = xi; j1 += m2; k1 -= m2; xr = a[j1]; xi = a[j1 + 1]; yr = a[k1]; yi = a[k1 + 1]; a[j1] = yr; a[j1 + 1] = yi; a[k1] = xr; a[k1 + 1] = xi; j1 += m2; k1 += 2 * m2; xr = a[j1]; xi = a[j1 + 1]; yr = a[k1]; yi = a[k1 + 1]; a[j1] = yr; a[j1 + 1] = yi; a[k1] = xr; a[k1 + 1] = xi; } j1 = 2 * k + m2 + ip[k]; k1 = j1 + m2; xr = a[j1]; xi = a[j1 + 1]; yr = a[k1]; yi = a[k1 + 1]; a[j1] = yr; a[j1 + 1] = yi; a[k1] = xr; a[k1 + 1] = xi; } } else { for (k = 1; k < m; k++) { for (j = 0; j < k; j++) { j1 = 2 * j + ip[k]; k1 = 2 * k + ip[j]; xr = a[j1]; xi = a[j1 + 1]; yr = a[k1]; yi = a[k1 + 1]; a[j1] = yr; a[j1 + 1] = yi; a[k1] = xr; a[k1 + 1] = xi; j1 += m2; k1 += m2; xr = a[j1]; xi = a[j1 + 1]; yr = a[k1]; yi = a[k1 + 1]; a[j1] = yr; a[j1 + 1] = yi; a[k1] = xr; a[k1 + 1] = xi; } } } } /////////////////////////////////////////////////////////////////// void CFft::cftfsub(qint32 n, double *a, double *w) { qint32 j, j1, j2, j3, l; double x0r, x0i, x1r, x1i, x2r, x2i, x3r, x3i; l = 2; if (n > 8) { cft1st(n, a, w); l = 8; while ((l << 2) < n) { cftmdl(n, l, a, w); l <<= 2; } } if ((l << 2) == n) { for (j = 0; j < l; j += 2) { j1 = j + l; j2 = j1 + l; j3 = j2 + l; x0r = a[j] + a[j1]; x0i = a[j + 1] + a[j1 + 1]; x1r = a[j] - a[j1]; x1i = a[j + 1] - a[j1 + 1]; x2r = a[j2] + a[j3]; x2i = a[j2 + 1] + a[j3 + 1]; x3r = a[j2] - a[j3]; x3i = a[j2 + 1] - a[j3 + 1]; a[j] = x0r + x2r; a[j + 1] = x0i + x2i; a[j2] = x0r - x2r; a[j2 + 1] = x0i - x2i; a[j1] = x1r - x3i; a[j1 + 1] = x1i + x3r; a[j3] = x1r + x3i; a[j3 + 1] = x1i - x3r; } } else { for (j = 0; j < l; j += 2) { j1 = j + l; x0r = a[j] - a[j1]; x0i = a[j + 1] - a[j1 + 1]; a[j] += a[j1]; a[j + 1] += a[j1 + 1]; a[j1] = x0r; a[j1 + 1] = x0i; } } } /////////////////////////////////////////////////////////////////// void CFft::cft1st(qint32 n, double *a, double *w) { qint32 j, k1, k2; double wk1r, wk1i, wk2r, wk2i, wk3r, wk3i; double x0r, x0i, x1r, x1i, x2r, x2i, x3r, x3i; x0r = a[0] + a[2]; x0i = a[1] + a[3]; x1r = a[0] - a[2]; x1i = a[1] - a[3]; x2r = a[4] + a[6]; x2i = a[5] + a[7]; x3r = a[4] - a[6]; x3i = a[5] - a[7]; a[0] = x0r + x2r; a[1] = x0i + x2i; a[4] = x0r - x2r; a[5] = x0i - x2i; a[2] = x1r - x3i; a[3] = x1i + x3r; a[6] = x1r + x3i; a[7] = x1i - x3r; wk1r = w[2]; x0r = a[8] + a[10]; x0i = a[9] + a[11]; x1r = a[8] - a[10]; x1i = a[9] - a[11]; x2r = a[12] + a[14]; x2i = a[13] + a[15]; x3r = a[12] - a[14]; x3i = a[13] - a[15]; a[8] = x0r + x2r; a[9] = x0i + x2i; a[12] = x2i - x0i; a[13] = x0r - x2r; x0r = x1r - x3i; x0i = x1i + x3r; a[10] = wk1r * (x0r - x0i); a[11] = wk1r * (x0r + x0i); x0r = x3i + x1r; x0i = x3r - x1i; a[14] = wk1r * (x0i - x0r); a[15] = wk1r * (x0i + x0r); k1 = 0; for (j = 16; j < n; j += 16) { k1 += 2; k2 = 2 * k1; wk2r = w[k1]; wk2i = w[k1 + 1]; wk1r = w[k2]; wk1i = w[k2 + 1]; wk3r = wk1r - 2 * wk2i * wk1i; wk3i = 2 * wk2i * wk1r - wk1i; x0r = a[j] + a[j + 2]; x0i = a[j + 1] + a[j + 3]; x1r = a[j] - a[j + 2]; x1i = a[j + 1] - a[j + 3]; x2r = a[j + 4] + a[j + 6]; x2i = a[j + 5] + a[j + 7]; x3r = a[j + 4] - a[j + 6]; x3i = a[j + 5] - a[j + 7]; a[j] = x0r + x2r; a[j + 1] = x0i + x2i; x0r -= x2r; x0i -= x2i; a[j + 4] = wk2r * x0r - wk2i * x0i; a[j + 5] = wk2r * x0i + wk2i * x0r; x0r = x1r - x3i; x0i = x1i + x3r; a[j + 2] = wk1r * x0r - wk1i * x0i; a[j + 3] = wk1r * x0i + wk1i * x0r; x0r = x1r + x3i; x0i = x1i - x3r; a[j + 6] = wk3r * x0r - wk3i * x0i; a[j + 7] = wk3r * x0i + wk3i * x0r; wk1r = w[k2 + 2]; wk1i = w[k2 + 3]; wk3r = wk1r - 2 * wk2r * wk1i; wk3i = 2 * wk2r * wk1r - wk1i; x0r = a[j + 8] + a[j + 10]; x0i = a[j + 9] + a[j + 11]; x1r = a[j + 8] - a[j + 10]; x1i = a[j + 9] - a[j + 11]; x2r = a[j + 12] + a[j + 14]; x2i = a[j + 13] + a[j + 15]; x3r = a[j + 12] - a[j + 14]; x3i = a[j + 13] - a[j + 15]; a[j + 8] = x0r + x2r; a[j + 9] = x0i + x2i; x0r -= x2r; x0i -= x2i; a[j + 12] = -wk2i * x0r - wk2r * x0i; a[j + 13] = -wk2i * x0i + wk2r * x0r; x0r = x1r - x3i; x0i = x1i + x3r; a[j + 10] = wk1r * x0r - wk1i * x0i; a[j + 11] = wk1r * x0i + wk1i * x0r; x0r = x1r + x3i; x0i = x1i - x3r; a[j + 14] = wk3r * x0r - wk3i * x0i; a[j + 15] = wk3r * x0i + wk3i * x0r; } } /////////////////////////////////////////////////////////////////// void CFft::cftmdl(qint32 n, qint32 l, double *a, double *w) { qint32 j, j1, j2, j3, k, k1, k2, m, m2; double wk1r, wk1i, wk2r, wk2i, wk3r, wk3i; double x0r, x0i, x1r, x1i, x2r, x2i, x3r, x3i; m = l << 2; for (j = 0; j < l; j += 2) { j1 = j + l; j2 = j1 + l; j3 = j2 + l; x0r = a[j] + a[j1]; x0i = a[j + 1] + a[j1 + 1]; x1r = a[j] - a[j1]; x1i = a[j + 1] - a[j1 + 1]; x2r = a[j2] + a[j3]; x2i = a[j2 + 1] + a[j3 + 1]; x3r = a[j2] - a[j3]; x3i = a[j2 + 1] - a[j3 + 1]; a[j] = x0r + x2r; a[j + 1] = x0i + x2i; a[j2] = x0r - x2r; a[j2 + 1] = x0i - x2i; a[j1] = x1r - x3i; a[j1 + 1] = x1i + x3r; a[j3] = x1r + x3i; a[j3 + 1] = x1i - x3r; } wk1r = w[2]; for (j = m; j < l + m; j += 2) { j1 = j + l; j2 = j1 + l; j3 = j2 + l; x0r = a[j] + a[j1]; x0i = a[j + 1] + a[j1 + 1]; x1r = a[j] - a[j1]; x1i = a[j + 1] - a[j1 + 1]; x2r = a[j2] + a[j3]; x2i = a[j2 + 1] + a[j3 + 1]; x3r = a[j2] - a[j3]; x3i = a[j2 + 1] - a[j3 + 1]; a[j] = x0r + x2r; a[j + 1] = x0i + x2i; a[j2] = x2i - x0i; a[j2 + 1] = x0r - x2r; x0r = x1r - x3i; x0i = x1i + x3r; a[j1] = wk1r * (x0r - x0i); a[j1 + 1] = wk1r * (x0r + x0i); x0r = x3i + x1r; x0i = x3r - x1i; a[j3] = wk1r * (x0i - x0r); a[j3 + 1] = wk1r * (x0i + x0r); } k1 = 0; m2 = 2 * m; for (k = m2; k < n; k += m2) { k1 += 2; k2 = 2 * k1; wk2r = w[k1]; wk2i = w[k1 + 1]; wk1r = w[k2]; wk1i = w[k2 + 1]; wk3r = wk1r - 2 * wk2i * wk1i; wk3i = 2 * wk2i * wk1r - wk1i; for (j = k; j < l + k; j += 2) { j1 = j + l; j2 = j1 + l; j3 = j2 + l; x0r = a[j] + a[j1]; x0i = a[j + 1] + a[j1 + 1]; x1r = a[j] - a[j1]; x1i = a[j + 1] - a[j1 + 1]; x2r = a[j2] + a[j3]; x2i = a[j2 + 1] + a[j3 + 1]; x3r = a[j2] - a[j3]; x3i = a[j2 + 1] - a[j3 + 1]; a[j] = x0r + x2r; a[j + 1] = x0i + x2i; x0r -= x2r; x0i -= x2i; a[j2] = wk2r * x0r - wk2i * x0i; a[j2 + 1] = wk2r * x0i + wk2i * x0r; x0r = x1r - x3i; x0i = x1i + x3r; a[j1] = wk1r * x0r - wk1i * x0i; a[j1 + 1] = wk1r * x0i + wk1i * x0r; x0r = x1r + x3i; x0i = x1i - x3r; a[j3] = wk3r * x0r - wk3i * x0i; a[j3 + 1] = wk3r * x0i + wk3i * x0r; } wk1r = w[k2 + 2]; wk1i = w[k2 + 3]; wk3r = wk1r - 2 * wk2r * wk1i; wk3i = 2 * wk2r * wk1r - wk1i; for (j = k + m; j < l + (k + m); j += 2) { j1 = j + l; j2 = j1 + l; j3 = j2 + l; x0r = a[j] + a[j1]; x0i = a[j + 1] + a[j1 + 1]; x1r = a[j] - a[j1]; x1i = a[j + 1] - a[j1 + 1]; x2r = a[j2] + a[j3]; x2i = a[j2 + 1] + a[j3 + 1]; x3r = a[j2] - a[j3]; x3i = a[j2 + 1] - a[j3 + 1]; a[j] = x0r + x2r; a[j + 1] = x0i + x2i; x0r -= x2r; x0i -= x2i; a[j2] = -wk2i * x0r - wk2r * x0i; a[j2 + 1] = -wk2i * x0i + wk2r * x0r; x0r = x1r - x3i; x0i = x1i + x3r; a[j1] = wk1r * x0r - wk1i * x0i; a[j1 + 1] = wk1r * x0i + wk1i * x0r; x0r = x1r + x3i; x0i = x1i - x3r; a[j3] = wk3r * x0r - wk3i * x0i; a[j3 + 1] = wk3r * x0i + wk3i * x0r; } } } void CFft::bitrv2conj(int n, int *ip, TYPEREAL *a) { int j, j1, k, k1, l, m, m2; TYPEREAL xr, xi, yr, yi; ip[0] = 0; l = n; m = 1; while ((m << 3) < l) { l >>= 1; for (j = 0; j < m; j++) { ip[m + j] = ip[j] + l; } m <<= 1; } m2 = 2 * m; if ((m << 3) == l) { for (k = 0; k < m; k++) { for (j = 0; j < k; j++) { j1 = 2 * j + ip[k]; k1 = 2 * k + ip[j]; xr = a[j1]; xi = -a[j1 + 1]; yr = a[k1]; yi = -a[k1 + 1]; a[j1] = yr; a[j1 + 1] = yi; a[k1] = xr; a[k1 + 1] = xi; j1 += m2; k1 += 2 * m2; xr = a[j1]; xi = -a[j1 + 1]; yr = a[k1]; yi = -a[k1 + 1]; a[j1] = yr; a[j1 + 1] = yi; a[k1] = xr; a[k1 + 1] = xi; j1 += m2; k1 -= m2; xr = a[j1]; xi = -a[j1 + 1]; yr = a[k1]; yi = -a[k1 + 1]; a[j1] = yr; a[j1 + 1] = yi; a[k1] = xr; a[k1 + 1] = xi; j1 += m2; k1 += 2 * m2; xr = a[j1]; xi = -a[j1 + 1]; yr = a[k1]; yi = -a[k1 + 1]; a[j1] = yr; a[j1 + 1] = yi; a[k1] = xr; a[k1 + 1] = xi; } k1 = 2 * k + ip[k]; a[k1 + 1] = -a[k1 + 1]; j1 = k1 + m2; k1 = j1 + m2; xr = a[j1]; xi = -a[j1 + 1]; yr = a[k1]; yi = -a[k1 + 1]; a[j1] = yr; a[j1 + 1] = yi; a[k1] = xr; a[k1 + 1] = xi; k1 += m2; a[k1 + 1] = -a[k1 + 1]; } } else { a[1] = -a[1]; a[m2 + 1] = -a[m2 + 1]; for (k = 1; k < m; k++) { for (j = 0; j < k; j++) { j1 = 2 * j + ip[k]; k1 = 2 * k + ip[j]; xr = a[j1]; xi = -a[j1 + 1]; yr = a[k1]; yi = -a[k1 + 1]; a[j1] = yr; a[j1 + 1] = yi; a[k1] = xr; a[k1 + 1] = xi; j1 += m2; k1 += m2; xr = a[j1]; xi = -a[j1 + 1]; yr = a[k1]; yi = -a[k1 + 1]; a[j1] = yr; a[j1 + 1] = yi; a[k1] = xr; a[k1 + 1] = xi; } k1 = 2 * k + ip[k]; a[k1 + 1] = -a[k1 + 1]; a[k1 + m2 + 1] = -a[k1 + m2 + 1]; } } } void CFft::cftbsub(int n, TYPEREAL *a, TYPEREAL *w) { int j, j1, j2, j3, l; TYPEREAL x0r, x0i, x1r, x1i, x2r, x2i, x3r, x3i; l = 2; if (n > 8) { cft1st(n, a, w); l = 8; while ((l << 2) < n) { cftmdl(n, l, a, w); l <<= 2; } } if ((l << 2) == n) { for (j = 0; j < l; j += 2) { j1 = j + l; j2 = j1 + l; j3 = j2 + l; x0r = a[j] + a[j1]; x0i = -a[j + 1] - a[j1 + 1]; x1r = a[j] - a[j1]; x1i = -a[j + 1] + a[j1 + 1]; x2r = a[j2] + a[j3]; x2i = a[j2 + 1] + a[j3 + 1]; x3r = a[j2] - a[j3]; x3i = a[j2 + 1] - a[j3 + 1]; a[j] = x0r + x2r; a[j + 1] = x0i - x2i; a[j2] = x0r - x2r; a[j2 + 1] = x0i + x2i; a[j1] = x1r - x3i; a[j1 + 1] = x1i - x3r; a[j3] = x1r + x3i; a[j3 + 1] = x1i + x3r; } } else { for (j = 0; j < l; j += 2) { j1 = j + l; x0r = a[j] - a[j1]; x0i = -a[j + 1] + a[j1 + 1]; a[j] += a[j1]; a[j + 1] = -a[j + 1] - a[j1 + 1]; a[j1] = x0r; a[j1 + 1] = x0i; } } } cutesdr-1.0.5/dsp/iir.h0000644000175000017500000000230311546075374014662 0ustar bottomsbottoms////////////////////////////////////////////////////////////////////// // iir.h: interface for the CIir class. // // This class implements an IIR filter // // History: // 2011-02-05 Initial creation MSW // 2011-03-27 Initial release ////////////////////////////////////////////////////////////////////// #ifndef IIR_H #define IIR_H #include "dsp/datatypes.h" class CIir { public: CIir(); void InitLP( TYPEREAL F0Freq, TYPEREAL FilterQ, TYPEREAL SampleRate); //create Low Pass void InitHP( TYPEREAL F0Freq, TYPEREAL FilterQ, TYPEREAL SampleRate); //create High Pass void InitBP( TYPEREAL F0Freq, TYPEREAL FilterQ, TYPEREAL SampleRate); //create Band Pass void InitBR( TYPEREAL F0Freq, TYPEREAL FilterQ, TYPEREAL SampleRate); //create Band Reject void ProcessFilter(int InLength, TYPEREAL* InBuf, TYPEREAL* OutBuf); void ProcessFilter(int InLength, TYPECPX* InBuf, TYPECPX* OutBuf); private: TYPEREAL m_SampleRate; TYPEREAL m_A1; //direct form 2 coefficients TYPEREAL m_A2; TYPEREAL m_B0; TYPEREAL m_B1; TYPEREAL m_B2; TYPEREAL m_w1a; //biquad delay storage TYPEREAL m_w2a; TYPEREAL m_w1b; //biquad delay storage TYPEREAL m_w2b; }; #endif // IIR_H cutesdr-1.0.5/dsp/wfmmod.h0000644000175000017500000000337211711303214015354 0ustar bottomsbottoms////////////////////////////////////////////////////////////////////// // wfmmod.h: interface for the CWFmMod class. // // History: // 2011-08-18 Initial creation MSW // 2011-08-18 Initial release ///////////////////////////////////////////////////////////////////// #ifndef WFMMOD_H #define WFMMOD_H #include "dsp/datatypes.h" #include "dsp/fir.h" #include "dsp/iir.h" #define RDSBUF_SIZE 16384 #define MAX_RDS_DATA 2000 //max number of blocks that can be stored class CWFmMod { public: CWFmMod(); void GenerateData(int InLength,TYPEREAL Amplitude, TYPECPX* pOutData); void SetSampleRate(TYPEREAL SampleRate); void SetSweep(TYPEREAL SweepFreqNorm, TYPEREAL SweepFrequency, TYPEREAL SweepStopFrequency, TYPEREAL SweepRateInc); private: void InitRDS(); void CreateRdsGroup(quint16 Blk1, quint16 Blk2, quint16 Blk3, quint16 Blk4); quint32 CreateBlockWithCheckword(quint16 Data, quint32 BlockOffset); void CreateRdsSamples(int InLength , TYPEREAL* pBuf); double CreateNextRdsBit(); double m_DeviationRate; double m_ModAcc; double m_PilotAcc; double m_PilotInc; double m_LeftAcc; double m_LeftInc; double m_RightAcc; double m_RightInc; double m_LeftAmp; double m_RightAmp; double m_SweepFrequency; double m_SweepFreqNorm; double m_SweepStopFrequency; double m_SweepRateInc; double m_SampleRate; int StateTimer; int TimerPeriod; int ModState; TYPEREAL m_RdsPulseCoef[RDSBUF_SIZE]; TYPEREAL m_RdsOut[RDSBUF_SIZE]; double m_RdsPulseLength; double m_RdsTime; double m_RdsSamplePeriod; double m_RdsTimeToIdx; int m_RdsD1; int m_RdsD2; int m_RdsBufPos; int m_RdsBufLength; int m_RdsLastBit; quint32 m_RdsBitPtr; quint32 m_RdsDataBuf[MAX_RDS_DATA]; }; #endif // WFMMOD_H cutesdr-1.0.5/dsp/fir.cpp0000644000175000017500000005123011711303214015172 0ustar bottomsbottoms////////////////////////////////////////////////////////////////////// // fir.cpp: implementation of the CFir class. // // This class implements a FIR filter using a double flat coefficient //array to eliminate testing for buffer wrap around. // //Filter coefficients can be from a fixed table or this class will create // a lowpass or highpass filter from frequency and attenuation specifications // using a Kaiser-Bessel windowed sinc algorithm // // History: // 2011-01-29 Initial creation MSW // 2011-03-27 Initial release // 2011-08-07 Modified FIR filter initialization to force fixed size ////////////////////////////////////////////////////////////////////// //========================================================================================== // + + + This Software is released under the "Simplified BSD License" + + + //Copyright 2010 Moe Wheatley. All rights reserved. // //Redistribution and use in source and binary forms, with or without modification, are //permitted provided that the following conditions are met: // // 1. Redistributions of source code must retain the above copyright notice, this list of // conditions and the following disclaimer. // // 2. Redistributions in binary form must reproduce the above copyright notice, this list // of conditions and the following disclaimer in the documentation and/or other materials // provided with the distribution. // //THIS SOFTWARE IS PROVIDED BY Moe Wheatley ``AS IS'' AND ANY EXPRESS OR IMPLIED //WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND //FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL Moe Wheatley OR //CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR //CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR //SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON //ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING //NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF //ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // //The views and conclusions contained in the software and documentation are those of the //authors and should not be interpreted as representing official policies, either expressed //or implied, of Moe Wheatley. //========================================================================================== #include "fir.h" #include #include #include ////////////////////////////////////////////////////////////////////// // Local Defines ////////////////////////////////////////////////////////////////////// #define MAX_HALF_BAND_BUFSIZE 8192 ///////////////////////////////////////////////////////////////////////////////// // Construct CFir object ///////////////////////////////////////////////////////////////////////////////// CFir::CFir() { m_NumTaps = 1; m_State = 0; } ///////////////////////////////////////////////////////////////////////////////// // Process InLength InBuf[] samples and place in OutBuf[] // Note the Coefficient array is twice the length and has a duplicated set // in order to eliminate testing for buffer wrap in the inner loop // ex: if 3 tap FIR with coefficients{21,-43,15} is made into a array of 6 entries // {21, -43, 15, 21, -43, 15 } //REAL version ///////////////////////////////////////////////////////////////////////////////// void CFir::ProcessFilter(int InLength, TYPEREAL* InBuf, TYPEREAL* OutBuf) { TYPEREAL acc; TYPEREAL* Zptr; const double* Hptr; m_Mutex.lock(); for(int i=0; iMAX_NUMCOEF) m_NumTaps = MAX_NUMCOEF; else m_NumTaps = NumTaps; for(int i=0; iMAX_NUMCOEF) m_NumTaps = MAX_NUMCOEF; else m_NumTaps = NumTaps; for(int i=0; i= 50.0) Beta = .1102 * (Astop - 8.71); else Beta = .5842 * pow( (Astop-20.96), 0.4) + .07886 * (Astop - 20.96); //Now Estimate number of filter taps required based on filter specs m_NumTaps = (Astop - 8.0) / (2.285*K_2PI*(normFstop - normFpass) ) + 1; //clamp range of filter taps if(m_NumTaps > MAX_NUMCOEF ) m_NumTaps = MAX_NUMCOEF; if(m_NumTaps < 3) m_NumTaps = 3; if(NumTaps) //if need to force to to a number of taps m_NumTaps = NumTaps; TYPEREAL fCenter = .5*(TYPEREAL)(m_NumTaps-1); TYPEREAL izb = Izero(Beta); //precalculate denominator since is same for all points for( n=0; n < m_NumTaps; n++) { TYPEREAL x = (TYPEREAL)n - fCenter; TYPEREAL c; // create ideal Sinc() LP filter with normFcut if( (TYPEREAL)n == fCenter ) //deal with odd size filter singularity where sin(0)/0==1 c = 2.0 * normFcut; else c = (TYPEREAL)sin(K_2PI*x*normFcut)/(K_PI*x); //calculate Kaiser window and multiply to get coefficient x = ((TYPEREAL)n - ((TYPEREAL)m_NumTaps-1.0)/2.0 ) / (((TYPEREAL)m_NumTaps-1.0)/2.0); m_Coef[n] = Scale * c * Izero( Beta * sqrt(1 - (x*x) ) ) / izb; } //make a 2x length array for FIR flat calculation efficiency for (n = 0; n < m_NumTaps; n++) m_Coef[n+m_NumTaps] = m_Coef[n]; //copy into complex coef buffers for (n = 0; n < m_NumTaps*2; n++) { m_ICoef[n] = m_Coef[n]; m_QCoef[n] = m_Coef[n]; } //Initialize the FIR buffers and state for(int i=0; i class CFastFIR { public: CFastFIR(); virtual ~CFastFIR(); void SetupParameters( TYPEREAL FLoCut,TYPEREAL FHiCut,TYPEREAL Offset, TYPEREAL SampleRate); int ProcessData(int InLength, TYPECPX* InBuf, TYPECPX* OutBuf); private: void CpxMpy(int N, TYPECPX* m, TYPECPX* src, TYPECPX* dest); void FreeMemory(); TYPEREAL m_FLoCut; TYPEREAL m_FHiCut; TYPEREAL m_Offset; TYPEREAL m_SampleRate; int m_InBufInPos; TYPEREAL* m_pWindowTbl; TYPECPX* m_pFFTOverlapBuf; TYPECPX* m_pFilterCoef; TYPECPX* m_pFFTBuf; QMutex m_Mutex; //for keeping threads from stomping on each other CFft m_Fft; }; #endif // FASTFIR_H cutesdr-1.0.5/dsp/smeter.h0000644000175000017500000000130511546075374015377 0ustar bottomsbottoms////////////////////////////////////////////////////////////////////// // smeter.h: interface for the CSMeter class. // // History: // 2010-09-22 Initial creation MSW // 2011-03-27 Initial release ///////////////////////////////////////////////////////////////////// #ifndef SMETER_H #define SMETER_H #include "dsp/datatypes.h" class CSMeter { public: CSMeter(); void ProcessData(int length, TYPECPX* pInData, TYPEREAL SampleRate); TYPEREAL GetPeak(); TYPEREAL GetAve(); private: TYPEREAL m_AverageMag; TYPEREAL m_PeakMag; TYPEREAL m_SampleRate; TYPEREAL m_AttackAve; TYPEREAL m_DecayAve; TYPEREAL m_AttackAlpha; TYPEREAL m_DecayAlpha; }; #endif // SMETER_H cutesdr-1.0.5/dsp/fractresampler.h0000644000175000017500000000234411546075374017116 0ustar bottomsbottoms////////////////////////////////////////////////////////////////////// // fractresampler.h: interface for the CFractResampler class. // // This class implements a fractional resampler that can be used to //convert between different sample rates // // History: // 2010-09-15 Initial creation MSW // 2011-03-27 Initial release ////////////////////////////////////////////////////////////////////// #ifndef FRACTRESAMPLER_H #define FRACTRESAMPLER_H #include "dsp/datatypes.h" class CFractResampler { public: CFractResampler(); virtual ~CFractResampler(); void Init(int MaxInputSize); //overloaded functions for processing different data types int Resample( int InLength, TYPEREAL Rate, TYPEREAL* pInBuf, TYPEREAL* pOutBuf); int Resample( int InLength, TYPEREAL Rate, TYPECPX* pInBuf, TYPECPX* pOutBuf); int Resample( int InLength, TYPEREAL Rate, TYPEREAL* pInBuf, TYPEMONO16* pOutBuf, TYPEREAL gain); int Resample( int InLength, TYPEREAL Rate, TYPECPX* pInBuf, TYPESTEREO16* pOutBuf, TYPEREAL gain); private: TYPEREAL m_FloatTime; //floating pt output time accumulator TYPEREAL* m_pSinc; //ptr to sinc table TYPECPX* m_pInputBuf; //internal working input sample buffer }; #endif // FRACTRESAMPLER_H cutesdr-1.0.5/dsp/demodulator.cpp0000644000175000017500000002766511711303214016750 0ustar bottomsbottoms///////////////////////////////////////////////////////////////////// // demodulator.cpp: implementation of the Cdemodulator class. // // This class implements the demodulation DSP functionality to take //raw I/Q data from the radio, shift to baseband, decimate, demodulate, //perform AGC, and send the audio to the sound card. // // History: // 2010-09-15 Initial creation MSW // 2011-03-27 Initial release ///////////////////////////////////////////////////////////////////// //========================================================================================== // + + + This Software is released under the "Simplified BSD License" + + + //Copyright 2010 Moe Wheatley. All rights reserved. // //Redistribution and use in source and binary forms, with or without modification, are //permitted provided that the following conditions are met: // // 1. Redistributions of source code must retain the above copyright notice, this list of // conditions and the following disclaimer. // // 2. Redistributions in binary form must reproduce the above copyright notice, this list // of conditions and the following disclaimer in the documentation and/or other materials // provided with the distribution. // //THIS SOFTWARE IS PROVIDED BY Moe Wheatley ``AS IS'' AND ANY EXPRESS OR IMPLIED //WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND //FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL Moe Wheatley OR //CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR //CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR //SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON //ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING //NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF //ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // //The views and conclusions contained in the software and documentation are those of the //authors and should not be interpreted as representing official policies, either expressed //or implied, of Moe Wheatley. //========================================================================================== #include "dsp/demodulator.h" #include "gui/testbench.h" #include ////////////////////////////////////////////////////////////////// // Constructor/Destructor ////////////////////////////////////////////////////////////////// CDemodulator::CDemodulator() { m_DesiredMaxOutputBandwidth = 48000.0; m_DownConverterOutputRate = 48000.0; m_DemodOutputRate = 48000.0; m_pDemodInBuf = new TYPECPX[MAX_INBUFSIZE]; m_pDemodTmpBuf = new TYPECPX[MAX_INBUFSIZE]; m_InBufPos = 0; m_InBufLimit = 1000; m_DemodMode = -1; m_pAmDemod = NULL; m_pSamDemod = NULL; m_pSsbDemod = NULL; m_pFmDemod = NULL; m_pWFmDemod = NULL; m_USFm = true; SetDemodFreq(0.0); } CDemodulator::~CDemodulator() { DeleteAllDemods(); if(m_pDemodInBuf) delete m_pDemodInBuf; if(m_pDemodTmpBuf) delete m_pDemodTmpBuf; } ////////////////////////////////////////////////////////////////// // Deletes all demod objects ////////////////////////////////////////////////////////////////// void CDemodulator::DeleteAllDemods() { if(m_pAmDemod) delete m_pAmDemod; if(m_pSamDemod) delete m_pSamDemod; if(m_pFmDemod) delete m_pFmDemod; if(m_pWFmDemod) delete m_pWFmDemod; if(m_pSsbDemod) delete m_pSsbDemod; m_pAmDemod = NULL; m_pSamDemod = NULL; m_pFmDemod = NULL; m_pWFmDemod = NULL; m_pSsbDemod = NULL; } ////////////////////////////////////////////////////////////////// // Called to set/change the demodulator input sample rate ////////////////////////////////////////////////////////////////// void CDemodulator::SetInputSampleRate(TYPEREAL InputRate) { if(m_InputRate != InputRate) { m_InputRate = InputRate; //change any demod parameters that may occur with sample rate change switch(m_DemodMode) { case DEMOD_FM: m_DownConverterOutputRate = m_DownConvert.SetDataRate(m_InputRate, m_DesiredMaxOutputBandwidth); m_DemodOutputRate = m_DownConverterOutputRate; if(m_pFmDemod) m_pFmDemod->SetSampleRate(m_DownConverterOutputRate); break; case DEMOD_WFM: m_DownConverterOutputRate = m_DownConvert.SetWfmDataRate(m_InputRate, 100000); if(m_pWFmDemod) m_DemodOutputRate = m_pWFmDemod->SetSampleRate(m_DownConverterOutputRate, m_USFm); break; case DEMOD_AM: case DEMOD_SAM: case DEMOD_USB: case DEMOD_LSB: case DEMOD_CWU: case DEMOD_CWL: m_DownConverterOutputRate = m_DownConvert.SetDataRate(m_InputRate, m_DesiredMaxOutputBandwidth); m_DemodOutputRate = m_DownConverterOutputRate; break; } } } ////////////////////////////////////////////////////////////////// // Called to set/change the active Demod object //or if a demod parameter or filter parameter changes ////////////////////////////////////////////////////////////////// void CDemodulator::SetDemod(int Mode, tDemodInfo CurrentDemodInfo) { m_Mutex.lock(); m_DemodInfo = CurrentDemodInfo; if(m_DemodMode != Mode) //do only if changes { DeleteAllDemods(); //remove current demod object m_DemodMode = Mode; //create decimation chain and get output sample rate if((DEMOD_LSB == m_DemodMode) || (DEMOD_CWL == m_DemodMode) ) m_DesiredMaxOutputBandwidth = -m_DemodInfo.LowCutmin; else m_DesiredMaxOutputBandwidth = m_DemodInfo.HiCutmax; //now create correct demodulator switch(m_DemodMode) { case DEMOD_AM: m_DownConverterOutputRate = m_DownConvert.SetDataRate(m_InputRate, m_DesiredMaxOutputBandwidth); m_pAmDemod = new CAmDemod(m_DownConverterOutputRate); m_DemodOutputRate = m_DownConverterOutputRate; break; case DEMOD_SAM: m_DownConverterOutputRate = m_DownConvert.SetDataRate(m_InputRate, m_DesiredMaxOutputBandwidth); m_pSamDemod = new CSamDemod(m_DownConverterOutputRate); m_DemodOutputRate = m_DownConverterOutputRate; break; case DEMOD_FM: m_DownConverterOutputRate = m_DownConvert.SetDataRate(m_InputRate, m_DesiredMaxOutputBandwidth); m_pFmDemod = new CFmDemod(m_DownConverterOutputRate); m_DemodOutputRate = m_DownConverterOutputRate; break; case DEMOD_WFM: m_DownConverterOutputRate = m_DownConvert.SetWfmDataRate(m_InputRate, 100000); m_pWFmDemod = new CWFmDemod(m_DownConverterOutputRate); m_DemodOutputRate = m_pWFmDemod->GetDemodRate(); break; case DEMOD_USB: case DEMOD_LSB: case DEMOD_CWU: case DEMOD_CWL: m_DownConverterOutputRate = m_DownConvert.SetDataRate(m_InputRate, m_DesiredMaxOutputBandwidth); m_pSsbDemod = new CSsbDemod(); m_DemodOutputRate = m_DownConverterOutputRate; break; } } m_CW_Offset = m_DemodInfo.Offset; m_DownConvert.SetCwOffset(m_CW_Offset); if(m_DemodMode != DEMOD_WFM) m_FastFIR.SetupParameters(m_DemodInfo.LowCut, m_DemodInfo.HiCut,m_CW_Offset,m_DownConverterOutputRate); m_Agc.SetParameters(m_DemodInfo.AgcOn, m_DemodInfo.AgcHangOn, m_DemodInfo.AgcThresh, m_DemodInfo.AgcManualGain, m_DemodInfo.AgcSlope, m_DemodInfo.AgcDecay, m_DownConverterOutputRate); if( m_pFmDemod != NULL) m_pFmDemod->SetSquelch(m_DemodInfo.SquelchValue); if(m_pAmDemod != NULL) m_pAmDemod->SetBandwidth( (m_DemodInfo.HiCut-m_DemodInfo.LowCut)/2.0); //set input buffer limit so that decimated output is abt 10mSec or more of data m_InBufLimit = (m_DemodOutputRate/100.0) * m_InputRate/m_DemodOutputRate; //process abt .01sec of output samples at a time m_InBufLimit &= 0xFFFFFF00; //keep modulo 256 since decimation is only in power of 2 m_Mutex.unlock(); qDebug()<<"m_InputRate="< #include #include ////////////////////////////////////////////////////////////////////// // Local defines ////////////////////////////////////////////////////////////////////// #define SINC_PERIOD_PTS 10000 //number of points in sinc table between "zero crossings" //smaller value increases noise floor #define SINC_PERIODS 28 //number of input sample periods("zero crossings"-1) in //sinc function(should be even) //decreasing reduces alias free bandwidth #define SINC_LENGTH ( (SINC_PERIODS)*SINC_PERIOD_PTS + 1)//number of total points in sinc table #define MAX_SOUNDCARDVAL 32767.0 ////////////////////////////////////////////////////////////////////// // Construction/Destruction ////////////////////////////////////////////////////////////////////// CFractResampler::CFractResampler() { m_pSinc = NULL; m_pInputBuf = NULL; } CFractResampler::~CFractResampler() { if(m_pSinc) delete m_pSinc; if(m_pInputBuf) delete m_pInputBuf; } // ////////////////////////////////////////////////////////////////////// // Initialize resampler memory and create windowed sinc table // MaxInputSize is the largest number of input samples expected to be processed ////////////////////////////////////////////////////////////////////// void CFractResampler::Init(int MaxInputSize) { int i; TYPEREAL fi; TYPEREAL window; MaxInputSize += SINC_PERIODS; //expand buffer size to include wrap around if(NULL == m_pSinc) m_pSinc = new TYPEREAL[SINC_LENGTH]; if(m_pInputBuf) delete m_pInputBuf; m_pInputBuf = new TYPECPX[MaxInputSize]; for(i=0; i MAX_SOUNDCARDVAL) tmp.re = MAX_SOUNDCARDVAL; if(tmp.re < -MAX_SOUNDCARDVAL) tmp.re = -MAX_SOUNDCARDVAL; if(tmp.im>MAX_SOUNDCARDVAL) tmp.im = MAX_SOUNDCARDVAL; if(tmp.im < -MAX_SOUNDCARDVAL) tmp.im = -MAX_SOUNDCARDVAL; pOutBuf[outsamples].re = (qint16)tmp.re; pOutBuf[outsamples++].im = (qint16)tmp.im; m_FloatTime += dt; //inc floating pt output time step IntegerTime = (int)m_FloatTime; //truncate to integer } m_FloatTime -= (double)InLength; //move floating time position back for next call //keeping leftover fraction //need to copy last SINC_PERIODS input samples in buffer to beginning of buffer // for FIR wrap around management. j points to last input sample processed j = InLength; for(i=0; i MAX_SOUNDCARDVAL) tmp = MAX_SOUNDCARDVAL; if(tmp < -MAX_SOUNDCARDVAL) tmp = -MAX_SOUNDCARDVAL; pOutBuf[outsamples++] = (TYPEMONO16)tmp; m_FloatTime += dt; IntegerTime = (int)m_FloatTime; } m_FloatTime -= (double)InLength; //move floating time position back for next call //keeping leftover fraction //need to copy last SINC_PERIODS input samples in buffer to beginning of buffer // for FIR wrap around management. j points to last input sample processed j = InLength; for(i=0; i //////////// //class for FIR Filters //////////// class CFir { public: CFir(); void InitConstFir( int NumTaps, const double* pCoef, TYPEREAL Fsamprate); void InitConstFir( int NumTaps, const double* pICoef, const double* pQCoef, TYPEREAL Fsamprate); int InitLPFilter(int NumTaps, TYPEREAL Scale, TYPEREAL Astop, TYPEREAL Fpass, TYPEREAL Fstop, TYPEREAL Fsamprate); int InitHPFilter(int NumTaps, TYPEREAL Scale, TYPEREAL Astop, TYPEREAL Fpass, TYPEREAL Fstop, TYPEREAL Fsamprate); void GenerateHBFilter( TYPEREAL FreqOffset); void ProcessFilter(int InLength, TYPEREAL* InBuf, TYPEREAL* OutBuf); void ProcessFilter(int InLength, TYPEREAL* InBuf, TYPECPX* OutBuf); void ProcessFilter(int InLength, TYPECPX* InBuf, TYPECPX* OutBuf); private: TYPEREAL Izero(TYPEREAL x); TYPEREAL m_SampleRate; int m_NumTaps; int m_State; TYPEREAL m_Coef[MAX_NUMCOEF*2]; TYPEREAL m_ICoef[MAX_NUMCOEF*2]; TYPEREAL m_QCoef[MAX_NUMCOEF*2]; TYPEREAL m_rZBuf[MAX_NUMCOEF]; TYPECPX m_cZBuf[MAX_NUMCOEF]; QMutex m_Mutex; //for keeping threads from stomping on each other }; //////////// //class for the Half Band decimate by 2 FIR filters //////////// class CDecimateBy2 { public: CDecimateBy2(int len, const TYPEREAL* pCoef); ~CDecimateBy2(){if(m_pHBFirRBuf) delete m_pHBFirRBuf; if(m_pHBFirCBuf) delete m_pHBFirCBuf;} int DecBy2(int InLength, TYPEREAL* pInData, TYPEREAL* pOutData); int DecBy2(int InLength, TYPECPX* pInData, TYPECPX* pOutData); TYPEREAL* m_pHBFirRBuf; TYPECPX* m_pHBFirCBuf; int m_FirLength; const TYPEREAL* m_pCoef; }; #endif // FIR_H cutesdr-1.0.5/dsp/ssbdemod.h0000644000175000017500000000107511546075374015704 0ustar bottomsbottoms////////////////////////////////////////////////////////////////////// // ssbdemod.h: interface for the CSsbDemod class. // // History: // 2010-09-22 Initial creation MSW // 2011-03-27 Initial release ///////////////////////////////////////////////////////////////////// #ifndef SSBDEMOD_H #define SSBDEMOD_H #include "dsp/datatypes.h" class CSsbDemod { public: CSsbDemod(); int ProcessData(int InLength, TYPECPX* pInData, TYPEREAL* pOutData); int ProcessData(int InLength, TYPECPX* pInData, TYPECPX* pOutData); private: }; #endif // SSBDEMOD_H cutesdr-1.0.5/dsp/wfmmod.cpp0000644000175000017500000002705511711303214015713 0ustar bottomsbottoms// wfmdemod.cpp: implementation of the CWFmMod class. // // This class Wideband stereo FM modulation // // History: // 2011-08-18 Initial creation MSW // 2011-08-18 Initial release ////////////////////////////////////////////////////////////////////// //========================================================================================== // + + + This Software is released under the "Simplified BSD License" + + + //Copyright 2011 Moe Wheatley. All rights reserved. // //Redistribution and use in source and binary forms, with or without modification, are //permitted provided that the following conditions are met: // // 1. Redistributions of source code must retain the above copyright notice, this list of // conditions and the following disclaimer. // // 2. Redistributions in binary form must reproduce the above copyright notice, this list // of conditions and the following disclaimer in the documentation and/or other materials // provided with the distribution. // //THIS SOFTWARE IS PROVIDED BY Moe Wheatley ``AS IS'' AND ANY EXPRESS OR IMPLIED //WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND //FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL Moe Wheatley OR //CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR //CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR //SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON //ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING //NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF //ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // //The views and conclusions contained in the software and documentation are those of the //authors and should not be interpreted as representing official policies, either expressed //or implied, of Moe Wheatley. //========================================================================================== #include "wfmmod.h" #include "gui/testbench.h" #include "dsp/datatypes.h" #include "dsp/rbdsconstants.h" #include #include #include #define PILOT_FREQ 19000.0 #define DEVIATION_FREQ 75000.0 #define PILOT_LEVEL 0.1 #define AUDIO_LEVEL 0.9 #define RDS_LEVEL 0.0267 #define LEFT_FREQ 440.0 #define RIGHT_FREQ 666.0 #define STATE_TIME 2 //seconds #define RDS_PICODE 0x75C8 //WMOE #define RDS_GRPTYPE_0A (0<<12) //Basic tuning info #define RDS_PTYCODE (13<<5) //Program type nostalgia ///////////////////////////////////////////////////////////////////////////////// // Construct WFM demod object ///////////////////////////////////////////////////////////////////////////////// CWFmMod::CWFmMod() { m_PilotAcc = 0.0; m_LeftAcc = 0.0; m_RightAcc = 0.0; m_ModAcc = 0.0; SetSampleRate(200000.0); StateTimer = 0; ModState = 0; TimerPeriod = 100000; m_SweepFreqNorm = 1.0; m_SweepFrequency = 0.0; m_SweepStopFrequency = 0.0; m_SweepRateInc = 0.0; m_LeftAmp = 1.0; m_RightAmp = 1.0; } ///////////////////////////////////////////////////////////////////////////////// // Initialize variables with given SampleRate ///////////////////////////////////////////////////////////////////////////////// void CWFmMod::SetSampleRate(TYPEREAL SampleRate) { m_SampleRate = SampleRate; m_PilotInc = PILOT_FREQ*K_2PI/m_SampleRate; m_LeftInc = LEFT_FREQ*K_2PI/m_SampleRate; m_RightInc = RIGHT_FREQ*K_2PI/m_SampleRate; m_DeviationRate = DEVIATION_FREQ*K_2PI/m_SampleRate; TimerPeriod = STATE_TIME*SampleRate; StateTimer = TimerPeriod; ModState = 0; InitRDS(); } ///////////////////////////////////////////////////////////////////////////////// // Set Sweep generator settings ///////////////////////////////////////////////////////////////////////////////// void CWFmMod::SetSweep(TYPEREAL SweepFreqNorm, TYPEREAL SweepFrequency, TYPEREAL SweepStopFrequency, TYPEREAL SweepRateInc) { m_SweepFreqNorm = SweepFreqNorm; m_SweepFrequency = SweepFrequency; m_SweepStopFrequency = SweepStopFrequency; m_SweepRateInc = SweepRateInc; } ///////////////////////////////////////////////////////////////////////////////// // Process WFM Mod STEREO version ///////////////////////////////////////////////////////////////////////////////// void CWFmMod::GenerateData(int Length, TYPEREAL Amplitude, TYPECPX* pOutData) { TYPEREAL mod; CreateRdsSamples(Length, m_RdsOut); for(int i=0; i= m_SweepStopFrequency) //reached end of sweep? m_SweepRateInc = 0.0; //stop sweep when end is reached mod = sin(m_PilotAcc); //sweep pilot tone for testing #endif m_ModAcc += (mod*m_DeviationRate); pOutData[i].re = Amplitude*cos(m_ModAcc); pOutData[i].im = Amplitude*sin(m_ModAcc); } while(m_PilotAcc>K_2PI) m_PilotAcc -= K_2PI; while(m_LeftAcc>K_2PI) m_LeftAcc -= K_2PI; while(m_RightAcc>K_2PI) m_RightAcc -= K_2PI; while(m_ModAcc>K_2PI) m_ModAcc -= K_2PI; #if 1 //state machine for stereo testing if(StateTimer++ > TimerPeriod/Length) { StateTimer = 0; switch(ModState) { case 0: m_LeftAmp = 1.0; m_RightAmp = 0.0; ModState = 1; break; case 1: m_LeftAmp = 0.0; m_RightAmp = 1.0; ModState = 2; break; case 2: m_LeftAmp = 1.0; m_RightAmp = 1.0; ModState = 3; break; case 3: m_LeftAmp = 0.0; m_RightAmp = 0.0; ModState = 0; break; } } #endif } ///////////////////////////////////////////////////////////////////////////////// // Initialize RDS generator variables ///////////////////////////////////////////////////////////////////////////////// void CWFmMod::InitRDS() { //create impulse response of bi-phase bits // This is basically the time domain shape of a single bi-phase bit // as defined for RDS and is close to a single cycle sine wave in shape m_RdsPulseLength = m_SampleRate / RDS_BITRATE; int Period = (int)(m_RdsPulseLength + .5); for(int i= 0; i<= Period; i++) { TYPEREAL t = (TYPEREAL)i/(m_SampleRate); TYPEREAL x = t*RDS_BITRATE; TYPEREAL x64 = 64.0*x; m_RdsPulseCoef[i+Period] = .75*cos(2.0*K_2PI*x)*( (1.0/(1.0/x-x64)) - (1.0/(9.0/x-x64)) ); m_RdsPulseCoef[Period-i] = -.75*cos(2.0*K_2PI*x)*( (1.0/(1.0/x-x64)) - (1.0/(9.0/x-x64)) ); } m_RdsPulseLength *= 2.0; m_RdsTime = 0.0; m_RdsSamplePeriod = 1.0/m_SampleRate; m_RdsD1 = 1; m_RdsD2 = 1; #if 0 //debug hack to write m_RdsPulseCoef to a file for analysis QDir::setCurrent("d:/"); QFile File; File.setFileName("rdscoef.txt"); if(File.open(QIODevice::WriteOnly)) { qDebug()<<"file Opened OK"; char Buf[256]; for(int n=0; n rdsperiod) n2 = (int)( (m_RdsTime-rdsperiod) * m_SampleRate); else n2 = (int)( (m_RdsTime+rdsperiod) * m_SampleRate); //if a pointer wraps to zero, get next new data bit value if(0==n1) m_RdsD1 = CreateNextRdsBit(); if(0==n2) m_RdsD2 = CreateNextRdsBit(); //get both table values and add togehter for output sample value pBuf[i] = m_RdsD1*m_RdsPulseCoef[n1] + m_RdsD2*m_RdsPulseCoef[n2]; //manage running floating point time position m_RdsTime += m_RdsSamplePeriod; if(m_RdsTime >= rds2period) m_RdsTime -= rds2period; } } ///////////////////////////////////////////////////////////////////////////////// // Gets next data bit from m_RdsDataBuf[] and converts to a + or - 1.0 value // used by CreateRdsSamples() to produce the modulation waveform ///////////////////////////////////////////////////////////////////////////////// double CWFmMod::CreateNextRdsBit() { int bit; //get next bit from 26 bit wide buffer msbit first if( m_RdsBitPtr & m_RdsDataBuf[m_RdsBufPos] ) bit = 1; else bit = 0; m_RdsBitPtr >>= 1; //shift bit pointer to next bit if(0 == m_RdsBitPtr) { //reached end of 26bit word so go to next word in m_RdsDataBuf[] m_RdsBitPtr = (1<<25); m_RdsBufPos++; if(m_RdsBufPos >= m_RdsBufLength) m_RdsBufPos = 0; //reached end of m_RdsDataBuf[] so start over } //differential encode output bit by XOR with previous output bit //return +1.0 for a '1' and -1.0 for a '0' if( m_RdsLastBit ^ bit ) { m_RdsLastBit = 1; return 1.0; } else { m_RdsLastBit = 0; return -1.0; } } cutesdr-1.0.5/dsp/agc.cpp0000644000175000017500000003643611546075374015202 0ustar bottomsbottoms////////////////////////////////////////////////////////////////////// // agc.cpp: implementation of the CAgc class. // // This class implements an automatic gain function. // // History: // 2010-09-15 Initial creation MSW // 2011-03-27 Initial release ////////////////////////////////////////////////////////////////////// //========================================================================================== // + + + This Software is released under the "Simplified BSD License" + + + //Copyright 2010 Moe Wheatley. All rights reserved. // //Redistribution and use in source and binary forms, with or without modification, are //permitted provided that the following conditions are met: // // 1. Redistributions of source code must retain the above copyright notice, this list of // conditions and the following disclaimer. // // 2. Redistributions in binary form must reproduce the above copyright notice, this list // of conditions and the following disclaimer in the documentation and/or other materials // provided with the distribution. // //THIS SOFTWARE IS PROVIDED BY Moe Wheatley ``AS IS'' AND ANY EXPRESS OR IMPLIED //WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND //FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL Moe Wheatley OR //CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR //CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR //SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON //ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING //NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF //ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // //The views and conclusions contained in the software and documentation are those of the //authors and should not be interpreted as representing official policies, either expressed //or implied, of Moe Wheatley. //========================================================================================== #include "dsp/agc.h" #include "gui/testbench.h" #include //#include ////////////////////////////////////////////////////////////////////// // Local Defines ////////////////////////////////////////////////////////////////////// //signal delay line time delay in seconds. //adjust to cover the impulse response time of filter #define DELAY_TIMECONST .015 //Peak Detector window time delay in seconds. #define WINDOW_TIMECONST .018 //attack time constant in seconds //just small enough to let attackave charge up within the DELAY_TIMECONST time #define ATTACK_RISE_TIMECONST .002 #define ATTACK_FALL_TIMECONST .005 #define DECAY_RISEFALL_RATIO .3 //ratio between rise and fall times of Decay time constants //adjust for best action with SSB // hang timer release decay time constant in seconds #define RELEASE_TIMECONST .05 //limit output to about 3db of max #define AGC_OUTSCALE 0.7 #define MAX_AMPLITUDE 32767.0 //keep max in and out the same #define MAX_MANUAL_AMPLITUDE 32767.0 #define MIN_CONSTANT 3.2767e-4 //const for calc log() so that a value of 0 magnitude == -8 //corresponding to -160dB. //K = 10^( -8 + log(32767) ) ////////////////////////////////////////////////////////////////////// // Construction/Destruction ////////////////////////////////////////////////////////////////////// CAgc::CAgc() { m_AgcOn = true; m_UseHang = false; m_Threshold = 0; m_ManualGain = 0; m_SlopeFactor = 0; m_Decay = 0; m_SampleRate = 100.0; } CAgc::~CAgc() { } //////////////////////////////////////////////////////////////////////////////// // Sets and calculates various AGC parameters // "On" switches between AGC on and off. // "Threshold" specifies AGC Knee in dB if AGC is active.( nominal range -160 to 0dB) // "ManualGain" specifies AGC manual gain in dB if AGC is not active.(nominal range 0 to 100dB) // "SlopeFactor" specifies dB reduction in output at knee from maximum output level(nominal range 0 to 10dB) // "Decay" is AGC decay value in milliseconds ( nominal range 20 to 5000 milliSeconds) // ""SampleRate" is current sample rate of AGC data //////////////////////////////////////////////////////////////////////////////// void CAgc::SetParameters(bool AgcOn, bool UseHang, int Threshold, int ManualGain, int SlopeFactor, int Decay, TYPEREAL SampleRate) { if( (AgcOn == m_AgcOn) && (UseHang == m_UseHang) && (Threshold == m_Threshold) && (ManualGain == m_ManualGain) && (SlopeFactor == m_SlopeFactor) && (Decay == m_Decay) && (SampleRate == m_SampleRate) ) { return; //just return if no parameter changed } m_Mutex.lock(); m_AgcOn = AgcOn; m_UseHang = UseHang; m_Threshold = Threshold; m_ManualGain = ManualGain; m_SlopeFactor = SlopeFactor; m_Decay = Decay; if( m_SampleRate != SampleRate) { //clear out delay buffer and init some things if sample rate changes m_SampleRate = SampleRate; for(int i=0; i #define FMPLL_RANGE 15000.0 //maximum deviation limit of PLL #define VOICE_BANDWIDTH 3000.0 #define FMPLL_BW VOICE_BANDWIDTH //natural frequency ~loop bandwidth #define FMPLL_ZETA .707 //PLL Loop damping factor #define FMDC_ALPHA 0.001 //time constant for DC removal filter #define MAX_FMOUT 100000.0 #define SQUELCH_MAX 8000.0 //roughly the maximum noise average with no signal #define SQUELCHAVE_TIMECONST .02 #define SQUELCH_HYSTERESIS 50.0 #define DEMPHASIS_TIME 80e-6 ///////////////////////////////////////////////////////////////////////////////// // Construct FM demod object ///////////////////////////////////////////////////////////////////////////////// CFmDemod::CFmDemod(TYPEREAL samplerate) : m_SampleRate(samplerate) { m_FreqErrorDC = 0.0; m_NcoPhase = 0.0; m_NcoFreq = 0.0; SetSampleRate(m_SampleRate); } ///////////////////////////////////////////////////////////////////////////////// // Sets sample rate and adjusts any parameters that are affected. ///////////////////////////////////////////////////////////////////////////////// void CFmDemod::SetSampleRate(TYPEREAL samplerate) { m_SampleRate = samplerate; TYPEREAL norm = K_2PI/m_SampleRate; //to normalize Hz to radians //initialize the PLL m_NcoLLimit = -FMPLL_RANGE * norm; //clamp FM PLL NCO m_NcoHLimit = FMPLL_RANGE * norm; m_PllAlpha = 2.0*FMPLL_ZETA*FMPLL_BW * norm; m_PllBeta = (m_PllAlpha * m_PllAlpha)/(4.0*FMPLL_ZETA*FMPLL_ZETA); m_OutGain = MAX_FMOUT/m_NcoHLimit; //audio output level gain value //DC removal filter time constant m_DcAlpha = (1.0 - exp(-1.0/(m_SampleRate*FMDC_ALPHA)) ); //initialize some noise squelch items m_SquelchHPFreq = VOICE_BANDWIDTH; m_SquelchAve = 0.0; m_SquelchState = true; m_SquelchAlpha = (1.0-exp(-1.0/(m_SampleRate*SQUELCHAVE_TIMECONST)) ); m_DeemphasisAlpha = (1.0-exp(-1.0/(m_SampleRate*DEMPHASIS_TIME)) ); m_DeemphasisAve = 0.0; m_LpFir.InitLPFilter(0,1.0,50.0,VOICE_BANDWIDTH, 1.6*VOICE_BANDWIDTH, m_SampleRate); InitNoiseSquelch(); } ///////////////////////////////////////////////////////////////////////////////// // Sets squelch threshold based on 'Value' which goes from 0 to 99. ///////////////////////////////////////////////////////////////////////////////// void CFmDemod::SetSquelch(int Value) { m_SquelchThreshold = (TYPEREAL)(SQUELCH_MAX - (( SQUELCH_MAX*Value)/99)); } ///////////////////////////////////////////////////////////////////////////////// // Sets up Highpass noise filter parameters based on input filter BW ///////////////////////////////////////////////////////////////////////////////// void CFmDemod::InitNoiseSquelch() { m_HpFir.InitHPFilter(0, 1.0, 50.0, m_SquelchHPFreq*.8, m_SquelchHPFreq*.65, m_SampleRate); // m_HpFir.InitHPFilter(0, 1.0, 50.0, VOICE_BANDWIDTH*2.0, VOICE_BANDWIDTH, m_SampleRate); } ///////////////////////////////////////////////////////////////////////////////// // Performs noise squelch by reading the noise power above the voice frequencies ///////////////////////////////////////////////////////////////////////////////// void CFmDemod::PerformNoiseSquelch(int InLength, TYPEREAL* pOutData) { if(InLength>MAX_SQBUF_SIZE) return; TYPEREAL sqbuf[MAX_SQBUF_SIZE]; //high pass filter to get the high frequency noise above the voice m_HpFir.ProcessFilter(InLength, pOutData, sqbuf); //g_pTestBench->DisplayData(InLength, sqbuf, m_SampleRate,PROFILE_6); for(int i=0; iDisplayData(1, &m_SquelchAve, m_SampleRate,PROFILE_3); } //perform squelch compare to threshold using some Hysteresis if(0==m_SquelchThreshold) { //force squelch if zero(strong signal threshold) m_SquelchState = true; } else if(m_SquelchState) //if in squelched state { if(m_SquelchAve < (m_SquelchThreshold-SQUELCH_HYSTERESIS)) m_SquelchState = false; } else { if(m_SquelchAve >= (m_SquelchThreshold+SQUELCH_HYSTERESIS)) m_SquelchState = true; } //m_SquelchState = false; if(m_SquelchState) { //zero output if squelched for(int i=0; iDisplayData(InLength, pOutData, m_SampleRate,PROFILE_6); } } ///////////////////////////////////////////////////////////////////////////////// // Process FM demod MONO version ///////////////////////////////////////////////////////////////////////////////// int CFmDemod::ProcessData(int InLength, TYPEREAL FmBW, TYPECPX* pInData, TYPEREAL* pOutData) { TYPECPX tmp; if(m_SquelchHPFreq != FmBW) { //update squelch HP filter cutoff from main filter BW m_SquelchHPFreq = FmBW; InitNoiseSquelch(); } for(int i=0; i m_NcoHLimit) m_NcoFreq = m_NcoHLimit; else if(m_NcoFreq < m_NcoLLimit) m_NcoFreq = m_NcoLLimit; //update NCO phase with new value m_NcoPhase += (m_NcoFreq + m_PllAlpha * phzerror); //LP filter the NCO frequency term to get DC offset value m_FreqErrorDC = (1.0-m_DcAlpha)*m_FreqErrorDC + m_DcAlpha*m_NcoFreq; //subtract out DC term to get FM audio pOutData[i] = (m_NcoFreq-m_FreqErrorDC)*m_OutGain; } //g_pTestBench->DisplayData(InLength, pOutData, m_SampleRate, PROFILE_3); m_NcoPhase = fmod(m_NcoPhase, K_2PI); //keep radian counter bounded PerformNoiseSquelch(InLength, pOutData); //calculate squelch return InLength; } ///////////////////////////////////////////////////////////////////////////////// // Process FM demod STEREO version ///////////////////////////////////////////////////////////////////////////////// int CFmDemod::ProcessData(int InLength, TYPEREAL FmBW, TYPECPX* pInData, TYPECPX* pOutData) { TYPECPX tmp; if(m_SquelchHPFreq != FmBW) { //update Squelch HP filter cutoff from main filter BW m_SquelchHPFreq = FmBW; InitNoiseSquelch(); } for(int i=0; i m_NcoHLimit) m_NcoFreq = m_NcoHLimit; else if(m_NcoFreq < m_NcoLLimit) m_NcoFreq = m_NcoLLimit; //update NCO phase with new value m_NcoPhase += (m_NcoFreq + m_PllAlpha * phzerror); //LP filter the NCO frequency term to get DC offset value m_FreqErrorDC = (1.0-m_DcAlpha)*m_FreqErrorDC + m_DcAlpha*m_NcoFreq; //subtract out DC term to get FM audio m_OutBuf[i] = (m_NcoFreq-m_FreqErrorDC)*m_OutGain; } m_NcoPhase = fmod(m_NcoPhase, K_2PI); //keep radian counter bounded PerformNoiseSquelch(InLength, m_OutBuf); for(int i=0; i typedef struct _snproc { bool NBOn; int NBThreshold; int NBWidth; }tNoiseProcdInfo; class CNoiseProc { public: CNoiseProc(); virtual ~CNoiseProc(); void SetupBlanker( bool On, TYPEREAL Threshold, TYPEREAL Width, TYPEREAL SampleRate); void ProcessBlanker(int InLength, TYPECPX* pInData, TYPECPX* pOutData); private: bool m_On; TYPEREAL m_Threshold; TYPEREAL m_Width; TYPEREAL m_SampleRate; TYPECPX* m_DelayBuf; TYPEREAL* m_MagBuf; TYPECPX* m_TestBenchDataBuf; int m_Dptr; int m_Mptr; int m_BlankCounter; int m_DelaySamples; int m_MagSamples; int m_WidthSamples; TYPEREAL m_Ratio; TYPEREAL m_MagAveSum; TYPEREAL m_MagAve; QMutex m_Mutex; //for keeping threads from stomping on each other }; #endif // NOISEPROC_H cutesdr-1.0.5/dsp/agc.h0000644000175000017500000000304211546075374014632 0ustar bottomsbottoms////////////////////////////////////////////////////////////////////// // agc.h: interface for the CAgc class. // // This class implements an automatic gain function. // // History: // 2010-09-15 Initial creation MSW // 2011-03-27 Initial release ////////////////////////////////////////////////////////////////////// #ifndef AGCX_H #define AGCX_H #include "dsp/datatypes.h" #include #define MAX_DELAY_BUF 2048 class CAgc { public: CAgc(); virtual ~CAgc(); void SetParameters(bool AgcOn, bool UseHang, int Threshold, int ManualGain, int Slope, int Decay, TYPEREAL SampleRate); void ProcessData(int Length, TYPECPX* pInData, TYPECPX* pOutData); void ProcessData(int Length, TYPEREAL* pInData, TYPEREAL* pOutData); private: bool m_AgcOn; //internal copy of AGC settings parameters bool m_UseHang; int m_Threshold; int m_ManualGain; int m_Slope; int m_Decay; TYPEREAL m_SampleRate; TYPEREAL m_SlopeFactor; TYPEREAL m_ManualAgcGain; TYPEREAL m_DecayAve; TYPEREAL m_AttackAve; TYPEREAL m_AttackRiseAlpha; TYPEREAL m_AttackFallAlpha; TYPEREAL m_DecayRiseAlpha; TYPEREAL m_DecayFallAlpha; TYPEREAL m_FixedGain; TYPEREAL m_Knee; TYPEREAL m_GainSlope; TYPEREAL m_Peak; int m_SigDelayPtr; int m_MagBufPos; int m_DelaySize; int m_DelaySamples; int m_WindowSamples; int m_HangTime; int m_HangTimer; QMutex m_Mutex; //for keeping threads from stomping on each other TYPECPX m_SigDelayBuf[MAX_DELAY_BUF]; TYPEREAL m_MagBuf[MAX_DELAY_BUF]; }; #endif // AGCX_H cutesdr-1.0.5/dsp/amdemod.cpp0000644000175000017500000001074311711303214016024 0ustar bottomsbottoms// amdemod.cpp: implementation of the CAmDemod class. // // This class takes I/Q baseband data and performs // AM demodulation // History: // 2010-09-22 Initial creation MSW // 2011-03-27 Initial release // 2011-08-07 Modified FIR filter initialization call ////////////////////////////////////////////////////////////////////// //========================================================================================== // + + + This Software is released under the "Simplified BSD License" + + + //Copyright 2010 Moe Wheatley. All rights reserved. // //Redistribution and use in source and binary forms, with or without modification, are //permitted provided that the following conditions are met: // // 1. Redistributions of source code must retain the above copyright notice, this list of // conditions and the following disclaimer. // // 2. Redistributions in binary form must reproduce the above copyright notice, this list // of conditions and the following disclaimer in the documentation and/or other materials // provided with the distribution. // //THIS SOFTWARE IS PROVIDED BY Moe Wheatley ``AS IS'' AND ANY EXPRESS OR IMPLIED //WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND //FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL Moe Wheatley OR //CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR //CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR //SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON //ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING //NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF //ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // //The views and conclusions contained in the software and documentation are those of the //authors and should not be interpreted as representing official policies, either expressed //or implied, of Moe Wheatley. //========================================================================================== #include "amdemod.h" #include "gui/testbench.h" #include "dsp/datatypes.h" #include "dsp/filtercoef.h" #include #define DC_ALPHA 0.99 //ALPHA for DC removal filter ~20Hz Fcut with 15625Hz Sample Rate ///////////////////////////////////////////////////////////////////////////////// // Construct AM demod object ///////////////////////////////////////////////////////////////////////////////// CAmDemod::CAmDemod(TYPEREAL samplerate) : m_SampleRate(samplerate) { m_z1 = 0.0; m_Fir.InitLPFilter(0, 1.0, 50.0, 10000, 10000*1.8, m_SampleRate);//initialize LP FIR filter } void CAmDemod::SetBandwidth(TYPEREAL Bandwidth) { //create a LP filter with passband the same as the main filter bandwidth for post audio filtering m_Fir.InitLPFilter(0, 1.0, 50.0, Bandwidth, Bandwidth*1.8, m_SampleRate);//initialize LP FIR filter } ///////////////////////////////////////////////////////////////////////////////// // Process envelope AM demod MONO version ///////////////////////////////////////////////////////////////////////////////// int CAmDemod::ProcessData(int InLength, TYPECPX* pInData, TYPEREAL* pOutData) { for(int i=0; i #define FMDEMOD_GAIN 8000.0 #define PILOTPLL_RANGE 20.0 //maximum deviation limit of PLL #define PILOTPLL_BW 10.0 //natural frequency ~loop bandwidth #define PILOTPLL_ZETA .707 //PLL Loop damping factor #define PILOTPLL_FREQ 19000.0 //Centerfreq #define LOCK_TIMECONST .5 //Lock filter time in seconds #define LOCK_MAG_THRESHOLD 0.05 //Lock error magnitude threshold #define PHASE_ADJ_M -7.267e-6 //fudge factor slope to compensate for PLL delay #define PHASE_ADJ_B 3.677 //fudge factor intercept to compensate for PLL delay //bunch of RDS constants #define USE_FEC 1 //set to zero to disable FEC correction #define RDS_FREQUENCY 57000.0 #define RDS_BITRATE (RDS_FREQUENCY/48.0) //1187.5 bps bitrate #define RDSPLL_RANGE 12.0 //maximum deviation limit of PLL #define RDSPLL_BW 1.0 //natural frequency ~loop bandwidth #define RDSPLL_ZETA .707 //PLL Loop damping factor //RDS decoder states #define STATE_BITSYNC 0 //looking for initial bit position in Block 1 #define STATE_BLOCKSYNC 1 //looking for initial correct block order #define STATE_GROUPDECODE 2 //decode groups after achieving bit and block sync #define STATE_GROUPRESYNC 3 //waiting for beginning of new group after getting a block error #define BLOCK_ERROR_LIMIT 5 //number of bad blocks before trying to resync at the bit level #define HILB_LENGTH 61 const double HILBLP_H[HILB_LENGTH] = { //LowPass filter prototype that is shifted and "hilbertized" to get 90 deg phase shift //and convert to baseband complex domain. //kaiser-Bessel alpha 1.4 cutoff 30Khz at sample rate of 250KHz -0.000389631665953405,0.000115430826670992,0.000945331102222503,0.001582460677684605, 0.001370803713784687,-0.000000000000000002,-0.002077413537668161,-0.003656132107176520, -0.003372610825000167,-0.000649815020884706, 0.003583263233560064, 0.006997162933343487, 0.006990985399916562, 0.002383133886438500,-0.005324501734543406,-0.012092135317628615, -0.013212201698221963,-0.006168904735839018, 0.007082277142635906, 0.020017841466263672, 0.024271835962039127, 0.014255112728911837,-0.008597071392140753,-0.034478282954624850, -0.048147195828726633,-0.035409729589347565, 0.009623663461671806, 0.080084441681677138, 0.157278883310078170, 0.217148915611638180, 0.239688166538436750, 0.217148915611638180, 0.157278883310078170, 0.080084441681677138, 0.009623663461671806,-0.035409729589347565, -0.048147195828726633,-0.034478282954624850,-0.008597071392140753, 0.014255112728911837, 0.024271835962039127, 0.020017841466263672, 0.007082277142635906,-0.006168904735839018, -0.013212201698221963,-0.012092135317628615,-0.005324501734543406, 0.002383133886438500, 0.006990985399916562, 0.006997162933343487, 0.003583263233560064,-0.000649815020884706, -0.003372610825000167,-0.003656132107176520,-0.002077413537668161,-0.000000000000000002, 0.001370803713784687, 0.001582460677684605, 0.000945331102222503, 0.000115430826670992, -0.000389631665953405 }; ///////////////////////////////////////////////////////////////////////////////// // Construct/destruct WFM demod object ///////////////////////////////////////////////////////////////////////////////// CWFmDemod::CWFmDemod(TYPEREAL samplerate) : m_SampleRate(samplerate) { m_pDecBy2A = NULL; m_pDecBy2B = NULL; m_pDecBy2C = NULL; m_PilotPhaseAdjust = 0.0; SetSampleRate(samplerate, true); m_InBitStream = 0; m_CurrentBitPosition = 0; m_CurrentBlock = BLOCK_A; m_DecodeState = STATE_BITSYNC; m_BGroupOffset = 0; m_PilotLocked = false; m_LastPilotLocked = !m_PilotLocked; m_BlockErrors = 0; } CWFmDemod::~CWFmDemod() { //destroy resources if(m_pDecBy2A) delete m_pDecBy2A; if(m_pDecBy2B) delete m_pDecBy2B; if(m_pDecBy2C) delete m_pDecBy2C; } ///////////////////////////////////////////////////////////////////////////////// // Sets demodulator parameters based on input sample rate // returns the audio sample rate that is produced // Input sample rate should be in the range 200 to 400Ksps // The output rate will be between 50KHz and 100KHz ///////////////////////////////////////////////////////////////////////////////// TYPEREAL CWFmDemod::SetSampleRate(TYPEREAL samplerate, bool USver) { //delete any resources that may still exist if(m_pDecBy2A) delete m_pDecBy2A; if(m_pDecBy2B) delete m_pDecBy2B; if(m_pDecBy2C) delete m_pDecBy2C; m_pDecBy2A = NULL; m_pDecBy2B = NULL; m_pDecBy2C = NULL; m_OutRate = m_SampleRate = samplerate; //Determine post demod decimation rate based on input sample rate range // try to get down to close to 50khz if(m_SampleRate>400000)//need dec by 8 { m_pDecBy2C = new CDecimateBy2(HB47TAP_LENGTH, HB47TAP_H); m_OutRate /= 2.0; } if(m_SampleRate>200000)//need dec by 4 { m_pDecBy2B = new CDecimateBy2(HB47TAP_LENGTH, HB47TAP_H); m_OutRate /= 2.0; } if(m_SampleRate>100000)//need dec by 2 { m_pDecBy2A = new CDecimateBy2(HB47TAP_LENGTH, HB47TAP_H); m_OutRate /= 2.0; } qDebug()<<"WFW Rates = "< m_PilotNcoHLimit) m_PilotNcoFreq = m_PilotNcoHLimit; else if(m_PilotNcoFreq < m_PilotNcoLLimit) m_PilotNcoFreq = m_PilotNcoLLimit; //update NCO phase with new value m_PilotNcoPhase += (m_PilotNcoFreq + m_PilotPllAlpha * phzerror); m_PilotPhase[i] = m_PilotNcoPhase + m_PilotPhaseAdjust; //phase fudge for exact phase delay //create long average of error magnitude for lock detection m_PhaseErrorMagAve = (1.0-m_PhaseErrorMagAlpha)*m_PhaseErrorMagAve + m_PhaseErrorMagAlpha*phzerror*phzerror; } m_PilotNcoPhase = fmod(m_PilotNcoPhase, K_2PI); //keep radian counter bounded //StopPerformance(InLength); if(m_PhaseErrorMagAve < LOCK_MAG_THRESHOLD) return TRUE; else return FALSE; } ///////////////////////////////////////////////////////////////////////////////// // Get present Stereo lock status and put in pPilotLock. // Returns true if lock status has changed since last call. ///////////////////////////////////////////////////////////////////////////////// int CWFmDemod::GetStereoLock(int* pPilotLock) { if(pPilotLock) *pPilotLock = m_PilotLocked; if(m_PilotLocked != m_LastPilotLocked) { m_LastPilotLocked = m_PilotLocked; return true; } else return false; } ///////////////////////////////////////////////////////////////////////////////// // Iniitalize IIR variables for De-emphasis IIR filter. ///////////////////////////////////////////////////////////////////////////////// void CWFmDemod::InitDeemphasis( TYPEREAL Time, TYPEREAL SampleRate) //create De-emphasis LP filter { m_DeemphasisAlpha = (1.0-exp(-1.0/(SampleRate*Time)) ); m_DeemphasisAveRe = 0.0; m_DeemphasisAveIm = 0.0; } ///////////////////////////////////////////////////////////////////////////////// // Process InLength InBuf[] samples and place in OutBuf[] //REAL version ///////////////////////////////////////////////////////////////////////////////// void CWFmDemod::ProcessDeemphasisFilter(int InLength, TYPEREAL* InBuf, TYPEREAL* OutBuf) { for(int i=0; i m_RdsNcoHLimit) m_RdsNcoFreq = m_RdsNcoHLimit; else if(m_RdsNcoFreq < m_RdsNcoLLimit) m_RdsNcoFreq = m_RdsNcoLLimit; //update NCO phase with new value m_RdsNcoPhase += (m_RdsNcoFreq + m_RdsPllAlpha * phzerror); pOutData[i] = tmp.im; } m_RdsNcoPhase = fmod(m_RdsNcoPhase, K_2PI); //keep radian counter bounded //g_pTestBench->DisplayData(InLength, m_RdsData, m_RdsOutputRate, PROFILE_6); } ///////////////////////////////////////////////////////////////////////////////// // Process one new bit from RDS data stream. // Manages state machine to find block data bit position, runs chksum and FEC on // each block, recovers good groups of 4 data blocks and places in data queue // for further upper level GUI processing depending on the application ///////////////////////////////////////////////////////////////////////////////// void CWFmDemod::ProcessNewRdsBit(int bit) { m_InBitStream = (m_InBitStream<<1) | bit; //shift in new bit switch(m_DecodeState) { case STATE_BITSYNC: //looking at each bit position till we find a "good" block A if( 0 == CheckBlock(OFFSET_SYNDROME_BLOCK_A, false) ) { //got initial good chkword on Block A not using FEC m_CurrentBitPosition = 0; m_BGroupOffset = 0; m_BlockData[BLOCK_A] = m_InBitStream>>NUMBITS_CRC; m_CurrentBlock = BLOCK_B; m_DecodeState = STATE_BLOCKSYNC; //next state is looking for blocks B,C, and D in sequence } break; case STATE_BLOCKSYNC: //Looking for 4 blocks in correct sequence to have good probability bit position is good m_CurrentBitPosition++; if(m_CurrentBitPosition >= NUMBITS_BLOCK) { m_CurrentBitPosition = 0; if( CheckBlock(BLK_OFFSET_TBL[m_CurrentBlock+m_BGroupOffset], false ) ) { //bad chkword so go look for bit sync again m_DecodeState = STATE_BITSYNC; } else { //good chkword so save data and setup for next block m_BlockData[m_CurrentBlock] = m_InBitStream>>NUMBITS_CRC; //save msg data //see if is group A or Group B if( (BLOCK_B == m_CurrentBlock) && (m_BlockData[m_CurrentBlock] & GROUPB_BIT) ) m_BGroupOffset = 4; else m_BGroupOffset = 0; if(m_CurrentBlock >= BLOCK_D) { //good chkword on all 4 blocks in correct sequence so are sure of bit position //Place all group data into data queue m_RdsGroupQueue[m_RdsQHead].BlockA = m_BlockData[BLOCK_A]; m_RdsGroupQueue[m_RdsQHead].BlockB = m_BlockData[BLOCK_B]; m_RdsGroupQueue[m_RdsQHead].BlockC = m_BlockData[BLOCK_C]; m_RdsGroupQueue[m_RdsQHead++].BlockD = m_BlockData[BLOCK_D]; if(m_RdsQHead >= RDS_Q_SIZE ) m_RdsQHead = 0; m_CurrentBlock = BLOCK_A; m_BlockErrors = 0; m_DecodeState = STATE_GROUPDECODE; qDebug()<<"RDS Blk Sync"; } else m_CurrentBlock++; } } break; case STATE_GROUPDECODE: //here after getting a good sequence of blocks m_CurrentBitPosition++; if(m_CurrentBitPosition>=NUMBITS_BLOCK) { m_CurrentBitPosition = 0; if( CheckBlock(BLK_OFFSET_TBL[m_CurrentBlock+m_BGroupOffset], USE_FEC ) ) { m_BlockErrors++; if( m_BlockErrors > BLOCK_ERROR_LIMIT ) { m_RdsQHead = m_RdsQTail = 0; //clear data queue m_RdsGroupQueue[m_RdsQHead].BlockA = 0; //stuff all zeros in que to indicate m_RdsGroupQueue[m_RdsQHead].BlockB = 0; //loss of signal m_RdsGroupQueue[m_RdsQHead].BlockC = 0; m_RdsGroupQueue[m_RdsQHead++].BlockD = 0; m_DecodeState = STATE_BITSYNC; } else { m_CurrentBlock++; if(m_CurrentBlock>BLOCK_D) m_CurrentBlock = BLOCK_A; if( BLOCK_A != m_CurrentBlock ) //skip remaining blocks of this group if error m_DecodeState = STATE_GROUPRESYNC; } } else { //good block so save and get ready for next one m_BlockData[m_CurrentBlock] = m_InBitStream>>NUMBITS_CRC; //save msg data //see if is group A or Group B if( (BLOCK_B == m_CurrentBlock) && (m_BlockData[m_CurrentBlock] & GROUPB_BIT) ) m_BGroupOffset = 4; else m_BGroupOffset = 0; m_CurrentBlock++; if(m_CurrentBlock>BLOCK_D) { //Place all group data into data queue m_RdsGroupQueue[m_RdsQHead].BlockA = m_BlockData[BLOCK_A]; m_RdsGroupQueue[m_RdsQHead].BlockB = m_BlockData[BLOCK_B]; m_RdsGroupQueue[m_RdsQHead].BlockC = m_BlockData[BLOCK_C]; m_RdsGroupQueue[m_RdsQHead++].BlockD = m_BlockData[BLOCK_D]; if(m_RdsQHead >= RDS_Q_SIZE ) m_RdsQHead = 0; m_CurrentBlock = BLOCK_A; m_BlockErrors = 0; //qDebug("Grp %X %X %X %X",m_BlockData[BLOCK_A],m_BlockData[BLOCK_B],m_BlockData[BLOCK_C],m_BlockData[BLOCK_D]); //here with complete good group } } } break; case STATE_GROUPRESYNC: //ignor blocks until start of next group m_CurrentBitPosition++; if(m_CurrentBitPosition>=NUMBITS_BLOCK) { m_CurrentBitPosition = 0; m_CurrentBlock++; if(m_CurrentBlock>BLOCK_D) { m_CurrentBlock = BLOCK_A; m_DecodeState = STATE_GROUPDECODE; //qDebug()<<"Grp Resync"; } } break; } } ///////////////////////////////////////////////////////////////////////////////// // Check block 'm_InBitStream' with 'BlockOffset' for errors. // if UseFec is false then no FEC is done else correct up to 5 bits. // Returns zero if no remaining errors if FEC is specified. ///////////////////////////////////////////////////////////////////////////////// quint32 CWFmDemod::CheckBlock(quint32 SyndromeOffset, int UseFec) { //First calculate syndrome for current 26 m_InBitStream bits quint32 testblock = (0x3FFFFFF & m_InBitStream); //isolate bottom 26 bits //copy top 10 bits of block into 10 syndrome bits since first 10 rows //of the check matrix is just an identity matrix(diagonal one's) quint32 syndrome = testblock>>16; for(int i=0; i>= 1; //advance correctable bit position } syndrome &= 0x3FF; //isolate syndrome bits if non-zero then still an error if(correctedbits && !syndrome) { // qDebug()<<"corrected bits "<BlockA = m_RdsGroupQueue[m_RdsQTail].BlockA; pGroupData->BlockB = m_RdsGroupQueue[m_RdsQTail].BlockB; pGroupData->BlockC = m_RdsGroupQueue[m_RdsQTail].BlockC; pGroupData->BlockD = m_RdsGroupQueue[m_RdsQTail++].BlockD; if(m_RdsQTail >= RDS_Q_SIZE ) m_RdsQTail = 0; if( (m_LastRdsGroup.BlockA != pGroupData->BlockA) || (m_LastRdsGroup.BlockB != pGroupData->BlockB) || (m_LastRdsGroup.BlockC != pGroupData->BlockC) || (m_LastRdsGroup.BlockD != pGroupData->BlockD) ) { m_LastRdsGroup = *pGroupData; return true; } else return false; } ///////////////////////////////////////////////////////////////////////////////// // Less acurate but somewhat faster atan2() function // |error| < 0.005 // Useful for plls but not for main FM demod if best audio quality desired. ///////////////////////////////////////////////////////////////////////////////// inline TYPEREAL CWFmDemod::arctan2(TYPEREAL y, TYPEREAL x) { TYPEREAL angle; if( x == 0.0 ) { //avoid divide by zero and just return angle if( y > 0.0 ) return K_PI2; if( y == 0.0 ) return 0.0; return -K_PI2; } TYPEREAL z = y/x; if( fabs( z ) < 1.0 ) { angle = z/(1.0 + 0.2854*z*z); if( x < 0.0 ) { if( y < 0.0 ) return angle - K_PI; return angle + K_PI; } } else { angle = K_PI2 - z/(z*z + 0.2854); if( y < 0.0 ) return angle - K_PI; } return angle; } cutesdr-1.0.5/dsp/rbdsconstants.h0000644000175000017500000001642711711303214016757 0ustar bottomsbottoms#ifndef RBDSCALLS_H #define RBDSCALLS_H #include #define RDS_FREQUENCY 57000.0 //"carrier" frequency of RDS signal within FM demod output #define RDS_BITRATE (RDS_FREQUENCY/48.0) //1187.5 bps bitrate #define NUMBITS_CRC 10 #define NUMBITS_MSG 16 #define NUMBITS_BLOCK (NUMBITS_CRC + NUMBITS_MSG) //Block offset words for chkword generation #define OFFSET_WORD_BLOCK_A 0x0FC #define OFFSET_WORD_BLOCK_B 0x198 #define OFFSET_WORD_BLOCK_C 0x168 #define OFFSET_WORD_BLOCK_CP 0x350 #define OFFSET_WORD_BLOCK_D 0x1B4 //Block offset syndromes for chkword checking //created multiplying Block Offsets by parity check matrix //Takes into account that the (26,16)code is a shortened cyclic block code //from the original (341,331) length cyclic code. #define OFFSET_SYNDROME_BLOCK_A 0x3D8 #define OFFSET_SYNDROME_BLOCK_B 0x3D4 #define OFFSET_SYNDROME_BLOCK_C 0x25C #define OFFSET_SYNDROME_BLOCK_CP 0x3CC #define OFFSET_SYNDROME_BLOCK_D 0x258 #define CRC_POLY 0x5B9 //RDS crc polynomial x^10+x^8+x^7+x^5+x^4+x^3+1 #define GROUPB_BIT 0x0800 //bit position in BlockB for determining group A or B msgs #define BLOCK_A 0 //indexes for the four blocks #define BLOCK_B 1 #define BLOCK_C 2 #define BLOCK_D 3 #define GROUPB_OFFSET 4 //offset into table below for type group B syndrome calcs const int BLK_OFFSET_TBL[8] = { OFFSET_SYNDROME_BLOCK_A, OFFSET_SYNDROME_BLOCK_B, OFFSET_SYNDROME_BLOCK_C, OFFSET_SYNDROME_BLOCK_D, // OFFSET_SYNDROME_BLOCK_A, OFFSET_SYNDROME_BLOCK_B, OFFSET_SYNDROME_BLOCK_CP, OFFSET_SYNDROME_BLOCK_D }; //Generator matrix from RDS spec for creating checksum word const quint32 CHKWORDGEN[16] = { 0x077, //00 0111 0111 0x2E7, //10 1110 0111 0x3AF, //11 1010 1111 0x30B, //11 0000 1011 0x359, //11 0101 1001 0x370, //11 0111 0000 0x1B8, //01 1011 1000 0x0DC, //00 1101 1100 0x06E, //00 0110 1110 0x037, //00 0011 0111 0x2C7, //10 1100 0111 0x3BF, //11 1011 1111 0x303, //11 0000 0011 0x35D, //11 0101 1101 0x372, //11 0111 0010 0x1B9 //01 1011 1001 }; //Parity check matrix from RDS spec for FEC //Takes into account that the (26,16)code is a shortened cyclic block code //from the original (341,331) length cyclic code. const quint32 PARCKH[16] = { 0x2DC, // 10 1101 1100 0x16E, // 01 0110 1110 0x0B7, // 00 1011 0111 0x287, // 10 1000 0111 0x39F, // 11 1001 1111 0x313, // 11 0001 0011 0x355, // 11 0101 0101 0x376, // 11 0111 0110 0x1BB, // 01 1011 1011 0x201, // 10 0000 0001 0x3DC, // 11 1101 1100 0x1EE, // 01 1110 1110 0x0F7, // 00 1111 0111 0x2A7, // 10 1010 0111 0x38F, // 11 1000 1111 0x31B // 11 0001 1011 }; typedef struct _RDS_GRPS { quint16 BlockA; quint16 BlockB; quint16 BlockC; quint16 BlockD; }tRDS_GROUPS; //structure to hold PI code vs 3 letter callsigns for RBDS msgs typedef struct { quint16 pi; char csign[4]; }tRBDS3LET; #define NUMENTRIES 72 const tRBDS3LET CALL3TABLE[NUMENTRIES] = { {0x99A5, "KBW",}, {0x9992, "KOY",}, {0x9978, "WHO",}, {0x99A6, "KCY",}, {0x9993, "KPQ",}, {0x999C, "WHP",}, {0x9990, "KDB",}, {0x9964, "KQV",}, {0x999D, "WIL",}, {0x99A7, "KDF",}, {0x9994, "KSD",}, {0x997A, "WIP",}, {0x9950, "KEX",}, {0x9965, "KSL",}, {0x99B3, "WIS",}, {0x9951, "KFH",}, {0x9966, "KUJ",}, {0x997B, "WJR",}, {0x9952, "KFI",}, {0x9995, "KUT",}, {0x99B4, "WJW",}, {0x9953, "KGA",}, {0x9967, "KVI",}, {0x99B5, "WJZ",}, {0x9991, "KGB",}, {0x9968, "KWG",}, {0x997C, "WKY",}, {0x9954, "KGO",}, {0x9996, "KXL",}, {0x997D, "WLS",}, {0x9955, "KGU",}, {0x9997, "KXO",}, {0x997E, "WLW",}, {0x9956, "KGW",}, {0x996B, "KYW",}, {0x999E, "WMC",}, {0x9957, "KGY",}, {0x9999, "WBT",}, {0x999F, "WMT",}, {0x99AA, "KHQ",}, {0x996D, "WBZ",}, {0x9981, "WOC",}, {0x9958, "KID",}, {0x996E, "WDZ",}, {0x99A0, "WOI",}, {0x9959, "KIT",}, {0x996F, "WEW",}, {0x9983, "WOL",}, {0x995A, "KJR",}, {0x999A, "WGH",}, {0x9984, "WOR",}, {0x995B, "KLO",}, {0x9971, "WGL",}, {0x99A1, "WOW",}, {0x995C, "KLZ",}, {0x9972, "WGN",}, {0x99B9, "WRC",}, {0x995D, "KMA",}, {0x9973, "WGR",}, {0x99A2, "WRR",}, {0x995E, "KMJ",}, {0x999B, "WGY",}, {0x99A3, "WSB",}, {0x995F, "KNX",}, {0x9975, "WHA",}, {0x99A4, "WSM",}, {0x9960, "KOA",}, {0x9976, "WHB",}, {0x9988, "WWJ",}, {0x99AB, "KOB",}, {0x9977, "WHK",}, {0x9989, "WWL"}, }; const char PTYPETABLERBDS[32][17] = { //16 char strings // 1234567890123456 {" "}, //0 {" News "}, //1 {" Information "}, //2 {" Sports "}, //3 {" Talk "}, //4 {" Rock "}, //5 {" Classic Rock "}, //6 {" Adult Hits "}, //7 {" Soft Rock "}, //8 {" Top 40 "}, //9 {" Country "}, //10 {" Oldies "}, //11 {" Soft "}, //12 {" Nostalgia "}, //13 {" Jazz "}, //14 {" Classical "}, //15 {"Rhythm and Blues"}, //16 {" Soft R & B "}, //17 {"Foreign Language"}, //18 {"Religious Music "}, //19 {" Religious Talk "}, //20 {" Personality "}, //21 {" Public "}, //22 {" College "}, //23 {" Hablar_Espanol "}, //24 {" Musica_Espanol "}, //25 {" Hip Hop "}, //26 {" "}, //27 {" "}, //28 {" Weather "}, //29 {"Emergency_Test "}, //30 {"ALERT! ALERT! "} //31 }; const char PTYPETABLERDS[32][20] = { //16 char strings // 1234567890123456 {" "}, //0 {" News "}, //1 {" Current Affairs "}, //2 {" Information "}, //3 {" Sport "}, //4 {" Education "}, //5 {" Drama "}, //6 {" Culture "}, //7 {" Science "}, //8 {" Varied "}, //9 {" Pop Music "}, //10 {" Rock Music "}, //11 {" M.O.R Music "}, //12 {" Light Classical "}, //13 {" Serious Classical "}, //14 {" Other Music "}, //15 {" Weather "}, //16 {" Finance "}, //17 {"Chidren's Programs "}, //18 {" Social Affairs "}, //19 {" Religion "}, //20 {" Phone In "}, //21 {" Travel "}, //22 {" Leisure "}, //23 {" Jazz Music "}, //24 {" Country Music "}, //25 {" National Music "}, //26 {" Oldies Music "}, //27 {" Folk Music "}, //28 {" Documentary "}, //29 {" Alarm Test "}, //30 {" ALARM! ALARM! "} //31 }; // List of Group Type Codes #define GRPTYPE_0A 0 #define GRPTYPE_0B 1 #define GRPTYPE_1A 2 #define GRPTYPE_1B 3 #define GRPTYPE_2A 4 //radio text #define GRPTYPE_2B 5 #define GRPTYPE_3A 6 #define GRPTYPE_3B 7 #define GRPTYPE_4A 8 #define GRPTYPE_4B 9 #define GRPTYPE_5A 10 #define GRPTYPE_5B 11 #define GRPTYPE_6A 12 #define GRPTYPE_6B 13 #define GRPTYPE_7A 14 #define GRPTYPE_7B 15 #define GRPTYPE_8A 16 #define GRPTYPE_8B 17 #define GRPTYPE_9A 18 #define GRPTYPE_9B 19 #define GRPTYPE_10A 20 #define GRPTYPE_10B 21 #define GRPTYPE_11A 22 #define GRPTYPE_11B 23 #define GRPTYPE_12A 24 #define GRPTYPE_12B 25 #define GRPTYPE_13A 26 #define GRPTYPE_13B 27 #define GRPTYPE_14A 28 #define GRPTYPE_14B 29 #define GRPTYPE_15A 30 #define GRPTYPE_15B 31 #define A_B_BIT 0x10 //bit toggles if need to reset radiotext #endif // RBDSCALLS_H cutesdr-1.0.5/dsp/fft.h0000644000175000017500000000457211546075374014670 0ustar bottomsbottoms// fft.h: interface for the CFft class. // // This is a somewhat modified version of Takuya OOURA's // original radix 4 FFT package. // A C++ wrapper around his code plus some specialized methods // for displaying power vs frequency // //Copyright(C) 1996-1998 Takuya OOURA // (email: ooura@mmm.t.u-tokyo.ac.jp). // // History: // 2010-09-15 Initial creation MSW // 2011-03-27 Initial release ////////////////////////////////////////////////////////////////////// #ifndef FFT_H #define FFT_H #include "dsp/datatypes.h" #include #define MAX_FFT_SIZE 65536 #define MIN_FFT_SIZE 512 class CFft { public: CFft(); virtual ~CFft(); void SetFFTParams( qint32 size, bool invert, double dBCompensation, double SampleFreq); //Methods to obtain spectrum formated power vs frequency void SetFFTAve( qint32 ave); void ResetFFT(); bool GetScreenIntegerFFTData(qint32 MaxHeight, qint32 MaxWidth, double MaxdB, double MindB, qint32 StartFreq, qint32 StopFreq, qint32* OutBuf ); qint32 PutInDisplayFFT(qint32 n, TYPECPX* InBuf); //Methods for doing Fast convolutions using forward and reverse FFT void FwdFFT( TYPECPX* pInOutBuf); void RevFFT( TYPECPX* pInOutBuf); private: void FreeMemory(); void makewt(qint32 nw, qint32 *ip, double *w); void makect(qint32 nc, qint32 *ip, double *c); void bitrv2(qint32 n, qint32 *ip, double *a); void cftfsub(qint32 n, double *a, double *w); void rftfsub(qint32 n, double *a, qint32 nc, double *c); void CpxFFT(qint32 n, double *a, double *w); void cft1st(qint32 n, double *a, double *w); void cftmdl(qint32 n, qint32 l, double *a, double *w); void bitrv2conj(int n, int *ip, TYPEREAL *a); void cftbsub(int n, TYPEREAL *a, TYPEREAL *w); bool m_Overload; bool m_Invert; qint32 m_AveCount; qint32 m_TotalCount; qint32 m_FFTSize; qint32 m_LastFFTSize; qint32 m_AveSize; qint32 m_StartFreq; qint32 m_StopFreq; qint32 m_BinMin; qint32 m_BinMax; qint32 m_PlotWidth; double m_K_C; double m_K_B; double m_dBCompensation; double m_SampleFreq; qint32* m_pWorkArea; qint32* m_pTranslateTbl; double* m_pSinCosTbl; double* m_pWindowTbl; double* m_pFFTPwrAveBuf; double* m_pFFTAveBuf; double* m_pFFTSumBuf; double* m_pFFTInBuf; QMutex m_Mutex; //for keeping threads from stomping on each other }; #endif // FFT_H cutesdr-1.0.5/dsp/smeter.cpp0000644000175000017500000001123611546075374015736 0ustar bottomsbottoms// smeter.cpp: implementation of the CS #define ATTACK_TIMECONST .01 //attack time in seconds #define DECAY_TIMECONST .5 //decay time in seconds #define SMETER_CALIBRATION 5.0 //S-Meter calibration offset added to make reading absolute dBm #define MAX_PWR (32767.0*32767.0) CSMeter::CSMeter() { m_PeakMag = 0; m_SampleRate = 1.0; m_AttackAlpha = 1.0; m_DecayAlpha = 1.0; m_AttackAve = -120.0; m_DecayAve = -120.0; } //////////////////////////////////////////////////////////////////////////////////// // Process magnitude data and set S-Meter power variables //////////////////////////////////////////////////////////////////////////////////// void CSMeter::ProcessData(int length, TYPECPX* pInData, TYPEREAL SampleRate) { if(SampleRate != m_SampleRate) { //need to recalculate any values dependent on sample rate m_SampleRate = SampleRate; m_AttackAlpha = (1.0-exp(-1.0/(m_SampleRate*ATTACK_TIMECONST)) ); m_DecayAlpha = (1.0-exp(-1.0/(m_SampleRate*DECAY_TIMECONST)) ); //qDebug()<<"SMeter vals "<m_DecayAve) { //if attack average is greater then must be increasing signal m_AverageMag = m_AttackAve; //use attack average value m_DecayAve = m_AttackAve; //force decay average to attack average } else { //is decreasing strength so use decay average m_AverageMag = m_DecayAve; //use decay average value } if(mag > m_PeakMag) m_PeakMag = mag; //save new peak (reset when read ) } } //////////////////////////////////////////////////////////////////////////////////// // returns Peak S-Meter power variable //////////////////////////////////////////////////////////////////////////////////// TYPEREAL CSMeter::GetPeak() { TYPEREAL x = m_PeakMag; m_PeakMag = 0; return x+SMETER_CALIBRATION; } //////////////////////////////////////////////////////////////////////////////////// // returns Average S-Meter power variable with attack and decay time averaging //////////////////////////////////////////////////////////////////////////////////// TYPEREAL CSMeter::GetAve() { return m_AverageMag + SMETER_CALIBRATION; } cutesdr-1.0.5/dsp/iir.cpp0000644000175000017500000001756311546075374015233 0ustar bottomsbottoms////////////////////////////////////////////////////////////////////// // iir.cpp: implementation of the CIir class. // // This class implements a biquad IIR filter. // // Implements a second order IIR filter stage. // The transfer function of the filter stage implemented is in // the direct 2 form : // // -1 -2 // B0 + B1 z + B2 z // H(z) = -------------------- // -1 -2 // 1 + A1 z + A2 z // // The block diagram used in the implementation is given below: // // input w(n) B0 output // -----> + ----------+-----[>--> + -----> // | | | // | +--+--+ | // | |Delay| | // | +--+--+ | // | -A1 |w(n-1) B1 | // +----<]-----+-------[>--+ // | | | // | +--+--+ | // | |Delay| | // | +--+--+ | // | -A2 |w(n-2) B2 | // +----<]-----+-------[>--+ // w(n) = in - A1*w(n-1) - A2*w(n-2) // out = B0*w(n) + B1*w(n-1) + B2*w(n-2) // w(n-2) = w(n-1) w(n-1) = w(n) //*========================================================================================= // The filter design equations came from a paper by Robert Bristow-Johnson // "Cookbook formulae for audio EQ biquad filter coefficients" // // History: // 2011-02-05 Initial creation MSW // 2011-03-27 Initial release ////////////////////////////////////////////////////////////////////// //========================================================================================== // + + + This Software is released under the "Simplified BSD License" + + + //Copyright 2010 Moe Wheatley. All rights reserved. // //Redistribution and use in source and binary forms, with or without modification, are //permitted provided that the following conditions are met: // // 1. Redistributions of source code must retain the above copyright notice, this list of // conditions and the following disclaimer. // // 2. Redistributions in binary form must reproduce the above copyright notice, this list // of conditions and the following disclaimer in the documentation and/or other materials // provided with the distribution. // //THIS SOFTWARE IS PROVIDED BY Moe Wheatley ``AS IS'' AND ANY EXPRESS OR IMPLIED //WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND //FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL Moe Wheatley OR //CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR //CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR //SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON //ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING //NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF //ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // //The views and conclusions contained in the software and documentation are those of the //authors and should not be interpreted as representing official policies, either expressed //or implied, of Moe Wheatley. //========================================================================================== #include "iir.h" ///////////////////////////////////////////////////////////////////////////////// // Construct CIir object ///////////////////////////////////////////////////////////////////////////////// CIir::CIir() { InitBR( 25000, 1000.0, 100000); } ///////////////////////////////////////////////////////////////////////////////// // Iniitalize IIR variables for Low Pass IIR filter. // analog prototype == H(s) = 1 / (s^2 + s/Q + 1) ///////////////////////////////////////////////////////////////////////////////// void CIir::InitLP( TYPEREAL F0Freq, TYPEREAL FilterQ, TYPEREAL SampleRate) { TYPEREAL w0 = K_2PI * F0Freq/SampleRate; //normalized corner frequency TYPEREAL alpha = sin(w0)/(2.0*FilterQ); TYPEREAL A = 1.0/(1.0 + alpha); //scale everything by 1/A0 for direct form 2 m_B0 = A*( (1.0 - cos(w0))/2.0); m_B1 = A*( 1.0 - cos(w0)); m_B2 = A*( (1.0 - cos(w0))/2.0); m_A1 = A*( -2.0*cos(w0)); m_A2 = A*( 1.0 - alpha); m_w1a = 0.0; m_w2a = 0.0; m_w1b = 0.0; m_w2b = 0.0; } ///////////////////////////////////////////////////////////////////////////////// // Iniitalize IIR variables for High Pass IIR filter. // analog prototype == H(s) = s^2 / (s^2 + s/Q + 1) ///////////////////////////////////////////////////////////////////////////////// void CIir::InitHP( TYPEREAL F0Freq, TYPEREAL FilterQ, TYPEREAL SampleRate) { TYPEREAL w0 = K_2PI * F0Freq/SampleRate; //normalized corner frequency TYPEREAL alpha = sin(w0)/(2.0*FilterQ); TYPEREAL A = 1.0/(1.0 + alpha); //scale everything by 1/A0 for direct form 2 m_B0 = A*( (1.0 + cos(w0))/2.0); m_B1 = -A*( 1.0 + cos(w0)); m_B2 = A*( (1.0 + cos(w0))/2.0); m_A1 = A*( -2.0*cos(w0)); m_A2 = A*( 1.0 - alpha); m_w1a = 0.0; m_w2a = 0.0; m_w1b = 0.0; m_w2b = 0.0; } ///////////////////////////////////////////////////////////////////////////////// // Iniitalize IIR variables for Band Pass IIR filter. // analog prototype == H(s) = (s/Q) / (s^2 + s/Q + 1) ///////////////////////////////////////////////////////////////////////////////// void CIir::InitBP( TYPEREAL F0Freq, TYPEREAL FilterQ, TYPEREAL SampleRate) { TYPEREAL w0 = K_2PI * F0Freq/SampleRate; //normalized corner frequency TYPEREAL alpha = sin(w0)/(2.0*FilterQ); TYPEREAL A = 1.0/(1.0 + alpha); //scale everything by 1/A0 for direct form 2 m_B0 = A * alpha; m_B1 = 0.0; m_B2 = A * -alpha; m_A1 = A*( -2.0*cos(w0)); m_A2 = A*( 1.0 - alpha); m_w1a = 0.0; m_w2a = 0.0; m_w1b = 0.0; m_w2b = 0.0; } ///////////////////////////////////////////////////////////////////////////////// // Iniitalize IIR variables for Band Reject(Notch) IIR filter. // analog prototype == H(s) = (s^2 + 1) / (s^2 + s/Q + 1) ///////////////////////////////////////////////////////////////////////////////// void CIir::InitBR( TYPEREAL F0Freq, TYPEREAL FilterQ, TYPEREAL SampleRate) { TYPEREAL w0 = K_2PI * F0Freq/SampleRate; //normalized corner frequency TYPEREAL alpha = sin(w0)/(2.0*FilterQ); TYPEREAL A = 1.0/(1.0 + alpha); //scale everything by 1/A0 for direct form 2 m_B0 = A*1.0; m_B1 = A*( -2.0*cos(w0)); m_B2 = A*1.0; m_A1 = A*( -2.0*cos(w0)); m_A2 = A*( 1.0 - alpha); m_w1a = 0.0; m_w2a = 0.0; m_w1b = 0.0; m_w2b = 0.0; } ///////////////////////////////////////////////////////////////////////////////// // Process InLength InBuf[] samples and place in OutBuf[] //REAL version ///////////////////////////////////////////////////////////////////////////////// void CIir::ProcessFilter(int InLength, TYPEREAL* InBuf, TYPEREAL* OutBuf) { for(int i=0; i ////////////////////////////////////////////////////////////////////// // Local Defines ////////////////////////////////////////////////////////////////////// #define MAX_WIDTH 4096 //abt 1 mSec at 2MHz #define MAX_DELAY 4096 //abt 1 mSec at 2MHz #define MAX_AVE 32768 //abt 10 mSec at 2MHz #define MAGAVE_TIME 0.005 ////////////////////////////////////////////////////////////////////// // Construction/Destruction ////////////////////////////////////////////////////////////////////// CNoiseProc::CNoiseProc() { m_DelayBuf = new TYPECPX[MAX_DELAY]; m_MagBuf = new TYPEREAL[MAX_AVE]; m_TestBenchDataBuf = new TYPECPX[4096]; SetupBlanker(false, 50.0, 2.0, 1000.0); } CNoiseProc::~CNoiseProc() { if(m_DelayBuf) delete m_DelayBuf; if(m_MagBuf) delete m_MagBuf; if(m_TestBenchDataBuf) delete m_TestBenchDataBuf; } void CNoiseProc::SetupBlanker(bool On, TYPEREAL Threshold, TYPEREAL Width, TYPEREAL SampleRate) { if( (Threshold==m_Threshold) && (Width==m_Width) && (m_On == On) && (SampleRate==SampleRate) ) { return; } m_Mutex.lock(); m_On = On; m_Threshold = Threshold; m_Width = Width; m_SampleRate = SampleRate; m_WidthSamples = Width * 1e-6 * m_SampleRate; if(m_WidthSamples < 1) m_WidthSamples = 1; else if(m_WidthSamples>MAX_WIDTH) m_WidthSamples = MAX_WIDTH; m_MagSamples = MAGAVE_TIME*m_SampleRate; m_Ratio = .005*(m_Threshold)*(TYPEREAL)m_MagSamples; m_DelaySamples = m_WidthSamples/2; // qDebug()<<"m_DelaySamples="<DisplayData(InLength, pOutData, m_SampleRate,PROFILE_7); return; } m_Mutex.lock(); //StartPerformance(); for(int i=0; imim) ? mre : mim; //m_TestBenchDataBuf[i].im = mag; //calc moving average of "m_MagSamples" m_MagAveSum -= m_MagBuf[m_Mptr]; //subtract oldest sample m_MagAveSum += mag; //add new sample m_MagBuf[m_Mptr++] = mag; //stick in buffer if(m_Mptr > m_MagSamples) m_Mptr = 0; //pull out oldest sample from delay buffer and put in new one oldest = m_DelayBuf[m_Dptr]; m_DelayBuf[m_Dptr++] = newsamp; if(m_Dptr > m_DelaySamples) m_Dptr = 0; if( mag*m_Ratio > m_MagAveSum ) { m_BlankCounter = m_WidthSamples; } m_TestBenchDataBuf[i].im = oldest.re + oldest.im; if(m_BlankCounter) { m_BlankCounter--; m_TestBenchDataBuf[i].re = 0.0; pOutData[i].re = 0.0; pOutData[i].im = 0.0; } else { m_TestBenchDataBuf[i].re = m_MagAveSum/m_Ratio; pOutData[i] = oldest; } } m_Mutex.unlock(); //StopPerformance(InLength); g_pTestBench->DisplayData(InLength, m_TestBenchDataBuf, m_SampleRate,PROFILE_7); } cutesdr-1.0.5/dsp/wfmdemod.h0000644000175000017500000000715611711303214015671 0ustar bottomsbottoms////////////////////////////////////////////////////////////////////// // wfmdemod.h: interface for the CWFmDemod class. // // History: // 2011-07-24 Initial creation MSW // 2011-08-05 Initial release ///////////////////////////////////////////////////////////////////// #ifndef WFMDEMOD_H #define WFMDEMOD_H #include "dsp/datatypes.h" #include "dsp/fir.h" #include "dsp/iir.h" #include "dsp/downconvert.h" #include "dsp/rbdsconstants.h" #define PHZBUF_SIZE 16384 #define RDS_Q_SIZE 100 class CWFmDemod { public: CWFmDemod(TYPEREAL samplerate); virtual ~CWFmDemod(); TYPEREAL SetSampleRate(TYPEREAL samplerate, bool USver); //overloaded functions for mono and stereo int ProcessData(int InLength, TYPECPX* pInData, TYPECPX* pOutData); int ProcessData(int InLength, TYPECPX* pInData, TYPEREAL* pOutData); TYPEREAL GetDemodRate(){return m_OutRate;} int GetNextRdsGroupData(tRDS_GROUPS* pGroupData); int GetStereoLock(int* pPilotLock); private: void InitPll( TYPEREAL SampleRate ); void ProcessPll( int InLength, TYPECPX* pInData, TYPEREAL* pOutData ); void InitDeemphasis( TYPEREAL Time, TYPEREAL SampleRate); //create De-emphasis LP filter void ProcessDeemphasisFilter(int InLength, TYPEREAL* InBuf, TYPEREAL* OutBuf); void ProcessDeemphasisFilter(int InLength, TYPECPX* InBuf, TYPECPX* OutBuf); void InitPilotPll( TYPEREAL SampleRate ); bool ProcessPilotPll( int InLength, TYPECPX* pInData ); void InitRds( TYPEREAL SampleRate ); void ProcessRdsPll( int InLength, TYPECPX* pInData, TYPEREAL* pOutData ); inline TYPEREAL arctan2(TYPEREAL y, TYPEREAL x); void ProcessNewRdsBit(int bit); quint32 CheckBlock(quint32 BlockOffset, int UseFec); void CreateCallSign(quint16 PIcode); TYPEREAL m_SampleRate; TYPEREAL m_OutRate; TYPEREAL m_RawFm[PHZBUF_SIZE]; TYPECPX m_CpxRawFm[PHZBUF_SIZE]; CDecimateBy2* m_pDecBy2A; CDecimateBy2* m_pDecBy2B; CDecimateBy2* m_pDecBy2C; TYPECPX m_D0; //complex delay line variables TYPECPX m_D1; TYPECPX m_D2; TYPECPX m_D3; TYPECPX m_D4; TYPECPX m_D5; TYPEREAL m_DeemphasisAveRe; TYPEREAL m_DeemphasisAveIm; TYPEREAL m_DeemphasisAlpha; CIir m_MonoLPFilter; CFir m_LPFilter; CIir m_NotchFilter; CIir m_PilotBPFilter; CFir m_HilbertFilter; int m_PilotLocked; //variables for Pilot PLL int m_LastPilotLocked; TYPEREAL m_PilotNcoPhase; TYPEREAL m_PilotNcoFreq; TYPEREAL m_PilotNcoAcc; TYPEREAL m_PilotNcoLLimit; TYPEREAL m_PilotNcoHLimit; TYPEREAL m_PilotPllAlpha; TYPEREAL m_PilotPllBeta; TYPEREAL m_PhaseErrorMagAve; TYPEREAL m_PhaseErrorMagAlpha; TYPEREAL m_PilotPhase[PHZBUF_SIZE]; TYPEREAL m_PilotPhaseAdjust; TYPEREAL m_RdsNcoPhase; //variables for RDS PLL TYPEREAL m_RdsNcoFreq; TYPEREAL m_RdsNcoAcc; TYPEREAL m_RdsNcoLLimit; TYPEREAL m_RdsNcoHLimit; TYPEREAL m_RdsPllAlpha; TYPEREAL m_RdsPllBeta; TYPECPX m_RdsRaw[PHZBUF_SIZE]; //variables for RDS processing TYPEREAL m_RdsMag[PHZBUF_SIZE]; TYPEREAL m_RdsData[PHZBUF_SIZE]; TYPEREAL m_RdsMatchCoef[PHZBUF_SIZE]; TYPEREAL m_RdsLastSync; TYPEREAL m_RdsLastSyncSlope; TYPEREAL m_RdsLastData; int m_MatchCoefLength; CDownConvert m_RdsDownConvert; CFir m_RdsBPFilter; CFir m_RdsMatchedFilter; CIir m_RdsBitSyncFilter; TYPEREAL m_RdsOutputRate; int m_RdsLastBit; tRDS_GROUPS m_RdsGroupQueue[RDS_Q_SIZE]; int m_RdsQHead; int m_RdsQTail; tRDS_GROUPS m_LastRdsGroup; quint32 m_InBitStream; //input shift register for incoming raw data int m_CurrentBlock; int m_CurrentBitPosition; int m_DecodeState; int m_BGroupOffset; int m_BlockErrors; quint16 m_BlockData[4]; }; #endif // WFMDEMOD_H cutesdr-1.0.5/dsp/fastfir.cpp0000644000175000017500000002505111711303214016052 0ustar bottomsbottoms////////////////////////////////////////////////////////////////////// // fastfir.cpp: implementation of the CFastFIR class. // // This class implements a FIR Bandpass filter using a FFT convolution algorithm //The filter is complex and is specified with 3 parameters: // sample frequency, Hicut and Lowcut frequency // //Uses FFT overlap and save method of implementing the FIR. //For best performance use FIR size 4*FIR <= FFT <= 8*FIR //If need output to be power of 2 then FIR must = 1/2FFT size // // History: // 2010-09-15 Initial creation MSW // 2011-03-27 Initial release // 2011-11-03 Fixed m_pFFTOverlapBuf initialization bug ////////////////////////////////////////////////////////////////////// //========================================================================================== // + + + This Software is released under the "Simplified BSD License" + + + //Copyright 2010 Moe Wheatley. All rights reserved. // //Redistribution and use in source and binary forms, with or without modification, are //permitted provided that the following conditions are met: // // 1. Redistributions of source code must retain the above copyright notice, this list of // conditions and the following disclaimer. // // 2. Redistributions in binary form must reproduce the above copyright notice, this list // of conditions and the following disclaimer in the documentation and/or other materials // provided with the distribution. // //THIS SOFTWARE IS PROVIDED BY Moe Wheatley ``AS IS'' AND ANY EXPRESS OR IMPLIED //WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND //FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL Moe Wheatley OR //CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR //CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR //SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON //ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING //NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF //ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // //The views and conclusions contained in the software and documentation are those of the //authors and should not be interpreted as representing official policies, either expressed //or implied, of Moe Wheatley. //========================================================================================== #include "dsp/fastfir.h" #include #include #include "interface/perform.h" #include #include ////////////////////////////////////////////////////////////////////// // Local Defines ////////////////////////////////////////////////////////////////////// #define CONV_FFT_SIZE 2048 //must be power of 2 #define CONV_FIR_SIZE 1025 //must be <= FFT size. Make 1/2 +1 if want //output to be in power of 2 #define CONV_INBUF_SIZE (CONV_FFT_SIZE+CONV_FIR_SIZE-1) ////////////////////////////////////////////////////////////////////// // Construction/Destruction ////////////////////////////////////////////////////////////////////// CFastFIR::CFastFIR() { int i; m_pWindowTbl = NULL; m_pFFTBuf = NULL; m_pFFTOverlapBuf = NULL; m_pFilterCoef = NULL; //allocate internal buffer space on Heap m_pWindowTbl = new TYPEREAL[CONV_FFT_SIZE]; m_pFilterCoef = new TYPECPX[CONV_FFT_SIZE]; m_pFFTBuf = new TYPECPX[CONV_FFT_SIZE]; m_pFFTOverlapBuf = new TYPECPX[CONV_FIR_SIZE]; if(!m_pWindowTbl || !m_pFilterCoef || !m_pFFTBuf || !m_pFFTOverlapBuf) { //major poblems if memory fails here return; } m_InBufInPos = (CONV_FIR_SIZE - 1); for( i=0; i= FHiCut) || (FLoCut >= SampleRate/2.0) || (FLoCut <= -SampleRate/2.0) || (FHiCut >= SampleRate/2.0) || (FHiCut <= -SampleRate/2.0) ) { qDebug()<<"Filter Parameter error"; return; } //qDebug()<<"FLowCut="<= 0 ) { //keep copy of last CONV_FIR_SIZE-1 samples for overlap save m_pFFTOverlapBuf[j] = InBuf[i]; } m_pFFTBuf[m_InBufInPos++] = InBuf[i++]; if(m_InBufInPos >= CONV_FFT_SIZE) { //perform FFT -> complexMultiply by FIR coefficients -> inverse FFT on filled FFT input buffer m_Fft.FwdFFT(m_pFFTBuf); CpxMpy(CONV_FFT_SIZE, m_pFilterCoef, m_pFFTBuf, m_pFFTBuf); m_Fft.RevFFT(m_pFFTBuf); for(j=(CONV_FIR_SIZE-1); j //pick a method of calculating the NCO #define NCO_LIB 0 //normal sin cos library (188nS) #define NCO_OSC 1 //quadrature oscillator (25nS) #define NCO_VCASM 0 //Visual C assembly call to floating point sin/cos instruction #define NCO_GCCASM 0 //GCC assembly call to floating point sin/cos instruction (100nS) #define MIN_OUTPUT_RATE (7900.0*2.0) #define MAX_HALF_BAND_BUFSIZE 32768 ////////////////////////////////////////////////////////////////////// // Construction/Destruction ////////////////////////////////////////////////////////////////////// CDownConvert::CDownConvert() { int i; m_NcoInc = 0.0; m_NcoTime = 0.0; m_NcoFreq = 0.0; m_CW_Offset = 0.0; m_InRate = 100000.0; m_MaxBW = 10000.0; for(i=0; i (m_MaxBW / HB51TAP_MAX) ) && (f > MIN_OUTPUT_RATE) ) { if(f >= (m_MaxBW / CIC3_MAX) ) //See if can use CIC order 3 m_pDecimatorPtrs[n++] = new CCicN3DecimateBy2; else if(f >= (m_MaxBW / HB11TAP_MAX) ) //See if can use fixed 11 Tap Halfband m_pDecimatorPtrs[n++] = new CHalfBand11TapDecimateBy2(); else if(f >= (m_MaxBW / HB15TAP_MAX) ) //See if can use Halfband 15 Tap m_pDecimatorPtrs[n++] = new CHalfBandDecimateBy2(HB15TAP_LENGTH, HB15TAP_H); else if(f >= (m_MaxBW / HB19TAP_MAX) ) //See if can use Halfband 19 Tap m_pDecimatorPtrs[n++] = new CHalfBandDecimateBy2(HB19TAP_LENGTH, HB19TAP_H); else if(f >= (m_MaxBW / HB23TAP_MAX) ) //See if can use Halfband 23 Tap m_pDecimatorPtrs[n++] = new CHalfBandDecimateBy2(HB23TAP_LENGTH, HB23TAP_H); else if(f >= (m_MaxBW / HB27TAP_MAX) ) //See if can use Halfband 27 Tap m_pDecimatorPtrs[n++] = new CHalfBandDecimateBy2(HB27TAP_LENGTH, HB27TAP_H); else if(f >= (m_MaxBW / HB31TAP_MAX) ) //See if can use Halfband 31 Tap m_pDecimatorPtrs[n++] = new CHalfBandDecimateBy2(HB31TAP_LENGTH, HB31TAP_H); else if(f >= (m_MaxBW / HB35TAP_MAX) ) //See if can use Halfband 35 Tap m_pDecimatorPtrs[n++] = new CHalfBandDecimateBy2(HB35TAP_LENGTH, HB35TAP_H); else if(f >= (m_MaxBW / HB39TAP_MAX) ) //See if can use Halfband 39 Tap m_pDecimatorPtrs[n++] = new CHalfBandDecimateBy2(HB39TAP_LENGTH, HB39TAP_H); else if(f >= (m_MaxBW / HB43TAP_MAX) ) //See if can use Halfband 43 Tap m_pDecimatorPtrs[n++] = new CHalfBandDecimateBy2(HB43TAP_LENGTH, HB43TAP_H); else if(f >= (m_MaxBW / HB47TAP_MAX) ) //See if can use Halfband 47 Tap m_pDecimatorPtrs[n++] = new CHalfBandDecimateBy2(HB47TAP_LENGTH, HB47TAP_H); else if(f >= (m_MaxBW / HB51TAP_MAX) ) //See if can use Halfband 51 Tap m_pDecimatorPtrs[n++] = new CHalfBandDecimateBy2(HB51TAP_LENGTH, HB51TAP_H); f /= 2.0; } m_Mutex.unlock(); m_OutputRate = f; SetFrequency(m_NcoFreq); qDebug()<<"Filters "<DecBy2(n, pInData, pInData); //if(1==j) //g_pTestBench->DisplayData(n, (TYPECPX*)pInData, 615385/2.0); } m_Mutex.unlock(); for(i=0; i #define MAX_DECSTAGES 10 //one more than max to make sure is a null at end of list ////////////////////////////////////////////////////////////////////////////////// // Main Downconverter Class ////////////////////////////////////////////////////////////////////////////////// class CDownConvert { public: CDownConvert(); virtual ~CDownConvert(); void SetFrequency(TYPEREAL NcoFreq); void SetCwOffset(TYPEREAL offset){m_CW_Offset= offset;} int ProcessData(int InLength, TYPECPX* pInData, TYPECPX* pOutData); TYPEREAL SetDataRate(TYPEREAL InRate, TYPEREAL MaxBW); TYPEREAL SetWfmDataRate(TYPEREAL InRate, TYPEREAL MaxBW); private: //////////// //pure abstract base class for all the different types of decimate by 2 stages //DecBy2 function is defined in derived classes //////////// class CDec2 { public: CDec2(){} virtual ~CDec2(){} virtual int DecBy2(int InLength, TYPECPX* pInData, TYPECPX* pOutData) = 0; }; //////////// //private class for the Half Band decimate by 2 stages //////////// class CHalfBandDecimateBy2 : public CDec2 { public: CHalfBandDecimateBy2(int len,const TYPEREAL* pCoef); ~CHalfBandDecimateBy2(){if(m_pHBFirBuf) delete m_pHBFirBuf;} int DecBy2(int InLength, TYPECPX* pInData, TYPECPX* pOutData); TYPECPX* m_pHBFirBuf; int m_FirLength; const TYPEREAL* m_pCoef; }; //////////// //private class for the fixed 11 tap Half Band decimate by 2 stages //////////// class CHalfBand11TapDecimateBy2 : public CDec2 { public: CHalfBand11TapDecimateBy2(); ~CHalfBand11TapDecimateBy2(){} int DecBy2(int InLength, TYPECPX* pInData, TYPECPX* pOutData); TYPEREAL H0; //unwrapped coeeficients TYPEREAL H2; TYPEREAL H4; TYPEREAL H5; TYPEREAL H6; TYPEREAL H8; TYPEREAL H10; TYPECPX d0; //unwrapped delay buffer TYPECPX d1; TYPECPX d2; TYPECPX d3; TYPECPX d4; TYPECPX d5; TYPECPX d6; TYPECPX d7; TYPECPX d8; TYPECPX d9; }; //////////// //private class for the N=3 CIC decimate by 2 stages //////////// class CCicN3DecimateBy2 : public CDec2 { public: CCicN3DecimateBy2(); ~CCicN3DecimateBy2(){} int DecBy2(int InLength, TYPECPX* pInData, TYPECPX* pOutData); TYPECPX m_Xodd; TYPECPX m_Xeven; }; private: //private helper functions void DeleteFilters(); TYPEREAL m_OutputRate; TYPEREAL m_NcoFreq; TYPEREAL m_CW_Offset; TYPEREAL m_NcoInc; TYPEREAL m_NcoTime; TYPEREAL m_InRate; TYPEREAL m_MaxBW; TYPECPX m_Osc1; TYPEREAL m_OscCos; TYPEREAL m_OscSin; QMutex m_Mutex; //for keeping threads from stomping on each other //array of pointers for performing decimate by 2 stages CDec2* m_pDecimatorPtrs[MAX_DECSTAGES]; }; #endif // DOWNCONVERT_H cutesdr-1.0.5/siqs/0000755000175000017500000000000011720506076014111 5ustar bottomsbottomscutesdr-1.0.5/siqs/discover.h0000644000175000017500000000636511711273721016110 0ustar bottomsbottoms// // sdriq-ftdi: An attempt to create a Unix server for RF-SPACE SDR-IQ // // Copyright (C) 2012 Andrea Montefusco IW0HDV // // 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, either 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 for more details. // // You should have received a copy of the GNU General Public License // along with this program. If not, see . // #if !defined __DISCOVER_H__ #define __DISCOVER_H__ #include #include #include #include #include #include using namespace std; #ifdef __cplusplus extern "C" { #endif // // Excerpted from the sdrxx original code // typedef struct __attribute__((__packed__)) _COMMON__DISCOVER_MSG_SDRXX { // 56 fixed common byte fields unsigned char length[2]; //length of total message in bytes (little endian byte order) unsigned char key[2]; //fixed key key[0]==0x5A key[1]==0xA5 unsigned char op; //0==Request(to device) 1==Response(from device) 2 ==Set(to device) char name[16]; //Device name string null terminated char sn[16]; //Serial number string null terminated unsigned char ipaddr[16]; //device IP address (little endian byte order) unsigned char port[2]; //device Port number (little endian byte order) unsigned char customfield; //Specify a custom data field for a particular device } COMMON_DISCOVER_MSG_SDRXX; typedef struct __attribute__((__packed__)) _EXTENDED__DISCOVER_MSG_SDRXX { COMMON_DISCOVER_MSG_SDRXX c; // start of optional variable custom byte fields unsigned char fwver[2]; //Firmware version*100 (little endian byte order)(read only) unsigned char btver[2]; //Boot version*100 (little endian byte order) (read only) unsigned char subnet[4]; //IP subnet mask (little endian byte order) unsigned char gwaddr[4]; //gateway address (little endian byte order) char connection[32]; //interface connection string null terminated(ex: COM3, DEVTTY5, etc) unsigned char status; //bit 0 == TCP connected Bit 1 == running Bit 2-7 not defined unsigned char future[15]; //future use } EXTENDED_DISCOVER_MSG_SDRXX; #define STATUS_BIT_CONNECTED (1) #define STATUS_BIT_RUNNING (2) #define KEY0 0x5A #define KEY1 0xA5 #define MSG_REQ 0 #define MSG_RESP 1 #define MSG_SET 2 #ifdef __cplusplus }; #endif #include "minithread.h" #include "netsupport.h" class DiscoverSdrxx: public MiniThread { public: DiscoverSdrxx (unsigned short p = 50000); ~DiscoverSdrxx () {} int threadproc (); in_addr_t get_addresses(const int domain, const char *if_name); private: const static unsigned short discover_port = 48321; const static unsigned short discover_client_port = 48322; int sock; unsigned int myIP; unsigned short myPort; unsigned short myBootVer; unsigned short myFirmVer; char hostname [16]; }; #endif cutesdr-1.0.5/siqs/netsupport.h0000644000175000017500000000653711714313226016514 0ustar bottomsbottoms// // sdriq-ftdi: An attempt to create a Unix server for RF-SPACE SDR-IQ // // Copyright (C) 2012 Andrea Montefusco IW0HDV // // 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, either 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 for more details. // // You should have received a copy of the GNU General Public License // along with this program. If not, see . // #if !defined __NETSUPPORT_H__ #define __NETSUPPORT_H__ #define LISTEN_PORT 50000 #include #include #include #include #include #include #include #include #include #include #include "logger.h" #include "minithread.h" struct SysException { SysException (int eno, char *pMsg, const char *pDesc = ""): errno_(eno), msg_(pMsg), desc_(pDesc) {} int errno_; char * msg_; const char *desc_; }; template class Listener: public MiniThread { public: Listener (T *p, int port = LISTEN_PORT):lis_port(port), pExec(p) {} int getSocket () { return sock; } int threadproc () { //int address_length; struct sockaddr_in address; int on=1; // create TCP socket to listen on s=socket(AF_INET,SOCK_STREAM,0); if(s < 0) { //perror(); throw SysException(errno, strerror(errno), "Listen socket failed"); } setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)); // bind to listening port memset(&address,0,sizeof(address)); address.sin_family=AF_INET; address.sin_addr.s_addr=INADDR_ANY; address.sin_port=htons(lis_port); if(bind(s,(struct sockaddr*)&address,sizeof(address))<0) { throw SysException(errno, strerror(errno), "Command bind failed"); } while (1) { fprintf(stderr,"Listening for TCP connections on port %d socket: %d\n",LISTEN_PORT, s); if(listen(s,5)<0) { throw SysException(errno, strerror(errno), "Command listen failed"); } address_length=sizeof(address); iq_port=-1; if((sock=accept(s,(struct sockaddr*)&address, (socklen_t *)&address_length)) < 0) { throw SysException(errno, strerror(errno), "Command accept failed"); } else { DEBUG(DBG_WARN, "client socket " << sock); DEBUG(DBG_WARN, "client connected: " << inet_ntoa(address.sin_addr) << ":" << ntohs(address.sin_port)); // // allow for one only instance at time // pExec->setSocket(sock); pExec->run(); pExec->wait(); } } } private: int address_length; struct sockaddr_in address; int iq_port; int lis_port; int s; pthread_t thread_id; protected: int sock; T *pExec; }; #endif cutesdr-1.0.5/siqs/siqs_serial.cpp0000644000175000017500000005144211711273721017137 0ustar bottomsbottoms// // sdriq-cute: An attempt to create a Unix server for RF-SPACE SDR-IQ // Copyright (C) 2012 Andrea Montefusco IW0HDV // // 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, either 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 for more details. // // You should have received a copy of the GNU General Public License // along with this program. If not, see . // ///// g++ -msse3 siqs.cpp -o siqs -lpthread && ./siqs #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define LENGTH_MASK 0x1FFF #include using namespace std; /** * * open_sdriq_dev * * * VMIN = 0 and VTIME = 0 * * This is a completely non-blocking read - the * call is satisfied immediately directly from the driver's input queue. * If data are available, it's transferred to the caller's buffer up to * nbytes and returned. Otherwise zero is immediately returned to * indicate "no data". We'll note that this is "polling" of the serial * port, and it's almost always a bad idea. If done repeatedly, it can * consume enormous amounts of processor time and is highly inefficient. * Don't use this mode unless you really, really know what you're doing. * * * * VMIN = 0 and VTIME > 0 * * This is a pure timed read. If data are * available in the input queue, it's transferred to the caller's buffer * up to a maximum of nbytes, and returned immediately to the caller. * Otherwise the driver blocks until data arrives, or when VTIME tenths * expire from the start of the call. If the timer expires without data, * zero is returned. A single byte is sufficient to satisfy this read * call, but if more is available in the input queue, it's returned to the * caller. Note that this is an overall timer, not an intercharacter one. * * * * VMIN > 0 and VTIME > 0 * * A read() is satisfied when either VMIN * characters have been transferred to the caller's buffer, or when VTIME * tenths expire between characters. Since this timer is not started * until the first character arrives, this call can block indefinitely if * the serial line is idle. This is the most common mode of operation, * and we consider VTIME to be an intercharacter timeout, not an overall * one. This call should never return zero bytes read. * * * * VMIN > 0 and VTIME = 0 * * This is a counted read that is satisfied only * when at least VMIN characters have been transferred to the caller's * buffer - there is no timing component involved. This read can be * satisfied from the driver's input queue (where the call could return * immediately), or by waiting for new data to arrive: in this respect the * call could block indefinitely. We believe that it's undefined behavior * if nbytes is less then VMIN. * * */ int open_sdriq_dev (const string name, string &msg) { struct termios newtio; int sdriq_fd = -1; if (name.find("/dev/ttyUSB") != string::npos) { // use ftdi_sio driver sdriq_fd = open(name.c_str(), O_RDWR | O_NOCTTY); if (sdriq_fd < 0) { msg = "Open SDR-IQ : " + string(strerror(errno)); return -1; } bzero(&newtio, sizeof(newtio)); newtio.c_cflag = CS8 | CLOCAL | CREAD; newtio.c_iflag = IGNPAR; newtio.c_oflag = 0; //cfsetispeed(&newtio, B230400); //cfsetospeed(&newtio, B230400); int ispeed = cfsetispeed(&newtio, B230400); int ospeed = cfsetospeed(&newtio, B230400); newtio.c_lflag = 0; newtio.c_cc[VTIME] = 1; // specify blocking read newtio.c_cc[VMIN] = 0; tcflush(sdriq_fd, TCIFLUSH); int rc = tcsetattr(sdriq_fd, TCSANOW, &newtio); //fprintf (stderr, "%d %d %d\n", ispeed, ospeed, rc); int nr; unsigned char ch; fprintf (stderr, "Flushing the serial...."); while ((nr = read(sdriq_fd, &ch, sizeof(ch))) > 0) { fprintf (stderr, " %d", nr); } fprintf (stderr, "\n"); } else { // use ft245 or similar driver sdriq_fd = open(name.c_str(), O_RDWR | O_NONBLOCK); if (sdriq_fd < 0) { msg = "Open SDR-IQ: " + string(strerror(errno)); } } return sdriq_fd; } void dump (unsigned char*buf, int len) { for (int i =0; i < len; ++i) { fprintf (stderr, "%02X " , buf[i]); } fprintf (stderr, "\n"); fflush(stderr); } class MiniThread { public: MiniThread (): thread_id(-1) {} ~MiniThread () {} virtual int threadproc () = 0; private: pthread_t thread_id; int stop_f; static void *thread (void *arg) { MiniThread *p = (MiniThread *) arg; p->threadproc (); return p; } public: int have_to_stop () { return !stop_f ; } int run() { stop_f = 0; int rc = pthread_create (&thread_id,NULL, thread,(void *)this); if(rc<0) { perror("pthread_create MiniThread failed"); exit(1); } } int wait () { void *ret; if (thread_id != -1) { int rc = pthread_join (thread_id, &ret); } } int stop () { void *ret; if (thread_id != -1) { stop_f = -1; int rc = pthread_join (thread_id, &ret); if (rc) { } } } }; #define LISTEN_PORT 50000 template class Listener: public MiniThread { public: Listener (T *p, int port = LISTEN_PORT):lis_port(port), pExec(p) {} int getSocket () { return sock; } int threadproc (); private: int address_length; struct sockaddr_in address; int iq_port; int lis_port; int s; pthread_t thread_id; protected: int sock; T *pExec; }; template int Listener::threadproc () { //int address_length; struct sockaddr_in address; int rc; int on=1; // create TCP socket to listen on s=socket(AF_INET,SOCK_STREAM,0); if(s < 0) { perror("Listen socket failed"); exit(1); } setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)); // bind to listening port memset(&address,0,sizeof(address)); address.sin_family=AF_INET; address.sin_addr.s_addr=INADDR_ANY; address.sin_port=htons(lis_port); if(bind(s,(struct sockaddr*)&address,sizeof(address))<0) { perror("Command bind failed"); exit(1); } while (1) { fprintf(stderr,"Listening for TCP connections on port %d socket: %d\n",LISTEN_PORT, s); if(listen(s,5)<0) { perror("Command listen failed"); exit(1); } address_length=sizeof(address); iq_port=-1; if((sock=accept(s,(struct sockaddr*)&address, (socklen_t *)&address_length)) < 0) { perror("Command accept failed"); exit(1); } fprintf(stderr,"client socket %d\n",sock); fprintf(stderr,"client connected: %s:%d\n",inet_ntoa(address.sin_addr),ntohs(address.sin_port)); // // allow for one only instance at time // pExec->setSocket(sock); pExec->run(); pExec->wait(); } } /** * Radio class * * Run on his own thread, read the serial interface (emulated on USB) * Decodes the ASCP messages and send it to CuteSDR via IP * * we need to decode each message because the data messages containing the * I/Q samples are to be forwarded back to CuteSDR via UDP packets * whilst the others are pushd into the TCP socket */ class Radio: public MiniThread { public: enum MsgState { MSGSTATE_HDR1 = 0, MSGSTATE_HDR2 = 1, MSGSTATE_DATA = 2 } msgState; Radio (int s, int u); ~Radio(); int process (unsigned char ch); int threadproc (); private: void udp_write (unsigned char *msg, int len); //void SendUDPPacketToSocket( unsigned char* pData); int m_UDPSocket; short seqNumber; sockaddr m_ClientTCPAddress; sockaddr_in m_ClientUDPAddress; int sock; int usb_fd; unsigned char ascpMsg[10240]; int msgLen; int msgIndex; pthread_t thread_id; // timing variable timespec time_start, time_end, time_diff; public: }; Radio::Radio (int s, int u): sock(s), usb_fd(u) { msgState = MSGSTATE_HDR1; msgLen = 0; msgIndex = 0; m_UDPSocket = -1; seqNumber = 0; fprintf (stderr, "Radio::Radio: %d %d\n", sock, usb_fd); socklen_t addrlen = sizeof(m_ClientTCPAddress); if (getpeername(sock, &m_ClientTCPAddress, &addrlen)) { perror("getpeername failed"); exit(1); } else { m_ClientUDPAddress.sin_family = AF_INET; m_ClientUDPAddress.sin_addr = ((sockaddr_in *)&m_ClientTCPAddress)->sin_addr; m_ClientUDPAddress.sin_port = htons(50000); // // create the UDP Socket for I/Q sending // m_UDPSocket = socket(PF_INET,SOCK_DGRAM,IPPROTO_UDP); if(m_UDPSocket < 0) { perror("create socket failed for I/Q socket\n"); } else { int buffsize = 1000000; setsockopt(m_UDPSocket, SOL_SOCKET, SO_RCVBUF, (const char*)&buffsize, sizeof(buffsize)); } } } Radio :: ~Radio () { close (usb_fd); close (m_UDPSocket); close (sock); } timespec diff(timespec start, timespec end) { timespec temp; if ((end.tv_nsec - start.tv_nsec) < 0) { temp.tv_sec = end.tv_sec - start.tv_sec - 1; temp.tv_nsec = 1000000000 + end.tv_nsec - start.tv_nsec; } else { temp.tv_sec = end.tv_sec - start.tv_sec; temp.tv_nsec = end.tv_nsec - start.tv_nsec; } return temp; } /** * * Called send a 1024 byte ASCP formated message to the Client UDP Socket * Add a two byte sequence number to the header * */ //void Radio::SendUDPPacketToSocket( unsigned char* pData) //{ //} void Radio::udp_write( unsigned char *msg, int length) { //break 8192 byte packet into eight 1024 ASCP packets for(int i=2; i<8194; i+=1024) { unsigned char dataMsg [10240]; if(m_UDPSocket == -1) return; dataMsg[0] = 0x04; //data item0 length 0x404 = 1024+4 dataMsg[1] = 0x84; dataMsg[2] = seqNumber & 0xFF; dataMsg[3] = (seqNumber >> 8) & 0xFF; memcpy(&dataMsg[4], (msg+i), 1024); int result = sendto(m_UDPSocket, (char*)dataMsg, 1024+4, 0, (struct sockaddr *)&m_ClientUDPAddress,sizeof(m_ClientUDPAddress) ); if (result < 0) { perror( "!!!!!!!!!!! unable to write into UDP socket !"); } else if(result != (1024+4) ) { fprintf (stderr, "!!!!!!!!!!! partial write into UDP socket !"); // Something bad happened on the socket. Deal with it. //StopSDRxx(); //ShutdownConnection(m_ClientSocket); //m_SocketSstatus = SOCKET_WAITINGCONNECT; //m_USBReInit = TRUE; //signal USB port so it can reinitialize } seqNumber++; if ((seqNumber % 1024) == 0) { clock_gettime(CLOCK_REALTIME, &time_end); time_diff = ::diff(time_start, time_end); long double diff_s = time_diff.tv_sec + (time_diff.tv_nsec/1E9) ; //fprintf(stderr, "diff: %ds::%dns %Lf seconds\n", time_diff.tv_sec, time_diff.tv_nsec, diff_s); long ts = 1024 * (1024 / 4); fprintf (stderr, "Samples received: %lu, %.1Lf kS/s\n", ts, ((double)ts / (diff_s)/1E3) ); clock_gettime (CLOCK_REALTIME, &time_start); } if( 0 == seqNumber) //if seq number rollover seqNumber = 1; //dont use zero as seq number } } int Radio :: process (unsigned char ch) { // // reading from radio serial and decoding // the messages // we need to decode because the data messages, containing the // I/Q samples, are to be forwarded back to CuteSDR via UDP packets // switch(msgState) //state machine to get generic ASCP msg from TCP data stream { case MSGSTATE_HDR1: //get first header byte of ASCP msg ascpMsg[0] = ch; msgState = MSGSTATE_HDR2; //go to get second header byte state msgLen = 0; msgIndex = 0; break; case MSGSTATE_HDR2: //here for second byte of header ascpMsg[1] = ch; msgLen = ((ascpMsg[1] << 8) | ascpMsg[0] ) & LENGTH_MASK; // 0x03ff msgIndex = 2; if(2 == msgLen) {//if msg has no parameters then we are done msgState = MSGSTATE_HDR1; //go back to first statewrite ( sock, ascpMsg, msgLen ); #ifdef DEBUG fprintf (stderr, "%s: writing in TCP socket %d bytes: ", __FUNCTION__, msgLen); dump (ascpMsg, msgLen); #endif write ( sock, ascpMsg, msgLen ); msgLen = 0; msgIndex = 0; } else { //there are data bytes to fetch if (0 == msgLen) msgLen = 8192+2; //handle special case of 8192 byte data message msgState = MSGSTATE_DATA; //go to data byte reading state } break; case MSGSTATE_DATA: //try to read the rest of the message ascpMsg[msgIndex++] = ch; if( msgIndex >= msgLen ) { msgState = MSGSTATE_HDR1; //go back to first stage // // analyze if the message has to be sent via UDP // if (ascpMsg[1] == 0x80 && ascpMsg[0] == 0x00) { udp_write ( ascpMsg, msgLen ); } else { #ifdef DEBUG fprintf (stderr, "%s: writing in TCP socket %d bytes: ", __FUNCTION__, msgLen); dump (ascpMsg, msgLen); #endif write (sock, ascpMsg, msgLen ); } msgIndex = 0; } break; } //end switch statement } int Radio :: threadproc () { unsigned char buf [64]; fprintf (stderr, "TTT %s, entering reading loop....\n", __FUNCTION__); clock_gettime (CLOCK_REALTIME, &time_start); while (1) { // read from serial int len = read (usb_fd, buf, sizeof(buf)); if (len > 0) { #ifdef DEBUG fprintf (stderr, "%s: read from serial: %d: ", __FUNCTION__, len); dump (buf, len); #endif for (int i=0; irun(); } int threadproc (); int process (unsigned char ch); }; /** * reading from radio serial and decoding the messages * * */ int Cute :: process (unsigned char ch) { switch(msgState) //state machine to get generic ASCP msg from TCP data stream { case MSGSTATE_HDR1: //get first header byte of ASCP msg ascpMsg[0] = ch; msgState = MSGSTATE_HDR2; //go to get second header byte state msgLen = 0; msgIndex = 0; break; case MSGSTATE_HDR2: //here for second byte of header ascpMsg[1] = ch; msgLen = ((ascpMsg[1] << 8) | ascpMsg[0] ) & LENGTH_MASK; msgIndex = 2; if(2 == msgLen) {//if msg has no parameters then we are done msgState = MSGSTATE_HDR1; //go back to first statewrite #ifdef DEBUG fprintf (stderr, "%s: writing in SERIAL(2) %d bytes: ", __FUNCTION__, msgLen); dump (ascpMsg, msgLen); #endif write ( usb_fd, ascpMsg, msgLen ); msgLen = 0; msgIndex = 0; } else { //there are data bytes to fetch if (0 == msgLen) msgLen = 8192+2; //handle special case of 8192 byte data message msgState = MSGSTATE_DATA; //go to data byte reading state } break; case MSGSTATE_DATA: //try to read the rest of the message ascpMsg[msgIndex++] = ch; if( msgIndex >= msgLen ) { msgState = MSGSTATE_HDR1; //go back to first stage // // analyze if the message has to be sent via UDP // if (ascpMsg[1] == 0x80 && ascpMsg[0] == 0x00) { //udp_write ( ascpMsg, msgLen ); } else { #ifdef DEBUG fprintf (stderr, "%s: writing in SERIAL %d bytes: ", __FUNCTION__, msgLen); dump (ascpMsg, msgLen); #endif write (usb_fd, ascpMsg, msgLen ); //usleep (10000); } msgIndex = 0; } break; } //end switch statement } int Cute::threadproc () { string buf; if (usb_fd < 0) { usb_fd = open_sdriq_dev(name_serial, buf); } msgState = MSGSTATE_HDR1; msgLen = 0; msgIndex = 0; seqNumber = 0; fprintf (stderr, "TTT Entering in execute, reading from TCP socket\n"); while (1) { int nr; unsigned char buf[1024]; nr = read (sock, buf, sizeof(buf)); if (nr > 0) { #ifdef DEBUG fprintf (stderr, "%s: %d bytes read from socket: ", __FUNCTION__, nr); dump (buf, nr); #endif for (int j=0; jstop(); delete pRadio; break; } } } const char *name = "/dev/ttyUSB0"; int main () { fprintf (stderr, "------ \n"); fprintf (stderr, "------ SDR-IQ server for Cute SDR\n"); fprintf (stderr, "------ \n"); fprintf (stderr, "------ (C) 2012, Ken N9VV && Andrea IW0HDV\n"); fprintf (stderr, "\n"); string buf; int usb_fd = open_sdriq_dev(name, buf); if (usb_fd <0) { fprintf (stderr, "Fatal: error opening: %s:\n[%s]\n", name, buf.c_str()); exit (255); } else { fprintf (stderr, "%s opened. \n", name); close (usb_fd); } Cute c(name); // Instantiate a Cute object Listener l(&c); // Instantiate the Listener l.run(); // wait for exit from user puts( "Press q to exit." ); char ch; while((ch = getc(stdin)) != EOF) { if (ch == 'q') break; } return 0; } // obsolete code #if 0 #endif cutesdr-1.0.5/siqs/siqs_ftdi.cpp0000644000175000017500000004462611716037373016622 0ustar bottomsbottoms// // sdriq-ftdi: An attempt to create a Unix server for RF-SPACE SDR-IQ // // Copyright (C) 2012 Andrea Montefusco IW0HDV // // 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, either 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 for more details. // // You should have received a copy of the GNU General Public License // along with this program. If not, see . // #include #include #include #include #include #include #include #include #include #include #include using namespace std; #include "minithread.h" #include "netsupport.h" #include "discover.h" #include "logger.h" struct ftdi_context * open_ftdi () { struct ftdi_context *ftdi; char *descstring = NULL; if ((ftdi = ftdi_new()) == 0) { fprintf(stderr, "ftdi_new failed\n"); return 0; } if (ftdi_set_interface(ftdi, INTERFACE_A) < 0) { fprintf(stderr, "ftdi_set_interface failed\n"); ftdi_free(ftdi); return 0; } if (ftdi_usb_open_desc(ftdi, 0x0403, 0x6001, descstring, NULL) < 0) { fprintf(stderr,"Can't open ftdi device: %s\n",ftdi_get_error_string(ftdi)); ftdi_free(ftdi); return 0; } /* A timeout value of 1 results in may skipped blocks */ if(ftdi_set_latency_timer(ftdi, 2)) { fprintf(stderr,"Can't set latency, Error %s\n",ftdi_get_error_string(ftdi)); ftdi_usb_close(ftdi); ftdi_free(ftdi); return 0; } if(ftdi_usb_purge_rx_buffer(ftdi) < 0) { fprintf(stderr,"Can't rx purge, error: %s\n",ftdi_get_error_string(ftdi)); return 0; } return ftdi; } void dump (unsigned char*buf, int len) { for (int i =0; i < len; ++i) { fprintf (stderr, "%02X " , buf[i]); } fprintf (stderr, "\n"); fflush(stderr); } static const int LENGTH_MASK = 0x1FFF; enum MsgState { MSGSTATE_HDR1 = 0, MSGSTATE_HDR2 = 1, MSGSTATE_DATA = 2 }; /** * Radio class * * Run on his own thread, read the serial interface (emulated on USB) * Decodes the ASCP messages and send it to CuteSDR via IP * * we need to decode each message because the data messages containing the * I/Q samples are to be forwarded back to CuteSDR via UDP packets * whilst the others are pushd into the TCP socket */ class Radio: public MiniThread { public: Radio (int s, struct ftdi_context *f); ~Radio(); int process (unsigned char ch); int threadproc (); private: void udp_write (unsigned char *msg, int len); //void SendUDPPacketToSocket( unsigned char* pData); int udpSocket; short seqNumber; sockaddr clientTCPAddr; sockaddr_in clientUDPAddr; int sock; int usb_fd; struct ftdi_context *ftdi; unsigned char ascpMsg[10240]; int msgLen; int msgIndex; pthread_t thread_id; enum MsgState msgState; // timing variables timespec time_start, time_end, time_diff; public: }; Radio::Radio (int s, struct ftdi_context *f): sock(s), ftdi(f) { msgState = MSGSTATE_HDR1; msgLen = 0; msgIndex = 0; udpSocket = -1; seqNumber = 0; fprintf (stderr, "Radio::Radio: %d %p\n", sock, ftdi); socklen_t addrlen = sizeof(clientTCPAddr); if (getpeername(sock, &clientTCPAddr, &addrlen)) { perror("getpeername failed"); throw; } else { clientUDPAddr.sin_family = AF_INET; clientUDPAddr.sin_addr = ((sockaddr_in *)&clientTCPAddr)->sin_addr; clientUDPAddr.sin_port = htons(50000); // // create the UDP Socket for I/Q sending // udpSocket = socket(PF_INET,SOCK_DGRAM,IPPROTO_UDP); if(udpSocket < 0) { perror("create socket failed for I/Q socket\n"); throw; } else { int buffsize = 1000000; setsockopt(udpSocket, SOL_SOCKET, SO_RCVBUF, (const char*)&buffsize, sizeof(buffsize)); } } } Radio :: ~Radio () { close (usb_fd); close (udpSocket); close (sock); } timespec diff(timespec start, timespec end) { timespec temp; if ((end.tv_nsec - start.tv_nsec) < 0) { temp.tv_sec = end.tv_sec - start.tv_sec - 1; temp.tv_nsec = 1000000000 + end.tv_nsec - start.tv_nsec; } else { temp.tv_sec = end.tv_sec - start.tv_sec; temp.tv_nsec = end.tv_nsec - start.tv_nsec; } return temp; } /** * * Called send a 1024 byte ASCP formated message to the Client UDP Socket * Add a two byte sequence number to the header * */ void Radio::udp_write( unsigned char *msg, int length) { //break 8192 byte packet into eight 1024 ASCP packets for(int i=2; i<8194; i+=1024) { unsigned char dataMsg [10240]; if(udpSocket == -1) return; dataMsg[0] = 0x04; //data item0 length 0x404 = 1024+4 dataMsg[1] = 0x84; dataMsg[2] = seqNumber & 0xFF; dataMsg[3] = (seqNumber >> 8) & 0xFF; memcpy(&dataMsg[4], (msg+i), 1024); int result = sendto(udpSocket, (char*)dataMsg, 1024+4, 0, (struct sockaddr *)&clientUDPAddr,sizeof(clientUDPAddr) ); if (result < 0) { perror( "!!!!!!!!!!! unable to write into UDP socket !"); } else if(result != (1024+4) ) { fprintf (stderr, "!!!!!!!!!!! partial write into UDP socket !"); // Something bad happened on the socket. Deal with it. } seqNumber++; if ((seqNumber % 1024) == 0) { clock_gettime(CLOCK_REALTIME, &time_end); time_diff = ::diff(time_start, time_end); long double diff_s = time_diff.tv_sec + (time_diff.tv_nsec/1E9) ; long ts = 1024 * (1024 / 4); fprintf (stderr, "Samples received: %lu, %.1Lf kS/s\n", ts, ((double)ts / (diff_s)/1E3) ); clock_gettime (CLOCK_REALTIME, &time_start); } if( 0 == seqNumber) //if seq number rollover seqNumber = 1; //dont use zero as seq number } } int Radio :: process (unsigned char ch) { int rc = 0; // // reading from radio serial and decoding // the messages // we need to decode because the data messages, containing the // I/Q samples, are to be forwarded back to CuteSDR via UDP packets // switch(msgState) //state machine to get generic ASCP msg from TCP data stream { case MSGSTATE_HDR1: //get first header byte of ASCP msg ascpMsg[0] = ch; msgState = MSGSTATE_HDR2; //go to get second header byte state msgLen = 0; msgIndex = 0; break; case MSGSTATE_HDR2: //here for second byte of header ascpMsg[1] = ch; msgLen = ((ascpMsg[1] << 8) | ascpMsg[0] ) & LENGTH_MASK; // 0x03ff msgIndex = 2; if(2 == msgLen) {//if msg has no parameters then we are done msgState = MSGSTATE_HDR1; //go back to first statewrite ( sock, ascpMsg, msgLen ); XDEBUG (DBG_DEBUG, fprintf (stderr, "%s: writing in TCP socket %d bytes: ", __FUNCTION__, msgLen); dump (ascpMsg, msgLen); ); rc = write ( sock, ascpMsg, msgLen ); msgLen = 0; msgIndex = 0; } else { //there are data bytes to fetch if (0 == msgLen) msgLen = 8192+2; //handle special case of 8192 byte data message msgState = MSGSTATE_DATA; //go to data byte reading state } break; case MSGSTATE_DATA: //try to read the rest of the message ascpMsg[msgIndex++] = ch; if( msgIndex >= msgLen ) { msgState = MSGSTATE_HDR1; //go back to first stage // // analyze if the message has to be sent via UDP // if (ascpMsg[1] == 0x80 && ascpMsg[0] == 0x00) { udp_write ( ascpMsg, msgLen ); } else { XDEBUG (DBG_DEBUG, fprintf (stderr, "%s: writing in TCP socket %d bytes: ", __FUNCTION__, msgLen); dump (ascpMsg, msgLen); ); rc = write (sock, ascpMsg, msgLen ); } msgIndex = 0; } break; } //end switch statement return rc; } int Radio :: threadproc () { unsigned char buf [64]; fprintf (stderr, "TTT %s, entering reading loop....\n", __FUNCTION__); clock_gettime (CLOCK_REALTIME, &time_start); while (1) { // read from serial //int len = read (usb_fd, buf, sizeof(buf)); if (ftdi == 0) break; int len = ftdi_read_data (ftdi, buf, sizeof(buf) ); if (len > 0) { XDEBUG (DBG_DEBUG, fprintf (stderr, "%s: read from serial: %d: ", __FUNCTION__, len); dump (buf, len); ); for (int i=0; irun(); } int threadproc (); int process (unsigned char ch); }; /** * reading from radio serial and decoding the messages * * */ int Cute :: process (unsigned char ch) { int rc = 0; switch(msgState) //state machine to get generic ASCP msg from TCP data stream { case MSGSTATE_HDR1: //get first header byte of ASCP msg ascpMsg[0] = ch; msgState = MSGSTATE_HDR2; //go to get second header byte state msgLen = 0; msgIndex = 0; break; case MSGSTATE_HDR2: //here for second byte of header ascpMsg[1] = ch; msgLen = ((ascpMsg[1] << 8) | ascpMsg[0] ) & LENGTH_MASK; msgIndex = 2; if(2 == msgLen) {//if msg has no parameters then we are done msgState = MSGSTATE_HDR1; //go back to first statewrite XDEBUG (DBG_DEBUG, fprintf (stderr, "%s: writing in SERIAL(2) %d bytes: ", __FUNCTION__, msgLen); dump (ascpMsg, msgLen); ); rc = ftdi_write_data (ftdi, ascpMsg, msgLen ); msgLen = 0; msgIndex = 0; } else { //there are data bytes to fetch if (0 == msgLen) msgLen = 8192+2; //handle special case of 8192 byte data message msgState = MSGSTATE_DATA; //go to data byte reading state } break; case MSGSTATE_DATA: //try to read the rest of the message ascpMsg[msgIndex++] = ch; if( msgIndex >= msgLen ) { msgState = MSGSTATE_HDR1; //go back to first stage // // analyze if the message has to be sent via UDP // if (ascpMsg[1] == 0x80 && ascpMsg[0] == 0x00) { //udp_write ( ascpMsg, msgLen ); } else { XDEBUG (DBG_DEBUG, fprintf (stderr, "%s: writing in SERIAL %d bytes: ", __FUNCTION__, msgLen); dump (ascpMsg, msgLen); ); //write (usb_fd, ascpMsg, msgLen ); rc = ftdi_write_data (ftdi, ascpMsg, msgLen ); //usleep (10000); } msgIndex = 0; } break; } //end switch statement return rc; } int Cute::threadproc () { string buf; msgState = MSGSTATE_HDR1; msgLen = 0; msgIndex = 0; seqNumber = 0; DEBUG(DBG_WARN, "TTT Entering in execute, reading from TCP socket"); while (1) { int nr; unsigned char buf[1024]; nr = read (sock, buf, sizeof(buf)); if (nr > 0) { XDEBUG (DBG_DEBUG, fprintf (stderr, "%s: %d bytes read from socket: ", __FUNCTION__, nr); dump (buf, nr); ); for (int j=0; jstop(); delete pRadio; break; } } return 0; } char *name = "/dev/ttyUSB0"; int scan_ftdi () { int ret, i; struct ftdi_context *ftdi; struct ftdi_device_list *devlist, *curdev; char manufacturer[128], description[128]; int retval = EXIT_SUCCESS; if ((ftdi = ftdi_new()) == 0) throw "ftdi_new failed"; if ((ret = ftdi_usb_find_all(ftdi, &devlist, 0, 0)) < 0) { fprintf(stderr, "ftdi_usb_find_all failed: %d (%s)\n", ret, ftdi_get_error_string(ftdi)); retval = EXIT_FAILURE; goto do_deinit; } DEBUG(DBG_DEBUG, "Number of FTDI devices found: " << ret); i = 0; for (curdev = devlist; curdev != NULL; i++) { DEBUG(DBG_ERROR, "Checking device: " << i); if ((ret = ftdi_usb_get_strings(ftdi, curdev->dev, manufacturer, 128, description, 128, NULL, 0)) < 0) { DEBUG (DBG_ERROR, "ftdi_usb_get_strings failed: " << ret << " (" << ftdi_get_error_string(ftdi) << ")" ); retval = EXIT_FAILURE; goto done; } DEBUG(DBG_WARN, "Manufacturer: " << manufacturer <<", Description: " << description ); curdev = curdev->next; } done: ftdi_list_free(&devlist); do_deinit: ftdi_free(ftdi); return retval; } int debug_level = 0; void print_banner () { fprintf (stderr, "------ \n"); fprintf (stderr, "------ SDR-IQ server for Cute SDR\n"); fprintf (stderr, "------ \n"); fprintf (stderr, "------ libftdi version\n"); fprintf (stderr, "------ \n"); fprintf (stderr, "------ (C) 2012, Ken N9VV && Andrea IW0HDV\n"); fprintf (stderr, "\n"); } void print_help() { print_banner(); cerr << " Allowed options: " << std::endl << " -i [ --interface ] arg (=/dev/ttyUSB0) interface name " << std::endl << " -h [ --help ] print usage message " << std::endl << " -d [ --debug ] arg (=0) 0|1|2 debug level " << std::endl << std::endl; } int parseOptions (int argc, char **argv) { int c; int rc = 0; while (1) { static struct option long_options[] = { /* These options set a flag. */ {"help", no_argument, 0, 1 }, {"interface", optional_argument, 0, 'i'}, {"debug", required_argument, 0, 'd'}, {0, 0, 0, 0} }; /* getopt_long stores the option index here. */ int option_index = 0; c = getopt_long (argc, argv, "hi:d:", long_options, &option_index); /* Detect the end of the options. */ if (c == -1) break; switch (c) { case 'd': debug_level = atoi(optarg); break; case 'i': name = optarg; break; case 1: case '?': case 'h': default: /* getopt_long already printed an error message. */ print_help(); return -1; break; } } return rc; } int main (int argc, char **argv) { if (parseOptions (argc, argv)) return 255; switch (debug_level) { case 0: DEBUG_CONF("outputfile", Logger::file_off|Logger::screen_on, DBG_ERROR, DBG_ERROR); break; case 1: DEBUG_CONF("outputfile", Logger::file_off|Logger::screen_on, DBG_WARN, DBG_WARN); break; case 2: DEBUG_CONF("outputfile", Logger::file_off|Logger::screen_on, DBG_DEBUG, DBG_DEBUG); break; } //struct ftdi_context *ftdi; //char *descstring = NULL; print_banner(); string buf; XDEBUG (DBG_DEBUG, scan_ftdi()); try { DiscoverSdrxx sd; sd.run(); Cute c(name); // Instantiate a Cute object Listener l(&c); // Instantiate the Listener l.run(); // wait for exit from user puts( "Press q to exit." ); char ch; while((ch = getc(stdin)) != EOF) { if (ch == 'q') break; } return 0; } catch (const char *msg) { std::cerr << msg << std::endl; } catch (SysException &le) { std::cerr << le.desc_ << ": " << le.msg_ << std::endl; } catch (...) { std::cerr << "Unexpected exception !" << std::endl; } ; } cutesdr-1.0.5/siqs/minithread.h0000644000175000017500000000403711711273721016410 0ustar bottomsbottoms// // sdriq-ftdi: An attempt to create a Unix server for RF-SPACE SDR-IQ // // Copyright (C) 2012 Andrea Montefusco IW0HDV // // 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, either 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 for more details. // // You should have received a copy of the GNU General Public License // along with this program. If not, see . // #if !defined __MINITHREAD_H__ #define __MINITHREAD_H__ #include /** * Mini Thread * * Minimalistic implementation of a pthread wrapper */ class MiniThread { public: MiniThread (): thread_run(-1) {} ~MiniThread () {} virtual int threadproc () = 0; // pure virtual method to be implemented // in derived classes private: pthread_t thread_id; int thread_run; int stop_f; static void *thread (void *arg) { MiniThread *p = (MiniThread *) arg; p->threadproc (); return p; } public: int have_to_stop () { return !stop_f ; } int run() { stop_f = 0; int rc = pthread_create (&thread_id,NULL, thread,(void *)this); if(rc<0) { throw "pthread_create MiniThread failed"; } else thread_run = 0; return 0; } int wait () { void *ret; if (thread_run != -1) { int rc = pthread_join (thread_id, &ret); thread_run = -1; return rc; } return -1; } int stop () { if (thread_run != -1) { stop_f = -1; return wait(); } return -1; } }; #endif cutesdr-1.0.5/siqs/discover.cpp0000644000175000017500000001636411714313226016441 0ustar bottomsbottoms// // sdriq-ftdi: An attempt to create a Unix server for RF-SPACE SDR-IQ // // Copyright (C) 2012 Andrea Montefusco IW0HDV // // 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, either 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 for more details. // // You should have received a copy of the GNU General Public License // along with this program. If not, see . // #include "logger.h" #include "discover.h" DiscoverSdrxx :: DiscoverSdrxx (unsigned short p):myPort(p) { myIP = 0; myBootVer = 0; myFirmVer = 0; struct sockaddr_in sa; if( (sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP) ) == -1) { throw SysException (errno, strerror(errno), "Error creating UDP socket in Discover"); } int optval = 1; setsockopt( sock, SOL_SOCKET, SO_REUSEADDR, (const char*)&optval, sizeof(optval) ); setsockopt( sock, SOL_SOCKET, SO_LINGER, (const char*)&optval, sizeof(optval) ); setsockopt( sock, SOL_SOCKET, SO_BROADCAST, (const char*) &optval, sizeof(optval) ); memset( &sa, 0, sizeof(sa)); sa.sin_family = AF_INET; sa.sin_addr.s_addr = htonl(INADDR_ANY ); sa.sin_port = htons(discover_port); if (bind(sock, (sockaddr *)&sa, sizeof(sa)) == -1) { throw SysException (errno, strerror(errno), "Socket failed to bind"); } // get the hostname gethostname(hostname, sizeof(hostname)); optval = 1; setsockopt( sock, SOL_SOCKET, SO_LINGER, (const char*)&optval, sizeof(optval) ); setsockopt( sock, SOL_SOCKET, SO_BROADCAST,(const char*) &optval, sizeof(optval) ); } in_addr_t DiscoverSdrxx :: get_addresses(const int domain, const char *if_name) { int s; struct ifconf ifc; struct ifreq ifr[50]; int ifs; int i; s = socket(domain, SOCK_STREAM, 0); if (s < 0) throw SysException (errno, strerror(errno), "Socket failed to create"); ifc.ifc_buf = (char *) ifr; ifc.ifc_len = sizeof ifr; if (ioctl(s, SIOCGIFCONF, &ifc) == -1) throw SysException (errno, strerror(errno), "Socket failed to IOCTL"); ifs = ifc.ifc_len / sizeof(ifr[0]); DEBUG(DBG_DEBUG, "interfaces = " << ifs); for (i = 0; i < ifs; i++) { char ip[INET_ADDRSTRLEN]; struct sockaddr_in *s_in = (struct sockaddr_in *) &ifr[i].ifr_addr; if (!inet_ntop(domain, &s_in->sin_addr, ip, sizeof(ip))) { throw SysException (errno, strerror(errno), "inet_ntop: converting IP address"); } else { DEBUG(DBG_DEBUG, ifr[i].ifr_name << " - " << ip); if (strcmp(if_name, ifr[i].ifr_name)==0) { return s_in->sin_addr.s_addr; } } } close(s); return 0; } int DiscoverSdrxx :: threadproc () { unsigned char buf [10240]; myIP = get_addresses ( AF_INET , "eth0" ); XDEBUG(DBG_WARN, fprintf (stderr, "SRXXX STARTED: size %d\n", sizeof(COMMON_DISCOVER_MSG_SDRXX) ); ); while (1) { int rc = read (sock, buf, sizeof(buf)); if ( rc < 0 ) break; unsigned int len = rc; if (len && len >= sizeof(COMMON_DISCOVER_MSG_SDRXX) && len <= (sizeof(COMMON_DISCOVER_MSG_SDRXX) + sizeof(EXTENDED_DISCOVER_MSG_SDRXX)) ) { COMMON_DISCOVER_MSG_SDRXX *p = (COMMON_DISCOVER_MSG_SDRXX *)buf; if ( p->key[0] == KEY0 && p->key[1] == KEY1 ) { int len = p->length[0] | p->length[1] << 8; XDEBUG(DBG_WARN, fprintf (stderr, "GOT SRXX message: %d\n", len); if (strlen(p->name)) { fprintf (stderr, "Destination name: [%s]\n", p->name); } if (strlen(p->sn)) { fprintf (stderr, "Destination /N: [%s]\n", p->sn); } ); // // setup the answer // EXTENDED_DISCOVER_MSG_SDRXX emsg; COMMON_DISCOVER_MSG_SDRXX *pa; memset (&emsg, 0, sizeof(emsg)); memcpy (&emsg, p, sizeof(*p)); pa = (COMMON_DISCOVER_MSG_SDRXX *)&emsg ; strcpy( pa->name, hostname); //fill in name string field strcpy( pa->sn, "NOT IMPLEMENTED"); //fill in Serial Number string field pa->length[0] = sizeof(emsg) & 0xFF; pa->length[1] = sizeof(emsg) >> 8; pa->key[0] = KEY0; pa->key[1] = KEY1; pa->op = MSG_RESP; //fill in IP adr and port pa->ipaddr[3] = myIP & 0x000000FF; pa->ipaddr[2] = (myIP>>8) & 0x000000FF; pa->ipaddr[1] = (myIP>>16) & 0x000000FF; pa->ipaddr[0] = (myIP>>24) & 0x000000FF; pa->port[0] = myPort&0x00FF; pa->port[1] = (myPort>>8)&0x00FF; emsg.btver[0] = myBootVer&0x00FF; emsg.btver[1] = (myBootVer>>8)&0x00FF; emsg.fwver[0] = myFirmVer&0x00FF; emsg.fwver[1] = (myFirmVer>>8)&0x00FF; emsg.connection[0] = 'U'; emsg.connection[1] = 'S'; emsg.connection[2] = 'B'; if(0) emsg.status |= STATUS_BIT_CONNECTED; else emsg.status &= ~STATUS_BIT_CONNECTED; if( sock != -1 ) { int length = pa->length[1]; length <<= 8; length += (int)pa->length[0]; struct sockaddr_in satx; memset( &satx, 0, sizeof(satx)-1 ); satx.sin_family = AF_INET; satx.sin_addr.s_addr = inet_addr("255.255.255.255"); satx.sin_port = htons(discover_client_port); int ns = sendto (sock, (const char*)&emsg, length, 0, (sockaddr *)&satx, sizeof(satx) ); if( ns != length ) { DEBUG(DBG_ERROR, "Send Failed"); } else { DEBUG(DBG_ERROR, "Send Msg"); } } } else { DEBUG(DBG_ERROR, "Invalid key in discover message: " << p->key[0] << p->key[1]); } } } return 0; } cutesdr-1.0.5/siqs/README0000644000175000017500000000424711712354175015002 0ustar bottomsbottomsSdr IQ Server ------------------------------------------- Copyright (C) 2012 Andrea Montefusco IW0HDV 0. Introduction =============== This program attempt to mimic the sdrxx server created by Moe WHEATLEY in order to provide SDR-IQ with a network interface compatible with that of more sophisticated radio from RFSPACE. Alas the sdrxx server source code resulted unfeasible to port to Linux, but was invaluable as reference for write A special mention deservers Ken N9VV that put his radio, Linux computer and Internet connection to my disposal in order to develop and test this software. 1. Build ======== In order to compile this program and CuteSDR, you need to install the libftdi support and cmake: sudo apt-get install libftdi-dev cmake subversion # Debian /Ubuntu sudo rpm install libftdi-devel cmake subversion # RH/Fedora/OpenSuse After that you can extract the svn co https://cutesdr.svn.sourceforge.net/svnroot/cutesdr/branches/iw0hdv/linux/trunk/ cutesdr-lx Next compile with the following command: cd cutesdr-lx mkdir build cd build cmake ../ make -j4 sudo make install 2. Run ====== In order to run CuteSDR you have to remove from your system the standard Linux kernel FTDI driver; this is feasible at run time: sudo rmmod usbserial ftdi_sio Next check that the radio is correctly linked to the system: lsusb .... Bus 006 Device 002: ID 0403:6001 Future Technology Devices International, Ltd FT232 USB-Serial (UART) IC .... If everything is fine, open a terminal window and start the server: siqs ------ ------ SDR-IQ server for Cute SDR ------ ------ libftdi version ------ ------ (C) 2012, Ken N9VV && Andrea IW0HDV interfaces = 2: lo - 127.0.0.1 eth0 - 192.168.10.1 SRXXX STARTED: size 56 Press q to exit. Listening for TCP connections on port 50000 socket: 6 Last, run CuteSDR from another terminal window, and into the menu Setup->Network press "Find SDR" push button or manually insert the network data. The auto discovery proceudre works only if the CuteSDR and the server are placed on the same LAN (broadcast domain). Please not that the server is not yet completed with all the features: e.g. the discovery messages is only partially supported. cutesdr-1.0.5/siqs/cutesdrsrv.sh0000755000175000017500000000074011711273721016653 0ustar bottomsbottoms# # Example script for test the 'Bent pipe' concept. # Alas it is not fully working because the I/Q data messages coming out from the radio # have to be redirected into a separate UDP pipe. # # # interface="/dev/ttyUSB0" echo "Setting the $interface to 230400 b/s......" stty -F $interface 230400 cs8 -cstopb -parity -icanon min 1 time 1 echo "Listening on tcp/50000, please start CuteSDR......" socat tcp-l:50000,reuseaddr,fork file:$interface,nonblock,waitlock=./tty0.lock cutesdr-1.0.5/siqs/logger.h0000644000175000017500000001001111716037373015536 0ustar bottomsbottoms/* (C) 2012 Claudio Scordino No license on original code, see http://retis.sssup.it/~scordino/code/logger.html */ #ifndef LOGGER_H #define LOGGER_H #include #include #include #include #include /// Comment this line if you don't need multithread support #define LOGGER_MULTITHREAD const int DBG_ERROR = 0; const int DBG_WARN = 1; const int DBG_DEBUG = 2; #ifdef LOGGER_MULTITHREAD #include #endif /** * \brief Macro to configure the logger. * Example of configuration of the Logger: * DEBUG_CONF("outputfile", Logger::file_on|Logger::screen_on, DBG_DEBUG, DBG_ERROR); */ #define DEBUG_CONF(outputFile, \ configuration, \ fileVerbosityLevel, \ screenVerbosityLevel) { \ Logger::getInstance().configure(outputFile, \ configuration, \ fileVerbosityLevel, \ screenVerbosityLevel); \ } /** * \brief Macro to print log messages. * Example of usage of the Logger: * DEBUG(DBG_DEBUG, "hello " << "world"); */ #define DEBUG(priority, msg) { \ std::ostringstream __debug_stream__; \ __debug_stream__ << msg; \ Logger::getInstance().print(priority, __FILE__, __LINE__, \ __debug_stream__.str()); \ } #define XDEBUG(p, code) { \ if (Logger::getInstance().enabled(p)) { \ code ;\ } \ } /** * \brief Simple logger to log messages on file and console. * This is the implementation of a simple logger in C++. It is implemented * as a Singleton, so it can be easily called through two DEBUG macros. * It is Pthread-safe. * It allows to log on both file and screen, and to specify a verbosity * threshold for both of them. */ class Logger { /** * \brief Type used for the configuration */ enum loggerConf_ {L_nofile_ = 1 << 0, L_file_ = 1 << 1, L_noscreen_ = 1 << 2, L_screen_ = 1 << 3}; #ifdef LOGGER_MULTITHREAD /** * \brief Lock for mutual exclusion between different threads */ static pthread_mutex_t lock_; #endif bool configured_; /** * \brief Pointer to the unique Logger (i.e., Singleton) */ static Logger* m_; /** * \brief Initial part of the name of the file used for Logging. * Date and time are automatically appended. */ std::string logFile_; /** * \brief Current configuration of the logger. * Variable to know if logging on file and on screen are enabled. * Note that if the log on file is enabled, it means that the * logger has been already configured, therefore the stream is * already open. */ loggerConf_ configuration_; /** * \brief Stream used when logging on a file */ std::ofstream out_; /** * \brief Initial time (used to print relative times) */ struct timeval initialTime_; /** * \brief Verbosity threshold for files */ unsigned int fileVerbosityLevel_; /** * \brief Verbosity threshold for screen */ unsigned int screenVerbosityLevel_; Logger(); ~Logger(); /** * \brief Method to lock in case of multithreading */ inline static void lock(); /** * \brief Method to unlock in case of multithreading */ inline static void unlock(); public: typedef loggerConf_ loggerConf; static const loggerConf file_on= L_nofile_; static const loggerConf file_off= L_file_; static const loggerConf screen_on= L_noscreen_; static const loggerConf screen_off= L_screen_; static Logger& getInstance(); void print(const unsigned int verbosityLevel, const std::string& sourceFile, const int codeLine, const std::string& message); void configure (const std::string& outputFile, const loggerConf configuration, const int fileVerbosityLevel, const int screenVerbosityLevel); bool enabled (unsigned int lev) { return lev <= screenVerbosityLevel_; } }; inline Logger::loggerConf operator| (Logger::loggerConf __a, Logger::loggerConf __b) { return Logger::loggerConf(static_cast(__a) | static_cast(__b)); } inline Logger::loggerConf operator& (Logger::loggerConf __a, Logger::loggerConf __b) { return Logger::loggerConf(static_cast(__a) & static_cast(__b)); } #endif /* LOGGER_H */ cutesdr-1.0.5/siqs/CMakeLists.txt0000644000175000017500000000126311714313226016647 0ustar bottomsbottomsproject(siqs) set(CMAKE_VERBOSE_MAKEFILE OFF) set (siqs_ftdi_SRCS siqs_ftdi.cpp discover.cpp logger.cpp ) set (siqs_ftdi_INC netsupport.h minithread.h discover.h logger.h ) find_library (rt_LIB rt) find_library (pthread_LIB pthread) find_library (usb-1.0_LIB usb-1.0) find_library (ftdi_LIB ftdi) ADD_DEFINITIONS ( -g -Wall -O3 -Wno-unused-function ) add_executable(siqs_ftdi ${siqs_ftdi_SRCS} ${siqs_ftdi_INC}) target_link_libraries(siqs_ftdi ${rt_LIB} ${pthread_LIB} ${usb-1.0_LIB} ${ftdi_LIB} ) INSTALL_TARGETS(/bin siqs_ftdi) cutesdr-1.0.5/siqs/logger.cpp0000644000175000017500000000750511714313226016077 0ustar bottomsbottoms/* (C) 2012 Claudio Scordino No license on original code, see http://retis.sssup.it/~scordino/code/logger.html */ #include #include #include #include "logger.h" // Definition (and initialization) of static attributes Logger* Logger::m_ = 0; #ifdef LOGGER_MULTITHREAD pthread_mutex_t Logger::lock_ = PTHREAD_MUTEX_INITIALIZER; inline void Logger::lock() { pthread_mutex_lock(&lock_); } inline void Logger::unlock() { pthread_mutex_unlock(&lock_); } #else void Logger::lock(){} void Logger::unlock(){} #endif /** * \brief Constructor. * It is a private constructor, called only by getInstance() and only the * first time. It is called inside a lock, so lock inside this method * is not required. * It only initializes the initial time. All configuration is done inside the * configure() method. */ Logger::Logger(): configured_(false) { gettimeofday(&initialTime_, NULL); } /** * \brief Method to configure the logger. Called by the DEBUG_CONF() macro. * To make implementation easier, the old stream is always closed. * Then, in case, it is open again in append mode. * @param Name of the file used for logging * @param Configuration (i.e., log on file and on screen on or off) * @param Verbosity threshold for file * @param Verbosity threshold for screen */ void Logger::configure (const std::string& outputFile, const loggerConf configuration, const int fileVerbosityLevel, const int screenVerbosityLevel) { Logger::lock(); fileVerbosityLevel_ = fileVerbosityLevel; screenVerbosityLevel_ = screenVerbosityLevel; // Close the old stream, if needed if (configuration_&file_on) out_.close(); // Compute a new file name, if needed if (outputFile != logFile_){ std::ostringstream oss; time_t currTime; time(&currTime); struct tm *currTm = localtime(&currTime); oss << outputFile << "_" << currTm->tm_mday << "_" << currTm->tm_mon << "_" << (1900 + currTm->tm_year) << "_" << currTm->tm_hour << "-" << currTm->tm_min << "-" << currTm->tm_sec << ".log"; logFile_ = oss.str().c_str(); } // Open a new stream, if needed if (configuration&file_on) out_.open(logFile_.c_str(), std::ios::app); configuration_ = configuration; configured_ = true; Logger::unlock(); } /** * \brief Destructor. * It only closes the file, if open, and cleans memory. */ Logger::~Logger() { Logger::lock(); if (configuration_&file_on) out_.close(); delete m_; Logger::unlock(); } /** * \brief Method to get a reference to the object (i.e., Singleton) * It is a static method. * @return Reference to the object. */ Logger& Logger::getInstance() { Logger::lock(); if (m_ == 0) m_ = new Logger; Logger::unlock(); return *m_; } /** * \brief Method used to print messages. * Called by the DEBUG() macro. * @param Priority of the message * @param Source file where the method has been called (set equal to __FILE__ * by the DEBUG macro) * @param Source line where the method has been called (set equal to __LINE__ by the macro) * @param Message */ void Logger::print(const unsigned int verbosityLevel, const std::string& file, const int line, const std::string& message) { if (!configured_) { std::cerr << "ERROR: Logger not configured!" << std::endl; return; } struct timeval currentTime; gettimeofday(¤tTime, NULL); Logger::lock(); if ((configuration_&file_on) && (verbosityLevel <= fileVerbosityLevel_)) out_ << "DEBUG [" << file << ":" << line << "] @ " << (currentTime.tv_sec - initialTime_.tv_sec) << ":" << message << std::endl; if ((configuration_&screen_on) && (verbosityLevel <= screenVerbosityLevel_)) std::cerr << "DEBUG [" << file << ":" << line << "] @ " << (currentTime.tv_sec - initialTime_.tv_sec) << ":" << message << std::endl; Logger::unlock(); } cutesdr-1.0.5/README.linux0000644000175000017500000000066411553144014015150 0ustar bottomsbottoms1. Build instructions on Ubuntu 10.10 - Andrea Montefusco IW0HDV - andrew att montefusco dott com # install the Qt packages sudo apt-get install qt4-dev-tools qtmobility-dev # checkout the repository in your home directory cd svn co https://cutesdr.svn.sourceforge.net/svnroot/cutesdr/branches/iw0hdv/linux/trunk/ cutesdr-lx cd cutesdr-lx/ # generate the Makefile qmake-qt4 CuteSdr.pro # start build make # run it release/CuteSdr cutesdr-1.0.5/CuteSdr.pro0000644000175000017500000001010711711303214015211 0ustar bottomsbottoms#------------------------------------------------- # # Project created by QtCreator 2010-09-15T13:53:54 # patched for U10.10, Andrea IW0HDV, Ken N9VV # #------------------------------------------------- # check Qt version, 4.7is mandatory due to usage of some class introduced in Qt library only since release 4.7 QT_VERSION = $$[QT_VERSION] QT_VERSION = $$split(QT_VERSION, ".") QT_VER_MAJ = $$member(QT_VERSION, 0) QT_VER_MIN = $$member(QT_VERSION, 1) lessThan(QT_VER_MAJ, 4) | lessThan(QT_VER_MIN, 7) { error(CuteSDR requires Qt 4.7 or newer but Qt $$[QT_VERSION] was detected.) } QT += core gui QT += network linux-g++ { # # Ubuntu 10.10: changes due to some strangeness in Debian Qt packages # uncomment out to use changes since doesnt work with Mint # INCLUDEPATH += $$quote(/usr/include/QtMultimediaKit) # LIBS += $$quote(-lQtMultimediaKit) QT += multimedia # compiler optimization options, as per suggestion by Ken N9VV # Uncomment to try. Seems to use more CPU on some machines # QMAKE_CXXFLAGS += -O3 \ # -mfpmath=sse \ # -msse \ # -msse2 \ # -msse3 \ # -fomit-frame-pointer \ # -ffast-math \ # -march=i686 \ # -fexceptions } else { QT += multimedia } TARGET = CuteSdr TEMPLATE = app SOURCES += gui/main.cpp \ gui/sounddlg.cpp \ gui/sdrsetupdlg.cpp \ gui/sdrdiscoverdlg.cpp \ gui/plotter.cpp \ gui/mainwindow.cpp \ gui/ipeditwidget.cpp \ gui/freqctrl.cpp \ gui/displaydlg.cpp \ gui/demodsetupdlg.cpp \ gui/editnetdlg.cpp \ gui/testbench.cpp \ gui/meter.cpp \ gui/sliderctrl.cpp \ gui/noiseprocdlg.cpp \ gui/aboutdlg.cpp \ gui/rdsdecode.cpp \ interface/soundout.cpp \ interface/sdrinterface.cpp \ interface/netiobase.cpp \ interface/ad6620.cpp \ interface/perform.cpp \ dsp/fractresampler.cpp \ dsp/fastfir.cpp \ dsp/downconvert.cpp \ dsp/demodulator.cpp \ dsp/fft.cpp \ dsp/agc.cpp \ dsp/amdemod.cpp \ dsp/samdemod.cpp \ dsp/ssbdemod.cpp \ dsp/smeter.cpp \ dsp/fmdemod.cpp \ dsp/fir.cpp \ dsp/iir.cpp \ dsp/noiseproc.cpp \ dsp/wfmdemod.cpp \ dsp/wfmmod.cpp HEADERS += gui/mainwindow.h \ gui/sounddlg.h \ gui/sdrsetupdlg.h \ gui/sdrdiscoverdlg.h \ gui/plotter.h \ gui/ipeditwidget.h \ gui/freqctrl.h \ gui/sliderctrl.h \ gui/editnetdlg.h \ gui/displaydlg.h \ gui/demodsetupdlg.h \ gui/testbench.h \ gui/meter.h \ gui/noiseprocdlg.h \ gui/aboutdlg.h \ gui/rdsdecode.h \ interface/soundout.h \ interface/sdrinterface.h \ interface/protocoldefs.h \ interface/netiobase.h \ interface/ad6620.h \ interface/ascpmsg.h \ interface/perform.h \ dsp/fractresampler.h \ dsp/fastfir.h \ dsp/filtercoef.h \ dsp/downconvert.h \ dsp/demodulator.h \ dsp/datatypes.h \ dsp/fft.h \ dsp/agc.h \ dsp/amdemod.h \ dsp/samdemod.h \ dsp/ssbdemod.h \ dsp/smeter.h \ dsp/fmdemod.h \ dsp/fir.h \ dsp/iir.h \ dsp/noiseproc.h \ dsp/wfmdemod.h \ dsp/wfmmod.h \ dsp/rbdsconstants.h FORMS += gui/mainwindow.ui \ gui/sdrdiscoverdlg.ui \ gui/sounddlg.ui \ gui/sdrsetupdlg.ui \ gui/ipeditframe.ui \ gui/editnetdlg.ui \ gui/displaydlg.ui \ gui/demodsetupdlg.ui \ gui/testbench.ui \ gui/sliderctrl.ui \ gui/aboutdlg.ui \ gui/noiseprocdlg.ui unix:SOURCES += unix:!macx:SOURCES += macx { SOURCES += LIBS += -framework \ IOKit \ -framework \ CoreFoundation ICON=cutesdr1.icns } win32 { SOURCES += DEFINES += WINVER=0x0501 # needed for mingw to pull in appropriate dbt business...probably a better way to do this LIBS += libwsock32 RC_FILE = cutesdr.rc } linux-g++:DEFINES = _TTY_POSIX_ \ _TTY_LINUX_ win32:DEFINES += _TTY_WIN_ win32:DEFINES += WINVER=0x0501 macx:DEFINES = _TTY_POSIX_ \ _TTY_MACX_ CONFIG(debug, debug|release) { DESTDIR = debug/ OBJECTS_DIR = debug/ RCC_DIR = debug/ } else { DESTDIR = release/ OBJECTS_DIR = release/ RCC_DIR = release/ } OTHER_FILES += \ cutesdr.rc cutesdr-1.0.5/gui/0000755000175000017500000000000011720506076013716 5ustar bottomsbottomscutesdr-1.0.5/gui/ipeditframe.ui0000644000175000017500000000767311561233432016557 0ustar bottomsbottoms FrameIPdit 0 0 170 40 Frame QFrame::StyledPanel QFrame::Raised 130 10 31 20 10 75 true 3 Qt::AlignCenter 40 10 16 16 20 75 true . 120 10 16 16 20 75 true . 50 10 31 20 10 75 true 3 Qt::AlignCenter 90 10 31 20 10 75 true 3 Qt::AlignCenter 10 10 31 20 10 75 true 3 true Qt::AlignCenter 80 10 16 16 20 75 true . lineEdit_B3 lineEdit_B2 lineEdit_B1 lineEdit_B0 cutesdr-1.0.5/gui/meter.cpp0000644000175000017500000002023011546075374015543 0ustar bottomsbottoms////////////////////////////////////////////////////////////////////// // meter.cpp: implementation of the CMeter class. // // This class creates and draws an S meter widget // // History: // 2010-12-28 Initial creation MSW // 2011-03-27 Initial release ////////////////////////////////////////////////////////////////////// //========================================================================================== // + + + This Software is released under the "Simplified BSD License" + + + //Copyright 2010 Moe Wheatley. All rights reserved. // //Redistribution and use in source and binary forms, with or without modification, are //permitted provided that the following conditions are met: // // 1. Redistributions of source code must retain the above copyright notice, this list of // conditions and the following disclaimer. // // 2. Redistributions in binary form must reproduce the above copyright notice, this list // of conditions and the following disclaimer in the documentation and/or other materials // provided with the distribution. // //THIS SOFTWARE IS PROVIDED BY Moe Wheatley ``AS IS'' AND ANY EXPRESS OR IMPLIED //WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND //FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL Moe Wheatley OR //CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR //CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR //SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON //ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING //NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF //ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // //The views and conclusions contained in the software and documentation are those of the //authors and should not be interpreted as representing official policies, either expressed //or implied, of Moe Wheatley. //========================================================================================== #include "gui/meter.h" #include #include ////////////////////////////////////////////////////////////////////// // Local defines ////////////////////////////////////////////////////////////////////// //ratio to total control width or height #define CTRL_MARGIN 0.07 //left/right margin #define CTRL_MAJOR_START 0.4 //top of major tic line #define CTRL_MINOR_START 0.5 //top of minor tic line #define CTRL_XAXIS_HEGHT 0.6 //vert position of horizontal axis #define CTRL_NEEDLE_TOP 0.5 //vert position of top of needle triangle #define MIN_DBM -121.0 //S1 #define MAX_DBM -13.0 //S9+60 ////////////////////////////////////////////////////////////////////// // Construction/Destruction ////////////////////////////////////////////////////////////////////// CMeter::CMeter(QWidget *parent) : QFrame(parent) { m_pSdrInterface = NULL; setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); setFocusPolicy(Qt::StrongFocus); setAttribute(Qt::WA_PaintOnScreen,false); setAutoFillBackground(false); setAttribute(Qt::WA_OpaquePaintEvent, false); setAttribute(Qt::WA_NoSystemBackground, true); setMouseTracking ( true ); m_2DPixmap = QPixmap(0,0); m_OverlayPixmap = QPixmap(0,0); m_Size = QSize(0,0); m_Slevel = 0; m_dBm = -120; } CMeter::~CMeter() { } ////////////////////////////////////////////////////////////////////// // Sizing interface ////////////////////////////////////////////////////////////////////// QSize CMeter::minimumSizeHint() const { return QSize(10, 10); } QSize CMeter::sizeHint() const { return QSize(100, 30); } ////////////////////////////////////////////////////////////////////// // Called when screen size changes so must recalculate bitmaps ////////////////////////////////////////////////////////////////////// void CMeter::resizeEvent(QResizeEvent* ) { if(!size().isValid()) return; if( m_Size != size() ) { //if size changed, resize pixmaps to new screensize m_Size = size(); m_OverlayPixmap = QPixmap(m_Size.width(), m_Size.height()); m_OverlayPixmap.fill(Qt::black); m_2DPixmap = QPixmap(m_Size.width(), m_Size.height()); m_2DPixmap.fill(Qt::black); } DrawOverlay(); draw(); } ////////////////////////////////////////////////////////////////////// // Slot called to update meter level position ////////////////////////////////////////////////////////////////////// void CMeter::SetdBmLevel(double dbm) { qreal w = (qreal)m_2DPixmap.width(); w = w - 2.0*CTRL_MARGIN*w; //width of meter scale in pixels m_dBm = (int)dbm; if(dbmMAX_DBM) dbm = MAX_DBM; if(dbm <= -73.0) { qreal div = w/14.0; m_Slevel = (int)( (dbm/6.0 + 121.0/6.0)*div + .5); } else { qreal div = w/14.0; m_Slevel = (int)( ( 8 + dbm/10.0 + 73.0/10.0 )*div + .5); } draw(); } ////////////////////////////////////////////////////////////////////// // Called by QT when screen needs to be redrawn ////////////////////////////////////////////////////////////////////// void CMeter::paintEvent(QPaintEvent *) { QPainter painter(this); painter.drawPixmap(0,0,m_2DPixmap); return; } ////////////////////////////////////////////////////////////////////// // Called to update spectrum data for displaying on the screen ////////////////////////////////////////////////////////////////////// void CMeter::draw() { int w; int h; if(m_2DPixmap.isNull()) return; //get/draw the 2D spectrum w = m_2DPixmap.width(); h = m_2DPixmap.height(); //first copy into 2Dbitmap the overlay bitmap. m_2DPixmap = m_OverlayPixmap.copy(0,0,w,h); QPainter painter(&m_2DPixmap); //DrawCurrent position indicator qreal hline = (qreal)h*CTRL_XAXIS_HEGHT; qreal marg = (qreal)w*CTRL_MARGIN; qreal ht = (qreal)h*CTRL_NEEDLE_TOP; qreal x = marg + m_Slevel; QPoint pts[3]; pts[0].setX(x); pts[0].setY(ht); pts[1].setX(x-6); pts[1].setY(hline+6); pts[2].setX(x+6); pts[2].setY(hline+6); painter.setBrush(QBrush(Qt::yellow)); painter.setOpacity(1.0); painter.drawPolygon(pts,3); //create Font to use for scales QFont Font("Arial"); QFontMetrics metrics(Font); int y = (h)/3; Font.setPixelSize(y); Font.setWeight(QFont::Normal); painter.setFont(Font); painter.setPen(Qt::black); painter.setOpacity(1.0); m_Str.setNum(m_dBm); painter.drawText(marg, h-1, m_Str+" dBm" ); update(); } ////////////////////////////////////////////////////////////////////// // Called to draw an overlay bitmap containing grid and text that // does not need to be recreated every fft data update. ////////////////////////////////////////////////////////////////////// void CMeter::DrawOverlay() { if(m_OverlayPixmap.isNull()) return; int w = m_OverlayPixmap.width(); int h = m_OverlayPixmap.height(); int x,y; QRect rect; QPainter painter(&m_OverlayPixmap); // m_OverlayPixmap.fill(Qt::darkCyan); #if 1 //fill background with gradient QLinearGradient gradient(0, 0, 0 ,h); gradient.setColorAt(1, Qt::cyan); gradient.setColorAt(0, Qt::blue); painter.setBrush(gradient); painter.drawRect(0, 0, w, h); #endif //Draw scale lines qreal marg = (qreal)w*CTRL_MARGIN; qreal hline = (qreal)h*CTRL_XAXIS_HEGHT; qreal magstart = (qreal)h*CTRL_MAJOR_START; qreal minstart = (qreal)h*CTRL_MINOR_START; qreal hstop = (qreal)w-marg; painter.setPen(QPen(Qt::white, 1,Qt::SolidLine)); painter.drawLine( QLineF( marg, hline, hstop, hline) ); qreal xpos = marg; for(x=0; x<15; x++) { if(x&1) //minor tics painter.drawLine( QLineF(xpos, minstart, xpos, hline) ); else painter.drawLine( QLineF(xpos, magstart, xpos, hline) ); xpos += (hstop-marg)/14.0; } //draw scale text //create Font to use for scales QFont Font("Arial"); QFontMetrics metrics(Font); y = h/4; Font.setPixelSize(y); Font.setWeight(QFont::Normal); painter.setFont(Font); int rwidth = (int)((hstop-marg)/7.0); m_Str = "+60"; rect.setRect(marg/2, 0, rwidth, magstart); for(x=1; x<=9; x+=2) { m_Str.setNum(x); painter.drawText(rect, Qt::AlignHCenter|Qt::AlignVCenter, m_Str); rect.translate( rwidth,0); } painter.setPen(QPen(Qt::red, 1,Qt::SolidLine)); for(x=20; x<=60; x+=20) { m_Str = "+" + m_Str.setNum(x); painter.drawText(rect, Qt::AlignHCenter|Qt::AlignVCenter, m_Str); rect.translate( rwidth,0); } } cutesdr-1.0.5/gui/demodsetupdlg.cpp0000644000175000017500000001622111716016005017255 0ustar bottomsbottoms///////////////////////////////////////////////////////////////////// // demodsetupdlg.cpp: implementation of the CDemodSetupDlg class. // // This class implements a dialog to setup the demodulation parameters // // History: // 2010-09-15 Initial creation MSW // 2011-03-27 Initial release // 2011-08-07 Added WFM Support // 2012-02-11 Fixed compiler warning ///////////////////////////////////////////////////////////////////// //========================================================================================== // + + + This Software is released under the "Simplified BSD License" + + + //Copyright 2010 Moe Wheatley. All rights reserved. // //Redistribution and use in source and binary forms, with or without modification, are //permitted provided that the following conditions are met: // // 1. Redistributions of source code must retain the above copyright notice, this list of // conditions and the following disclaimer. // // 2. Redistributions in binary form must reproduce the above copyright notice, this list // of conditions and the following disclaimer in the documentation and/or other materials // provided with the distribution. // //THIS SOFTWARE IS PROVIDED BY Moe Wheatley ``AS IS'' AND ANY EXPRESS OR IMPLIED //WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND //FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL Moe Wheatley OR //CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR //CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR //SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON //ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING //NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF //ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // //The views and conclusions contained in the software and documentation are those of the //authors and should not be interpreted as representing official policies, either expressed //or implied, of Moe Wheatley. //========================================================================================== #include "gui/demodsetupdlg.h" #include "ui_demodsetupdlg.h" #include "gui/mainwindow.h" #include CDemodSetupDlg::CDemodSetupDlg(QWidget *parent) : QDialog(parent), ui(new Ui::CDemodSetupDlg) { ui->setupUi(this); m_pDemodInfo = NULL; m_DemodMode = DEMOD_AM; connect(ui->frameSlope, SIGNAL(sliderValChanged(int)), this, SLOT(OnAgcSlope(int))); connect(ui->frameThresh, SIGNAL(sliderValChanged(int)), this, SLOT(OnAgcThresh(int))); connect(ui->frameDecay, SIGNAL(sliderValChanged(int)), this, SLOT(OnAgcDecay(int))); ui->frameSlope->SetName("Slope"); ui->frameSlope->SetSuffix(" dB"); ui->frameSlope->setRange(0, 10); ui->frameSlope->setSingleStep(1); ui->frameSlope->setPageStep(1); ui->frameSlope->setTickInterval(1); ui->frameThresh->SetName("Knee"); ui->frameThresh->SetSuffix(" dB"); ui->frameThresh->setRange(-120, -30); ui->frameThresh->setSingleStep(5); ui->frameThresh->setPageStep(5); ui->frameThresh->setTickInterval(10); ui->frameDecay->SetName("Decay"); ui->frameDecay->SetSuffix(" mS"); ui->frameDecay->setRange(20, 2000); ui->frameDecay->setSingleStep(10); ui->frameDecay->setPageStep(100); ui->frameDecay->setTickInterval(200); } CDemodSetupDlg::~CDemodSetupDlg() { delete ui; } void CDemodSetupDlg::OnAgcOn(bool On) { if(m_pDemodInfo) { m_pDemodInfo->AgcOn = On; UpdateDemodInfo(); ((MainWindow*)this->parent())->SetupDemod(m_DemodMode); } } void CDemodSetupDlg::OnHangOn(bool On) { if(m_pDemodInfo) { m_pDemodInfo->AgcHangOn = On; UpdateDemodInfo(); ((MainWindow*)this->parent())->SetupDemod(m_DemodMode); } } void CDemodSetupDlg::OnUSFm(bool Us) { Q_UNUSED(Us); ((MainWindow*)this->parent())->SetupDemod(m_DemodMode); } //Fill in initial data void CDemodSetupDlg::InitDlg() { switch(m_DemodMode) { case DEMOD_AM: ui->AMradioButton->setChecked(TRUE); break; case DEMOD_SAM: ui->SAMradioButton->setChecked(TRUE); break; case DEMOD_FM: ui->FMradioButton->setChecked(TRUE); break; case DEMOD_WFM: ui->WFMradioButton->setChecked(TRUE); break; case DEMOD_USB: ui->USBradioButton->setChecked(TRUE); break; case DEMOD_LSB: ui->LSBradioButton->setChecked(TRUE); break; case DEMOD_CWU: ui->CWUradioButton->setChecked(TRUE); break; case DEMOD_CWL: ui->CWLradioButton->setChecked(TRUE); break; } m_pDemodInfo = &(((MainWindow*)this->parent())->m_DemodSettings[m_DemodMode]); UpdateDemodInfo(); } void CDemodSetupDlg::UpdateDemodInfo() { int tmp; ui->checkBoxAgcOn->setChecked(m_pDemodInfo->AgcOn); ui->checkBoxHang->setChecked(m_pDemodInfo->AgcHangOn); if(m_pDemodInfo->AgcOn) { ui->frameThresh->SetName("Knee"); tmp = m_pDemodInfo->AgcThresh;//save since range change triggers a value change for some reason ui->frameThresh->setRange(-120, -20); ui->frameThresh->SetValue(tmp); } else { ui->frameThresh->SetName("Gain"); tmp = m_pDemodInfo->AgcManualGain;//save since range change triggers a value change for some reason ui->frameThresh->setRange(0, 100); ui->frameThresh->SetValue(tmp); } if(m_pDemodInfo->AgcHangOn) { ui->frameDecay->SetName("Hang"); } else { ui->frameDecay->SetName("Decay"); } ui->spinBoxOffset->setValue(m_pDemodInfo->Offset); ui->horizontalSliderSquelch->setValue(m_pDemodInfo->SquelchValue); ui->frameSlope->SetValue(m_pDemodInfo->AgcSlope); ui->frameDecay->SetValue(m_pDemodInfo->AgcDecay); } void CDemodSetupDlg::OnOffsetChanged(int Offset) { if(m_pDemodInfo) { m_pDemodInfo->Offset = Offset; ((MainWindow*)this->parent())->SetupDemod(m_DemodMode); } } void CDemodSetupDlg::OnSquelchChanged(int SquelchValue) { if(m_pDemodInfo) { m_pDemodInfo->SquelchValue = SquelchValue; ((MainWindow*)this->parent())->SetupDemod(m_DemodMode); } } //Called when new mode is pressed so set the dialog data void CDemodSetupDlg::ModeChanged() { if(ui->AMradioButton->isChecked()) m_DemodMode = DEMOD_AM; else if(ui->SAMradioButton->isChecked()) m_DemodMode = DEMOD_SAM; else if(ui->FMradioButton->isChecked()) m_DemodMode = DEMOD_FM; else if(ui->WFMradioButton->isChecked()) m_DemodMode = DEMOD_WFM; else if(ui->USBradioButton->isChecked()) m_DemodMode = DEMOD_USB; else if(ui->LSBradioButton->isChecked()) m_DemodMode = DEMOD_LSB; else if(ui->CWUradioButton->isChecked()) m_DemodMode = DEMOD_CWU; else if(ui->CWLradioButton->isChecked()) m_DemodMode = DEMOD_CWL; ((MainWindow*)this->parent())->SetupDemod(m_DemodMode); m_pDemodInfo = &(((MainWindow*)this->parent())->m_DemodSettings[m_DemodMode]); UpdateDemodInfo(); } void CDemodSetupDlg::OnAgcSlope(int val) { if(m_pDemodInfo) { m_pDemodInfo->AgcSlope = val; ((MainWindow*)this->parent())->SetupDemod(m_DemodMode); } } void CDemodSetupDlg::OnAgcThresh(int val) { if(m_pDemodInfo) { if(m_pDemodInfo->AgcOn) m_pDemodInfo->AgcThresh = val; else m_pDemodInfo->AgcManualGain = val; ((MainWindow*)this->parent())->SetupDemod(m_DemodMode); } } void CDemodSetupDlg::OnAgcDecay(int val) { if(m_pDemodInfo) { m_pDemodInfo->AgcDecay = val; ((MainWindow*)this->parent())->SetupDemod(m_DemodMode); } } cutesdr-1.0.5/gui/testbench.ui0000644000175000017500000006200311711303214016222 0ustar bottomsbottoms CTestBench 0 0 695 400 0 0 Test Bench false 0 0 0 200 QFrame::Box QFrame::Plain 1 0 0 0 100 17 Time Domain 0 0 110 0 Enable Peak 0 0 100 0 -34156 34156 10 10 0 0 Qt::Horizontal 0 0 100 20 Hz Update 1 15 1 10 0 0 75 25 Reset 0 0 160 22 0 0 301 140 Signal Generator 120 50 71 16 70 0 Qt::NoFocus QFrame::Panel QFrame::Sunken Frequency 10 80 151 20 Hz/Sec Sweep 0 1000 10 10 50 101 20 KHz Stop -1000 1000 1 10 20 101 20 KHz Start -1000 1000 1 30 110 91 22 dB Noise -160 0 120 20 90 22 90 0 dB Pwr -160 0 220 10 70 17 70 0 Gen On 170 80 111 22 110 0 mS Width 500 1 170 110 111 22 100 0 mS Period 1000 100 220 60 61 16 Pulse Mode Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter 220 40 70 17 70 0 FM Stereo 0 0 100 140 150 16777215 true false Qt::NoTextInteraction 0 0 150 141 Time Domain Display 10 20 131 20 0 0 Range Vert 10 66000 100 1000 10 50 131 20 mSec Horz 1 1000 1 10 80 131 20 Trig Level -30000 30000 10 100 10 110 131 22 checkBoxTime toggled(bool) CTestBench OnTimeDisplay(bool) 109 236 40 327 spinBoxRate valueChanged(int) CTestBench OnDisplayRate(int) 375 237 234 324 spinBoxVertRange valueChanged(int) CTestBench OnVertRange(int) 613 288 539 345 spinBoxHorzSpan valueChanged(int) CTestBench OnHorzSpan(int) 613 318 541 374 comboBoxTrig currentIndexChanged(int) CTestBench OnTriggerMode(int) 613 380 530 404 pushButtonReset clicked() CTestBench Reset() 456 240 231 309 spinBoxStart valueChanged(int) CTestBench OnSweepStart(int) 120 288 75 315 spinBoxStop valueChanged(int) CTestBench OnSweepStop(int) 89 318 7 370 spinBoxSweep valueChanged(int) CTestBench OnSweepRate(int) 62 348 5 442 spinBoxThresh valueChanged(int) CTestBench OnTrigLevel(int) 613 348 470 469 comboBoxProfile currentIndexChanged(int) CTestBench OnProfile(int) 622 238 608 390 spinBoxAmp valueChanged(int) CTestBench OnSignalPwr(int) 189 290 163 353 checkBoxGen toggled(bool) CTestBench OnGenOn(bool) 271 275 238 361 spinBoxNoise valueChanged(int) CTestBench OnNoisePwr(int) 112 380 20 511 spinBoxPulseWidth valueChanged(int) CTestBench OnPulseWidth(int) 281 350 306 344 spinBoxPulsePeriod valueChanged(int) CTestBench OnPulsePeriod(int) 236 380 296 512 checkBoxPeak toggled(bool) CTestBench OnEnablePeak(bool) 225 236 194 360 horizontalSliderTest valueChanged(int) CTestBench OnTestSlider1(int) 271 226 291 242 checkBoxFm toggled(bool) CTestBench OnFmGen(bool) 299 296 333 294 ResetGenerator(int) Reset() OnTimeDisplay(bool) OnDisplayRate(int) OnVertRange(int) OnHorzSpan(int) OnTriggerMode(int) OnSweepStart(int) OnSweepStop(int) OnSweepRate(int) OnTrigLevel(int) OnProfile(int) OnGenSelect(int) OnSignalPwr(int) OnGenOn(bool) OnNoisePwr(int) OnPulseWidth(int) OnPulsePeriod(int) OnEnablePeak(bool) OnTestSlider1() OnTestSlider1(int) OnFmGen(bool) cutesdr-1.0.5/gui/sounddlg.cpp0000644000175000017500000000571211546075374016256 0ustar bottomsbottoms///////////////////////////////////////////////////////////////////// // sounddlg.cpp: implementation of the CSoundDlg class. // // This class implements a dialog to select a soundcard. // // History: // 2010-09-15 Initial creation MSW // 2011-03-27 Initial release ///////////////////////////////////////////////////////////////////// //========================================================================================== // + + + This Software is released under the "Simplified BSD License" + + + //Copyright 2010 Moe Wheatley. All rights reserved. // //Redistribution and use in source and binary forms, with or without modification, are //permitted provided that the following conditions are met: // // 1. Redistributions of source code must retain the above copyright notice, this list of // conditions and the following disclaimer. // // 2. Redistributions in binary form must reproduce the above copyright notice, this list // of conditions and the following disclaimer in the documentation and/or other materials // provided with the distribution. // //THIS SOFTWARE IS PROVIDED BY Moe Wheatley ``AS IS'' AND ANY EXPRESS OR IMPLIED //WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND //FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL Moe Wheatley OR //CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR //CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR //SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON //ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING //NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF //ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // //The views and conclusions contained in the software and documentation are those of the //authors and should not be interpreted as representing official policies, either expressed //or implied, of Moe Wheatley. //========================================================================================== #include "gui/sounddlg.h" #include #include ///////////////////////////////////////////////////////////////////// // Constructor/Destructor ///////////////////////////////////////////////////////////////////// CSoundDlg::CSoundDlg(QWidget *parent) : QDialog(parent) { QList InDevices; QList OutDevices; ui.setupUi(this); //setup the dialog form QAudioDeviceInfo deviceInfo; InDevices = deviceInfo.availableDevices(QAudio::AudioInput); OutDevices = deviceInfo.availableDevices(QAudio::AudioOutput); foreach (const QAudioDeviceInfo &deviceInfo, InDevices) ui.comboBoxSndIn->addItem(deviceInfo.deviceName(), qVariantFromValue(deviceInfo)); foreach (const QAudioDeviceInfo &deviceInfo, OutDevices) ui.comboBoxSndOut->addItem(deviceInfo.deviceName(), qVariantFromValue(deviceInfo)); } CSoundDlg::~CSoundDlg() { } cutesdr-1.0.5/gui/freqctrl.cpp0000644000175000017500000005313711716016005016246 0ustar bottomsbottoms///////////////////////////////////////////////////////////////////// // freqctrl.cpp: implementation of the CFreqCtrl class. // // This class implements a frequency control widget to set/change //frequency data. // // History: // 2010-09-15 Initial creation MSW // 2011-03-27 Initial release // 2011-04-17 Fixed bug with m_Oldfreq being set after emit instead of before // 2012-02-11 Fixed compiler warning ///////////////////////////////////////////////////////////////////// //========================================================================================== // + + + This Software is released under the "Simplified BSD License" + + + //Copyright 2010 Moe Wheatley. All rights reserved. // //Redistribution and use in source and binary forms, with or without modification, are //permitted provided that the following conditions are met: // // 1. Redistributions of source code must retain the above copyright notice, this list of // conditions and the following disclaimer. // // 2. Redistributions in binary form must reproduce the above copyright notice, this list // of conditions and the following disclaimer in the documentation and/or other materials // provided with the distribution. // //THIS SOFTWARE IS PROVIDED BY Moe Wheatley ``AS IS'' AND ANY EXPRESS OR IMPLIED //WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND //FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL Moe Wheatley OR //CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR //CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR //SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON //ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING //NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF //ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // //The views and conclusions contained in the software and documentation are those of the //authors and should not be interpreted as representing official policies, either expressed //or implied, of Moe Wheatley. //========================================================================================== #include "gui/freqctrl.h" #include #include //Manual adjustment of Font size as percent of control height #define DIGIT_SIZE_PERCENT 90 #define UNITS_SIZE_PERCENT 60 //adjustment for separation between digits #define SEPRATIO_N 100 //separation rectangle size ratio numerator times 100 #define SEPRATIO_D 3 //separation rectangle size ratio denominator ///////////////////////////////////////////////////////////////////// // Constructor/Destructor ///////////////////////////////////////////////////////////////////// CFreqCtrl::CFreqCtrl(QWidget *parent) : QFrame(parent) { setAutoFillBackground(false); setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); setFocusPolicy(Qt::StrongFocus); setMouseTracking ( TRUE ); m_BkColor = Qt::black; m_DigitColor = Qt::green; m_HighlightColor = Qt::yellow; m_UnitsColor = Qt::gray; m_freq = 146123456; Setup( 10, 1, 4000000000U, 1, UNITS_MHZ); m_Oldfreq = 0; m_LastLeadZeroPos = 0; m_LRMouseFreqSel = TRUE; m_ActiveEditDigit = -1; m_UnitsFont = QFont("Arial",8,QFont::Normal); m_DigitFont = QFont("Arial",8,QFont::Normal); } CFreqCtrl::~CFreqCtrl() { } ///////////////////////////////////////////////////////////////////// // Size hint stuff ///////////////////////////////////////////////////////////////////// QSize CFreqCtrl::minimumSizeHint() const { return QSize(100, 20); } QSize CFreqCtrl::sizeHint() const { return QSize(100, 20); } ///////////////////////////////////////////////////////////////////// // Various helper functions ///////////////////////////////////////////////////////////////////// bool CFreqCtrl::InRect(QRect &rect, QPoint &point) { if( ( point.x() < rect.right( ) ) && ( point.x() > rect.x() ) && ( point.y() < rect.bottom() ) && ( point.y() > rect.y() ) ) return TRUE; else return FALSE; } ////////////////////////////////////////////////////////////////////////////// // Setup various parameters for the control ////////////////////////////////////////////////////////////////////////////// void CFreqCtrl::Setup(int NumDigits, qint64 Minf, qint64 Maxf,int MinStep, FUNITS UnitsType) { int i; qint64 pwr = 1; m_LastEditDigit = 0; m_Oldfreq = -1; m_NumDigits = NumDigits; if( m_NumDigits>MAX_DIGITS ) m_NumDigits = MAX_DIGITS; if( m_NumDigits m_MaxFreq) m_freq = m_MaxFreq; for(i=0; ipwr ) m_MaxFreq = pwr-1; m_MaxFreq = m_MaxFreq - m_MaxFreq%m_MinStep; if( m_MinFreq>pwr ) m_MinFreq = 1; m_MinFreq = m_MinFreq - m_MinFreq%m_MinStep; m_DigStart = 0; switch(UnitsType) { case UNITS_HZ: m_DecPos = 0; m_UnitString = "Hz "; break; case UNITS_KHZ: m_DecPos = 3; m_UnitString = "KHz"; break; case UNITS_MHZ: m_DecPos = 6; m_UnitString = "MHz"; break; case UNITS_GHZ: m_DecPos = 9; m_UnitString = "GHz"; break; case UNITS_SEC: m_DecPos = 6; m_UnitString = "Sec"; break; case UNITS_MSEC: m_DecPos = 3; m_UnitString = "mS "; break; case UNITS_USEC: m_DecPos = 0; m_UnitString = "uS "; break; case UNITS_NSEC: m_DecPos = 0; m_UnitString = "nS "; break; } for(i=m_NumDigits-1; i>=0; i--) { if( m_DigitInfo[i].weight <= m_MinStep ) { if(m_DigStart == 0) { m_DigitInfo[i].incval = m_MinStep; m_DigStart = i; } else { if( (m_MinStep%m_DigitInfo[i+1].weight) != 0) m_DigStart = i; m_DigitInfo[i].incval = 0; } } } m_NumSeps = (m_NumDigits-1)/3 - m_DigStart/3; } ////////////////////////////////////////////////////////////////////////////// // Sets the frequency and individual digit values ////////////////////////////////////////////////////////////////////////////// void CFreqCtrl::SetFrequency(qint64 freq) { int i; qint64 acc = 0; qint64 rem; int val; if( freq == m_Oldfreq) return; if( freq < m_MinFreq) freq = m_MinFreq; if( freq > m_MaxFreq) freq = m_MaxFreq; m_freq = freq - freq%m_MinStep; rem = m_freq; m_LeadZeroPos = m_NumDigits; for(i=m_NumDigits-1; i>=m_DigStart; i--) { val = (int)(rem/m_DigitInfo[i].weight); if(m_DigitInfo[i].val != val) { m_DigitInfo[i].val = val; m_DigitInfo[i].modified = TRUE; } rem = rem - val*m_DigitInfo[i].weight; acc += val; if( (acc==0) && ( i>m_DecPos) ) m_LeadZeroPos = i; } // signal the new frequency to world m_Oldfreq = m_freq; emit NewFrequency( m_freq ); UpdateCtrl(m_LastLeadZeroPos != m_LeadZeroPos); m_LastLeadZeroPos = m_LeadZeroPos; } ////////////////////////////////////////////////////////////////////////////// // Sets the Digit and comma and decimal pt color ////////////////////////////////////////////////////////////////////////////// void CFreqCtrl::SetDigitColor(QColor cr) { m_UpdateAll = TRUE; m_DigitColor = cr; for(int i=m_DigStart; i=0) { if( m_DigitInfo[m_ActiveEditDigit].editmode ) { m_DigitInfo[m_ActiveEditDigit].editmode = FALSE; m_DigitInfo[m_ActiveEditDigit].modified = TRUE; m_ActiveEditDigit = -1; UpdateCtrl(FALSE); } } } ///////////////////////////////////////////////////////////////////// // main draw event for this control ///////////////////////////////////////////////////////////////////// void CFreqCtrl::paintEvent(QPaintEvent *) { QPainter painter(&m_Pixmap); if(m_UpdateAll) //if need to redraw everything { DrawBkGround(painter); m_UpdateAll = FALSE; } // draw any modified digits to the m_MemDC DrawDigits(painter); //now draw pixmap onto screen QPainter scrnpainter(this); scrnpainter.drawPixmap(0,0,m_Pixmap); //blt to the screen(flickers like a candle, why?) } ///////////////////////////////////////////////////////////////////// // Mouse Event overrides ///////////////////////////////////////////////////////////////////// void CFreqCtrl::mouseMoveEvent(QMouseEvent * event) { QPoint pt = event->pos(); //find which digit is to be edited if( isActiveWindow() ) { if(!hasFocus()) setFocus(Qt::MouseFocusReason); for(int i=m_DigStart; ipos(); if(event->button() == Qt::LeftButton) { for(int i=m_DigStart; ibutton() == Qt::RightButton) { for(int i=m_DigStart; ipos(); int numDegrees = event->delta() / 8; int numSteps = numDegrees / 15; for(int i=m_DigStart; i0) IncFreq(); else if(numSteps<0) DecFreq(); } } } ///////////////////////////////////////////////////////////////////// // Keyboard Event overrides ///////////////////////////////////////////////////////////////////// void CFreqCtrl::keyPressEvent( QKeyEvent * event ) { //call base class if dont over ride key bool fSkipMsg = FALSE; qint64 tmp; //qDebug() <key(); switch(event->key()) { case Qt::Key_0: case Qt::Key_1: case Qt::Key_2: case Qt::Key_3: case Qt::Key_4: case Qt::Key_5: case Qt::Key_6: case Qt::Key_7: case Qt::Key_8: case Qt::Key_9: if( m_ActiveEditDigit>=0) { if( m_DigitInfo[m_ActiveEditDigit].editmode) { tmp = (m_freq/m_DigitInfo[m_ActiveEditDigit].weight)%10; m_freq -= tmp*m_DigitInfo[m_ActiveEditDigit].weight; m_freq = m_freq+(event->key()-'0')*m_DigitInfo[m_ActiveEditDigit].weight; SetFrequency(m_freq); } } MoveCursorRight(); fSkipMsg = TRUE; break; case Qt::Key_Left: if( m_ActiveEditDigit != -1 ) { MoveCursorLeft(); fSkipMsg = TRUE; } break; case Qt::Key_Up: if(m_ActiveEditDigit != -1 ) { IncFreq(); fSkipMsg = TRUE; } break; case Qt::Key_Down: if(m_ActiveEditDigit != -1) { DecFreq(); fSkipMsg = TRUE; } break; case Qt::Key_Right: if(m_ActiveEditDigit != -1 ) { MoveCursorRight(); fSkipMsg = TRUE; } break; case Qt::Key_Home: CursorHome(); fSkipMsg = TRUE; break; case Qt::Key_End: CursorEnd(); fSkipMsg = TRUE; break; default: break; } if(!fSkipMsg) QFrame::keyPressEvent(event); } ////////////////////////////////////////////////////////////////////////////// // Calculates all the rectangles for the digits, separators, and units text // and creates the fonts for them. ////////////////////////////////////////////////////////////////////////////// void CFreqCtrl::DrawBkGround(QPainter &Painter) { QRect rect(0, 0, width(), height()); //qDebug() <m_DigStart) && ( (i%3)==0 ) ) { m_SepRect[i].setCoords( digpos - sepwidth, rect.top(), digpos, rect.bottom()); Painter.fillRect(m_SepRect[i], m_BkColor); digpos -= sepwidth; if( i==m_DecPos) Painter.drawText(m_SepRect[i], Qt::AlignHCenter|Qt::AlignVCenter, "."); else if( i>m_DecPos && i= m_LeadZeroPos) Painter.setPen(m_BkColor); else Painter.setPen(m_DigitColor); Painter.drawText(m_DigitInfo[i].dQRect, Qt::AlignHCenter|Qt::AlignVCenter, QString().number( m_DigitInfo[i].val ) ); m_DigitInfo[i].modified = FALSE; } } } ////////////////////////////////////////////////////////////////////////////// // Increment just the digit active in edit mode ////////////////////////////////////////////////////////////////////////////// void CFreqCtrl::IncDigit() { int tmp; qint64 tmpl; if( m_ActiveEditDigit>=0) { if( m_DigitInfo[m_ActiveEditDigit].editmode) { if(m_DigitInfo[m_ActiveEditDigit].weight == m_DigitInfo[m_ActiveEditDigit].incval) { // get the current digit value tmp = (int)((m_freq/m_DigitInfo[m_ActiveEditDigit].weight)%10); // set the current digit value to zero m_freq -= tmp*m_DigitInfo[m_ActiveEditDigit].weight; tmp++; if( tmp>9 ) tmp = 0; m_freq = m_freq+(qint64)tmp*m_DigitInfo[m_ActiveEditDigit].weight; } else { tmp = (int)((m_freq/m_DigitInfo[m_ActiveEditDigit+1].weight)%10); tmpl = m_freq + m_DigitInfo[m_ActiveEditDigit].incval; if(tmp != (int)((tmpl/m_DigitInfo[m_ActiveEditDigit+1].weight)%10) ) { tmpl -= m_DigitInfo[m_ActiveEditDigit+1].weight; } m_freq = tmpl; } SetFrequency(m_freq); } } } ////////////////////////////////////////////////////////////////////////////// // Increment the frequency by this digit active in edit mode ////////////////////////////////////////////////////////////////////////////// void CFreqCtrl::IncFreq() { if( m_ActiveEditDigit>=0) { if( m_DigitInfo[m_ActiveEditDigit].editmode) { m_freq += m_DigitInfo[m_ActiveEditDigit].incval; m_freq = m_freq - m_freq%m_DigitInfo[m_ActiveEditDigit].weight; SetFrequency(m_freq); m_LastEditDigit = m_ActiveEditDigit; } } } ////////////////////////////////////////////////////////////////////////////// // Decrement the digit active in edit mode ////////////////////////////////////////////////////////////////////////////// void CFreqCtrl::DecDigit() { int tmp; qint64 tmpl; if( m_ActiveEditDigit>=0) { if( m_DigitInfo[m_ActiveEditDigit].editmode) { if(m_DigitInfo[m_ActiveEditDigit].weight == m_DigitInfo[m_ActiveEditDigit].incval) { // get the current digit value tmp = (int)((m_freq/m_DigitInfo[m_ActiveEditDigit].weight)%10); // set the current digit value to zero m_freq -= tmp*m_DigitInfo[m_ActiveEditDigit].weight; tmp--; if( tmp<0 ) tmp = 9; m_freq = m_freq+(qint64)tmp*m_DigitInfo[m_ActiveEditDigit].weight; } else { tmp = (int)((m_freq/m_DigitInfo[m_ActiveEditDigit+1].weight)%10); tmpl = m_freq - m_DigitInfo[m_ActiveEditDigit].incval; if(tmp != (int)((tmpl/m_DigitInfo[m_ActiveEditDigit+1].weight)%10) ) { tmpl += m_DigitInfo[m_ActiveEditDigit+1].weight; } m_freq = tmpl; } SetFrequency(m_freq); } } } ////////////////////////////////////////////////////////////////////////////// // Decrement the frequency by this digit active in edit mode ////////////////////////////////////////////////////////////////////////////// void CFreqCtrl::DecFreq() { if( m_ActiveEditDigit>=0) { if( m_DigitInfo[m_ActiveEditDigit].editmode) { m_freq -= m_DigitInfo[m_ActiveEditDigit].incval; m_freq = m_freq - m_freq%m_DigitInfo[m_ActiveEditDigit].weight; SetFrequency(m_freq); m_LastEditDigit = m_ActiveEditDigit; } } } ///////////////////////////////////////////////////////////////////// // Cursor move routines for arrow key editing ///////////////////////////////////////////////////////////////////// void CFreqCtrl::MoveCursorLeft() { QPoint pt; if( (m_ActiveEditDigit >=0) && (m_ActiveEditDigit m_FirstEditableDigit ) { cursor().setPos( mapToGlobal( m_DigitInfo[--m_ActiveEditDigit].dQRect.center() ) ); } } void CFreqCtrl::CursorHome() { QPoint pt; if( m_ActiveEditDigit >= 0 ) { cursor().setPos( mapToGlobal( m_DigitInfo[m_NumDigits-1].dQRect.center() ) ); } } void CFreqCtrl::CursorEnd() { QPoint pt; if( m_ActiveEditDigit > 0 ) { cursor().setPos( mapToGlobal( m_DigitInfo[m_FirstEditableDigit].dQRect.center() ) ); } } cutesdr-1.0.5/gui/rdsdecode.cpp0000644000175000017500000002272711711303214016355 0ustar bottomsbottoms///////////////////////////////////////////////////////////////////// // rdsdecode.cpp: implementation of the CRdsDecode class. // // This class implements a an RDS data decoder // // History: // 2011-09-18 Initial creation MSW // 2011-09-18 Initial release ///////////////////////////////////////////////////////////////////// //========================================================================================== // + + + This Software is released under the "Simplified BSD License" + + + //Copyright 2011 Moe Wheatley. All rights reserved. // //Redistribution and use in source and binary forms, with or without modification, are //permitted provided that the following conditions are met: // // 1. Redistributions of source code must retain the above copyright notice, this list of // conditions and the following disclaimer. // // 2. Redistributions in binary form must reproduce the above copyright notice, this list // of conditions and the following disclaimer in the documentation and/or other materials // provided with the distribution. // //THIS SOFTWARE IS PROVIDED BY Moe Wheatley ``AS IS'' AND ANY EXPRESS OR IMPLIED //WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND //FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL Moe Wheatley OR //CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR //CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR //SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON //ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING //NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF //ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // //The views and conclusions contained in the software and documentation are those of the //authors and should not be interpreted as representing official policies, either expressed //or implied, of Moe Wheatley. //========================================================================================== #include "rdsdecode.h" #include CRdsDecode::CRdsDecode() { DecodeReset(true); } /////////////////////////////////////////////////////////////////////////////// //Call to reset the decoder and clear out the current text /////////////////////////////////////////////////////////////////////////////// void CRdsDecode::DecodeReset(int USFm) { int i; m_USFm = USFm; for(i=0; i<64; i++) { m_RText[i] = ' '; m_RTextOut[i] = ' '; m_PSText[i] = ' '; m_PSTextOut[i] = ' '; m_RBDSCallSign[i] = ' '; } m_RText[0] = 0; m_RTextOut[0] = 0; m_PSText[0] = 0; m_PSTextOut[0] = 0; m_RBDSCallSign[0] = 0; m_PTYText[0] = 0; m_LastABBit = -1; m_LastPrgType = -1; m_LastPICode = -1; m_MaxTextPos = -1; //qDebug()<<"Reset RDS"; } //////////////////////////////////////////////////////////////////////// //Call with user char buffer to get filled with latest RDS text string. //Returns true if a change in text string //////////////////////////////////////////////////////////////////////// int CRdsDecode::GetRdsString( char* Str) { m_RDSText[0] = 0; strcat( m_RDSText, m_PTYText ); strcat( m_RDSText, m_RTextOut ); if( strcmp(m_RDSText,Str) ) { strcpy(Str,m_RDSText); //qDebug()<>11) & 0x1F; DecodePIcode(); switch(m_GrpType) { case GRPTYPE_0A: //Program Service Text case GRPTYPE_0B: DecodePSText(); break; case GRPTYPE_1A: //Extended Country Codes break; case GRPTYPE_2A: //64 character Radio text Decode64RadioText(); break; case GRPTYPE_2B: //32 character Radio text Decode32RadioText(); break; default: //qDebug("unimplemented GrpType = %X\n\r", m_GrpType); break; } } //////////////////////////////////////////////////////////////////////// // Generate Program type text string from PTY code //////////////////////////////////////////////////////////////////////// void CRdsDecode::DecodePTYText() { m_PrgType = (m_Group.BlockB>>5) & 0x1F; if(m_PrgType != m_LastPrgType) { m_LastPrgType = m_PrgType; if(m_USFm) strcpy(m_PTYText, PTYPETABLERBDS[m_PrgType] ); else strcpy(m_PTYText, PTYPETABLERDS[m_PrgType] ); } } //////////////////////////////////////////////////////////////////////// //Generate 32 character Radio Text message from rds data //////////////////////////////////////////////////////////////////////// void CRdsDecode::Decode32RadioText() { char ch; int adr = (m_Group.BlockB&0x0F)<<1; if( (m_Group.BlockB & A_B_BIT) != m_LastABBit) { m_LastABBit = (m_Group.BlockB & A_B_BIT); m_RText[0] = 0; m_MaxTextPos = -1; //qDebug("Erase\n\r"); } ch = m_Group.BlockD>>8 & 0xFF; if(ch < ' ') ch = ' '; m_RText[adr+0] = ch; ch = m_Group.BlockD & 0xFF; if(ch < ' ') ch = ' '; m_RText[adr+1] = ch; if(0==adr) { //is starting at beginning of new line if(m_MaxTextPos>0) { m_RText[m_MaxTextPos+1] = 0; strcpy(m_RTextOut,m_RText); } m_MaxTextPos = 0; } else { if(m_MaxTextPos >= 0) { if(adr>m_MaxTextPos) m_MaxTextPos = adr+1; } } } //////////////////////////////////////////////////////////////////////// //Generate 64 character Radio Text message from rds data //////////////////////////////////////////////////////////////////////// void CRdsDecode::Decode64RadioText() { char ch; int adr = (m_Group.BlockB&0x0F)<<2; if( (m_Group.BlockB & A_B_BIT) != m_LastABBit) { m_LastABBit = (m_Group.BlockB & A_B_BIT); m_RText[0] = 0; m_MaxTextPos = -1; //qDebug("Erase\n\r"); } ch = m_Group.BlockC>>8 & 0xFF; if(ch < ' ') ch = ' '; m_RText[adr+0] = ch; ch = m_Group.BlockC & 0xFF; if(ch < ' ') ch = ' '; m_RText[adr+1] = ch; ch = m_Group.BlockD>>8 & 0xFF; if(ch < ' ') ch = ' '; m_RText[adr+2] = ch; ch = m_Group.BlockD & 0xFF; if(ch < ' ') ch = ' '; m_RText[adr+3] = ch; if(0==adr) { //is starting at beginning of new line if( (m_MaxTextPos>0) ) { m_RText[m_MaxTextPos+1] = 0; strcpy(m_RTextOut,m_RText); //qDebug("%s: %u\n\r",m_RText,m_MaxTextPos); } m_MaxTextPos = 0; } else { if(m_MaxTextPos >= 0) { if(adr>m_MaxTextPos) m_MaxTextPos = adr+3; } } } //////////////////////////////////////////////////////////////////////// //Generate Program Service name from rds data //////////////////////////////////////////////////////////////////////// void CRdsDecode::DecodePSText() { int adr = (m_Group.BlockB&0x03)<<1; //qDebug("adr = %u\n\r",adr); m_PSText[adr+0] = m_Group.BlockD>>8 & 0xFF; m_PSText[adr+1] = m_Group.BlockD & 0xFF; if(0==adr) { //is starting at beginning of new string m_PSText[8]=0; strcpy(m_PSTextOut,m_PSText); //qDebug("%s\n\r",m_PSTextOut); } } ///////////////////////////////////////////////////////////////////////////////// // Kludge to get US RBDS radio call signs from PI code. The format was // bastardized by a recent standards chenge so a lot of callsigns cannot be // derived from the code. ///////////////////////////////////////////////////////////////////////////////// void CRdsDecode::DecodePIcode() { quint16 PIcode = m_Group.BlockA; if(m_LastPICode == PIcode) return; if(m_USFm && ( (PIcode&0xF000) != 0x1000) ) { //if US version and doesn't have the new 1xxx hack added by the new RBDS standard if( (PIcode&0xF000) == 0xA000) { //deal with exception codes if( (PIcode&0x0F00) == 0x0F00) { //is AFyz so map to yz00 PIcode = (PIcode<<8); } else { quint16 tmp = PIcode&0x00FF; //save P3,P4 PIcode = ( (PIcode<<4)&0xF000 ) | tmp; } } if( (PIcode>=0x9950) && (PIcode<=0x9EFF) ) { // is a 3 letter call so need to search lookup table m_RBDSCallSign[3] = 0; for(int i=0; i=21672) { PIcode -= 21672; m_RBDSCallSign[0] = 'W'; } else { PIcode -= 4096; m_RBDSCallSign[0] = 'K'; } int x = PIcode/676; m_RBDSCallSign[1] = x + 'A'; PIcode = PIcode - x*676; x = PIcode/26; m_RBDSCallSign[2] = x + 'A'; PIcode = PIcode - x*26; m_RBDSCallSign[3] = PIcode + 'A'; m_RBDSCallSign[4] = 0; } else //for RDS version just make HEX string of PI value { sprintf(m_RBDSCallSign,"%4.4X",PIcode); } } cutesdr-1.0.5/gui/sdrsetupdlg.h0000644000175000017500000000211711711303214016415 0ustar bottomsbottoms////////////////////////////////////////////////////////////////////// // sdrsetupdlg.h: interface for the CSdrSetupDlg class. // // History: // 2010-09-15 Initial creation MSW // 2011-03-27 Initial release ///////////////////////////////////////////////////////////////////// #ifndef SDRSETUPDLG_H #define SDRSETUPDLG_H #include #include "interface/sdrinterface.h" #include "ui_sdrsetupdlg.h" namespace Ui { class CSdrSetupDlg; } class CSdrSetupDlg : public QDialog { Q_OBJECT public: explicit CSdrSetupDlg(QWidget *parent, CSdrInterface* pSdrInterface); ~CSdrSetupDlg(); void SetSpectrumInversion(bool Invert){ui->checkBoxInvert->setChecked(Invert);} bool GetSpectrumInversion(){return ui->checkBoxInvert->checkState();} void InitDlg(); qint32 m_RfGain; qint32 m_BandwidthIndex; bool m_USFm; public slots: void accept(); void RfGainChanged(); private: CSdrInterface* m_pSdrInterface; Ui::CSdrSetupDlg *ui; QString m_Str1; QString m_Str2; qint32 m_RadioType; double m_SampleRate; }; #endif // SDRSETUPDLG_H cutesdr-1.0.5/gui/sdrsetupdlg.cpp0000644000175000017500000001224311711303214016751 0ustar bottomsbottoms///////////////////////////////////////////////////////////////////// // sdrsetupdlg.cpp: implementation of the CSdrSetupDlg class. // // This class implements a dialog to setup SDR parameters. // // History: // 2010-09-15 Initial creation MSW // 2011-03-27 Initial release // 2011-08-07 Added Spectrum inversion Support ///////////////////////////////////////////////////////////////////// //========================================================================================== // + + + This Software is released under the "Simplified BSD License" + + + //Copyright 2010 Moe Wheatley. All rights reserved. // //Redistribution and use in source and binary forms, with or without modification, are //permitted provided that the following conditions are met: // // 1. Redistributions of source code must retain the above copyright notice, this list of // conditions and the following disclaimer. // // 2. Redistributions in binary form must reproduce the above copyright notice, this list // of conditions and the following disclaimer in the documentation and/or other materials // provided with the distribution. // //THIS SOFTWARE IS PROVIDED BY Moe Wheatley ``AS IS'' AND ANY EXPRESS OR IMPLIED //WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND //FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL Moe Wheatley OR //CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR //CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR //SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON //ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING //NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF //ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // //The views and conclusions contained in the software and documentation are those of the //authors and should not be interpreted as representing official policies, either expressed //or implied, of Moe Wheatley. //========================================================================================== #include "gui/sdrsetupdlg.h" #include "ui_sdrsetupdlg.h" #include "gui/mainwindow.h" #include CSdrSetupDlg::CSdrSetupDlg(QWidget *parent, CSdrInterface* pSdrInterface) : QDialog(parent), m_pSdrInterface(pSdrInterface), ui(new Ui::CSdrSetupDlg) { ui->setupUi(this); m_BandwidthIndex = 0; m_RfGain = pSdrInterface->GetSdrRfGain(); m_RadioType = pSdrInterface->GetRadioType(); m_SampleRate = pSdrInterface->GetSdrSampleRate(); } CSdrSetupDlg::~CSdrSetupDlg() { delete ui; } //Fill in initial data void CSdrSetupDlg::InitDlg() { m_Str1 = QString().number( m_pSdrInterface->GetMaxBWFromIndex(0)/1000 )+ " KHz "; m_Str2 = QString().number(m_pSdrInterface->GetSampleRateFromIndex(0)/1000.0)+ " Ksps"; ui->radioButtonRate0->setText(m_Str1 + m_Str2 ); m_Str1 = QString().number( m_pSdrInterface->GetMaxBWFromIndex(1)/1000 )+ " KHz "; m_Str2 = QString().number(m_pSdrInterface->GetSampleRateFromIndex(1)/1000.0)+ " Ksps"; ui->radioButtonRate1->setText(m_Str1 + m_Str2 ); m_Str1 = QString().number( m_pSdrInterface->GetMaxBWFromIndex(2)/1000 )+ " KHz "; m_Str2 = QString().number(m_pSdrInterface->GetSampleRateFromIndex(2)/1000.0)+ " Ksps"; ui->radioButtonRate2->setText(m_Str1 + m_Str2 ); m_Str1 = QString().number( m_pSdrInterface->GetMaxBWFromIndex(3)/1000 )+ " KHz "; m_Str2 = QString().number(m_pSdrInterface->GetSampleRateFromIndex(3)/1000.0)+ " Ksps"; ui->radioButtonRate3->setText(m_Str1 + m_Str2 ); if(0==m_RfGain) ui->radioButtonAttn0->setChecked(true); else if(-10==m_RfGain) ui->radioButtonAttn10->setChecked(true); else if(-20==m_RfGain) ui->radioButtonAttn20->setChecked(true); else if(-30==m_RfGain) ui->radioButtonAttn30->setChecked(true); if(0 == m_BandwidthIndex) ui->radioButtonRate0->setChecked(true); else if(1 == m_BandwidthIndex) ui->radioButtonRate1->setChecked(true); else if(2 == m_BandwidthIndex) ui->radioButtonRate2->setChecked(true); else if(3 == m_BandwidthIndex) ui->radioButtonRate3->setChecked(true); if(m_USFm) ui->checkBoxUSFmVer->setChecked(true); else ui->checkBoxUSFmVer->setChecked(false); ui->labelRadioType->setText(m_pSdrInterface->m_DeviceName); } void CSdrSetupDlg::RfGainChanged() { if(ui->radioButtonAttn0->isChecked()) m_RfGain = 0; else if(ui->radioButtonAttn10->isChecked()) m_RfGain = -10; else if(ui->radioButtonAttn20->isChecked()) m_RfGain = -20; else if(ui->radioButtonAttn30->isChecked()) m_RfGain = -30; m_pSdrInterface->SetSdrRfGain(m_RfGain); } //Called when OK button pressed so get all the dialog data void CSdrSetupDlg::accept() { if(ui->radioButtonRate0->isChecked()) m_BandwidthIndex = 0; else if(ui->radioButtonRate1->isChecked()) m_BandwidthIndex = 1; else if(ui->radioButtonRate2->isChecked()) m_BandwidthIndex = 2; else if(ui->radioButtonRate3->isChecked()) m_BandwidthIndex = 3; if( ui->checkBoxUSFmVer->isChecked() ) m_USFm = true; else m_USFm = false; QDialog::accept(); } cutesdr-1.0.5/gui/noiseprocdlg.cpp0000644000175000017500000001004711546075374017124 0ustar bottomsbottoms///////////////////////////////////////////////////////////////////// // noiseprocdlg.cpp: implementation of the CNoiseProcDlg class. // // This class implements a dialog to setup the noise processing parameters // // History: // 2011-02-05 Initial creation MSW // 2011-03-27 Initial release ///////////////////////////////////////////////////////////////////// //========================================================================================== // + + + This Software is released under the "Simplified BSD License" + + + //Copyright 2010 Moe Wheatley. All rights reserved. // //Redistribution and use in source and binary forms, with or without modification, are //permitted provided that the following conditions are met: // // 1. Redistributions of source code must retain the above copyright notice, this list of // conditions and the following disclaimer. // // 2. Redistributions in binary form must reproduce the above copyright notice, this list // of conditions and the following disclaimer in the documentation and/or other materials // provided with the distribution. // //THIS SOFTWARE IS PROVIDED BY Moe Wheatley ``AS IS'' AND ANY EXPRESS OR IMPLIED //WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND //FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL Moe Wheatley OR //CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR //CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR //SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON //ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING //NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF //ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // //The views and conclusions contained in the software and documentation are those of the //authors and should not be interpreted as representing official policies, either expressed //or implied, of Moe Wheatley. //========================================================================================== #include "gui/noiseprocdlg.h" #include "gui/mainwindow.h" #include "ui_noiseprocdlg.h" #include CNoiseProcDlg::CNoiseProcDlg(QWidget *parent) : QDialog(parent), ui(new Ui::CNoiseProcDlg) { ui->setupUi(this); m_pNoiseProcSettings = NULL; connect(ui->frameNBWidth, SIGNAL(sliderValChanged(int)), this, SLOT(OnNBWidth(int))); connect(ui->frameNBThresh, SIGNAL(sliderValChanged(int)), this, SLOT(OnNBThresh(int))); ui->frameNBThresh->SetName("Thresh"); ui->frameNBThresh->SetSuffix(" "); ui->frameNBThresh->setRange(0, 99); ui->frameNBThresh->setSingleStep(1); ui->frameNBThresh->setPageStep(1); ui->frameNBThresh->setTickInterval(10); ui->frameNBWidth->SetName("Width"); ui->frameNBWidth->SetSuffix(" uSec"); ui->frameNBWidth->setRange(10, 300); ui->frameNBWidth->setSingleStep(10); ui->frameNBWidth->setPageStep(10); ui->frameNBWidth->setTickInterval(30); } CNoiseProcDlg::~CNoiseProcDlg() { delete ui; } //Fill in initial data void CNoiseProcDlg::InitDlg(tNoiseProcdInfo* pNoiseProcSettings) { if(NULL == pNoiseProcSettings) return; m_pNoiseProcSettings = pNoiseProcSettings; if(m_pNoiseProcSettings->NBOn) ui->checkBoxNBOn->setChecked(true); else ui->checkBoxNBOn->setChecked(false); ui->frameNBThresh->SetValue(m_pNoiseProcSettings->NBThreshold); ui->frameNBWidth->SetValue(m_pNoiseProcSettings->NBWidth); } void CNoiseProcDlg::OnNBOn(bool On) { if(m_pNoiseProcSettings) { m_pNoiseProcSettings->NBOn = On; ((MainWindow*)this->parent())->SetupNoiseProc(); } } void CNoiseProcDlg::OnNBThresh(int val) { if(m_pNoiseProcSettings) { m_pNoiseProcSettings->NBThreshold = val; ((MainWindow*)this->parent())->SetupNoiseProc(); } } void CNoiseProcDlg::OnNBWidth(int val) { if(m_pNoiseProcSettings) { m_pNoiseProcSettings->NBWidth = val; ((MainWindow*)this->parent())->SetupNoiseProc(); } } cutesdr-1.0.5/gui/rdsdecode.h0000644000175000017500000000213111711303214016005 0ustar bottomsbottoms////////////////////////////////////////////////////////////////////// // rdsdecode.h: decodes RDS messages into text. // // History: // 2011-08-26 Initial creation MSW // 2011-08-26 Initial release ///////////////////////////////////////////////////////////////////// #ifndef RDSDECODE_H #define RDSDECODE_H #include #include "dsp/rbdsconstants.h" #define MAX_TEXT 128 class CRdsDecode { public: CRdsDecode(); void DecodeRdsGroup(tRDS_GROUPS* pGrp); int GetRdsString( char* Str); int GetRdsCallString( char* Str); void DecodeReset(int USFm); private: void Decode32RadioText(); void Decode64RadioText(); void DecodePTYText(); void DecodePSText(); void DecodePIcode(); tRDS_GROUPS m_Group; int m_USFm; int m_GrpType; int m_PrgType; int m_LastPrgType; int m_LastABBit; int m_LastPICode; int m_MaxTextPos; char m_PTYText[MAX_TEXT]; char m_RDSText[MAX_TEXT]; char m_RTextOut[MAX_TEXT]; char m_RText[MAX_TEXT]; char m_PSTextOut[MAX_TEXT]; char m_PSText[MAX_TEXT]; char m_RBDSCallSign[MAX_TEXT]; }; #endif // RDSDECODE_H cutesdr-1.0.5/gui/sdrdiscoverdlg.h0000644000175000017500000001233711716016005017104 0ustar bottomsbottoms////////////////////////////////////////////////////////////////////// // sdrdiscoverdlg.h: interface for the CSdrDiscoverDlg class. // // History: // 2010-09-15 Initial creation MSW // 2011-03-27 Initial release ///////////////////////////////////////////////////////////////////// #ifndef SDRDISCOVERDLG_H #define SDRDISCOVERDLG_H #include "ui_sdrdiscoverdlg.h" #include #include #include #include #include #define MAX_DEVICES 32 typedef struct __attribute__ ((__packed__)) _DISCOVER_MSG_COMMON { //56 fixed common byte fields unsigned char length[2]; //length of total message in bytes (little endian byte order) unsigned char key[2]; //fixed key key[0]==0x5A key[1]==0xA5 unsigned char op; //0==Request(to device) 1==Response(from device) 2 ==Set(to device) char name[16]; //Device name string null terminated char sn[16]; //Serial number string null terminated unsigned char ipaddr[16]; //device IP address (little endian byte order) unsigned char port[2]; //device Port number (little endian byte order) unsigned char customfield; //Specifies a custom data field for a particular device }tDiscover_COMMONMSG; typedef struct __attribute__ ((__packed__)) _DISCOVER_MSG_NETSDR { //56 fixed common byte fields unsigned char length[2]; //length of total message in bytes (little endian byte order) unsigned char key[2]; //fixed key key[0]==0x5A key[1]==0xA5 unsigned char op; //0==Request(to device) 1==Response(from device) 2 ==Set(to device) char name[16]; //Device name string null terminated char sn[16]; //Serial number string null terminated unsigned char ipaddr[16]; //device IP address (little endian byte order) unsigned char port[2]; //device Port number (little endian byte order) unsigned char customfield; //Specifies a custom data field for a particular device //start of optional variable custom byte fields unsigned char macaddr[6]; //HW mac address (little endian byte order) (read only) unsigned char hwver[2]; //Hardware version*100 (little endian byte order) (read only) unsigned char fwver[2]; //Firmware version*100 (little endian byte order)(read only) unsigned char btver[2]; //Boot version*100 (little endian byte order) (read only) unsigned char fpgaid; //FPGA ID (read only) unsigned char fpgarev; //FPGA revision (read only) unsigned char opts; //Options (read only) unsigned char mode; //0 == Use DHCP 1==manual 2==manual Alternate data address unsigned char subnet[4]; //IP subnet mask (little endian byte order) unsigned char gwaddr[4]; //gateway address (little endian byte order) unsigned char dataipaddr[4];// Alternate data IP address for UDP data (little endian byte order) unsigned char dataport[2]; // Alternate data Port address for UDP (little endian byte order) unsigned char fpga; //0 == default cfg 1==custom1 2==custom2 unsigned char status; //bit 0 == TCP connected Bit 1 == running Bit 2-7 not defined unsigned char future[15]; //future use }tDiscover_NETSDR; typedef struct __attribute__ ((__packed__)) _DISCOVER_MSG_SDRXX { //56 fixed common byte fields unsigned char length[2]; //length of total message in bytes (little endian byte order) unsigned char key[2]; //fixed key key[0]==0x5A key[1]==0xA5 unsigned char op; //0==Request(to device) 1==Response(from device) 2 ==Set(to device) char name[16]; //Device name string null terminated char sn[16]; //Serial number string null terminated unsigned char ipaddr[16]; //device IP address (little endian byte order) unsigned char port[2]; //device Port number (little endian byte order) unsigned char customfield; //Specifies a custom data field for a particular device //start of optional variable custom byte fields unsigned char fwver[2]; //Firmware version*100 (little endian byte order)(read only) unsigned char btver[2]; //Boot version*100 (little endian byte order) (read only) unsigned char subnet[4]; //IP subnet mask (little endian byte order) unsigned char gwaddr[4]; //gateway address (little endian byte order) char connection[32]; //interface connection string null terminated(ex: COM3, DEVTTY5, etc) unsigned char status; //bit 0 == TCP connected Bit 1 == running Bit 2-7 not defined unsigned char future[15]; //future use }tDiscover_SDRxx; #define STATUS_BIT_CONNECTED (1) #define STATUS_BIT_RUNNING (2) namespace Ui { class CSdrDiscoverDlg; } class CSdrDiscoverDlg : public QDialog { Q_OBJECT public: explicit CSdrDiscoverDlg(QWidget *parent); ~CSdrDiscoverDlg(); void InitDlg(); QString m_NameFilter; quint32 m_IPAdr; unsigned short m_Port; QString m_Name; public slots: void accept(); private slots: void OnFind(); void OnItemDoubleClick( QListWidgetItem * item ); void ReadUDPMessages(); void SendDiscoverRequest(); void CloseUdp(); private: Ui::CSdrDiscoverDlg *ui; void ParseMsg(int index); QTimer m_Timer; QTimer m_CloseTimer; bool m_UdpOpen; QUdpSocket* m_pUdpDiscoverSocket; tDiscover_COMMONMSG m_DiscovermsgCommon[MAX_DEVICES]; tDiscover_NETSDR m_DiscovermsgNetSDR[MAX_DEVICES]; tDiscover_SDRxx m_DiscovermsgSDRxx[MAX_DEVICES]; }; #endif // SDRDISCOVERDLG_H cutesdr-1.0.5/gui/demodsetupdlg.h0000644000175000017500000000201011711303214016705 0ustar bottomsbottoms////////////////////////////////////////////////////////////////////// // demodsetupdlg.h: interface for the CDemodSetupDlg class. // // History: // 2010-09-15 Initial creation MSW // 2011-03-27 Initial release ///////////////////////////////////////////////////////////////////// #ifndef DEMODSETUPDLG_H #define DEMODSETUPDLG_H #include #include "dsp/demodulator.h" #include "gui/sliderctrl.h" namespace Ui { class CDemodSetupDlg; } class CDemodSetupDlg : public QDialog { Q_OBJECT public: explicit CDemodSetupDlg(QWidget *parent); ~CDemodSetupDlg(); void InitDlg(); int m_DemodMode; private slots: void ModeChanged(); void OnOffsetChanged(int Offset); void OnSquelchChanged(int SquelchValue); void OnAgcSlope(int); void OnAgcThresh(int); void OnAgcDecay(int); void OnAgcOn(bool); void OnHangOn(bool); void OnUSFm(bool); private: void UpdateDemodInfo(); Ui::CDemodSetupDlg *ui; tDemodInfo* m_pDemodInfo; }; #endif // DEMODSETUPDLG_H cutesdr-1.0.5/gui/demodsetupdlg.ui0000644000175000017500000003150711716016005017114 0ustar bottomsbottoms CDemodSetupDlg 0 0 351 309 Demodulation Setup 130 280 81 23 Qt::Horizontal QDialogButtonBox::Close 10 10 101 211 Mode 20 21 56 181 AM SAM FM WFM USB LSB CWU CWL 110 250 151 22 0 0 120 0 Hz CW Offset -1500 1500 100 120 10 221 201 AGC Qt::AlignCenter 10 30 81 17 AGC On 10 50 191 41 0 0 QFrame::StyledPanel QFrame::Sunken 2 0 10 100 191 41 QFrame::StyledPanel QFrame::Plain 2 10 150 191 41 QFrame::StyledPanel QFrame::Plain 2 90 30 131 17 Use Hang Timer instead of Decay Use Hang Timer 150 220 160 21 50 Qt::Horizontal QSlider::TicksBelow 10 50 220 91 20 FM Squelch Control FM Squelch Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter CSliderCtrl QFrame
gui/sliderctrl.h
1 valueChanged(int)
buttonBox accepted() CDemodSetupDlg accept() 170 280 157 240 buttonBox rejected() CDemodSetupDlg reject() 170 280 286 240 AMradioButton clicked() CDemodSetupDlg ModeChanged() 51 40 148 24 SAMradioButton clicked() CDemodSetupDlg ModeChanged() 66 63 145 63 FMradioButton clicked() CDemodSetupDlg ModeChanged() 55 88 127 97 USBradioButton clicked() CDemodSetupDlg ModeChanged() 54 140 241 81 LSBradioButton clicked() CDemodSetupDlg ModeChanged() 45 163 178 129 CWUradioButton clicked() CDemodSetupDlg ModeChanged() 65 186 124 143 CWLradioButton clicked() CDemodSetupDlg ModeChanged() 36 209 35 217 spinBoxOffset valueChanged(int) CDemodSetupDlg OnOffsetChanged(int) 171 250 104 222 checkBoxAgcOn toggled(bool) CDemodSetupDlg OnAgcOn(bool) 194 39 165 5 checkBoxHang toggled(bool) CDemodSetupDlg OnHangOn(bool) 284 41 285 3 horizontalSliderSquelch valueChanged(int) CDemodSetupDlg OnSquelchChanged(int) 255 225 314 280 WFMradioButton clicked() CDemodSetupDlg ModeChanged() 57 109 175 154 ModeChanged() OnOffsetChanged(int) OnAgcOn(bool) OnHangOn(bool) OnSquelchChanged(int) OnUSFm(bool)
cutesdr-1.0.5/gui/sliderctrl.cpp0000644000175000017500000000537711546075374016615 0ustar bottomsbottoms///////////////////////////////////////////////////////////////////// // sliderctrl.cpp: implementation of the CSliderCtrl class. // // This class implements a slider control widget // // History: // 2010-09-15 Initial creation MSW // 2011-03-27 Initial release ///////////////////////////////////////////////////////////////////// //========================================================================================== // + + + This Software is released under the "Simplified BSD License" + + + //Copyright 2010 Moe Wheatley. All rights reserved. // //Redistribution and use in source and binary forms, with or without modification, are //permitted provided that the following conditions are met: // // 1. Redistributions of source code must retain the above copyright notice, this list of // conditions and the following disclaimer. // // 2. Redistributions in binary form must reproduce the above copyright notice, this list // of conditions and the following disclaimer in the documentation and/or other materials // provided with the distribution. // //THIS SOFTWARE IS PROVIDED BY Moe Wheatley ``AS IS'' AND ANY EXPRESS OR IMPLIED //WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND //FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL Moe Wheatley OR //CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR //CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR //SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON //ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING //NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF //ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // //The views and conclusions contained in the software and documentation are those of the //authors and should not be interpreted as representing official policies, either expressed //or implied, of Moe Wheatley. //========================================================================================== #include "gui/sliderctrl.h" #include CSliderCtrl::CSliderCtrl(QWidget *parent) : QFrame(parent) { ui.setupUi(this); ui.labelName->setText(""); ui.labelVal->setText(""); m_Prefix = ""; m_Suffix = ""; ui.horizontalSlider->setValue(0); } CSliderCtrl::~CSliderCtrl() { } void CSliderCtrl::valueChanged(int val) { int newval = val; int delta = ui.horizontalSlider->singleStep(); //keep value on singleStep increments val = val - val%delta; if(newval != val) ui.horizontalSlider->setValue(val); emit sliderValChanged(val); DisplayValue(val); } void CSliderCtrl::DisplayValue(int val) { QString str; ui.labelVal->setText(m_Prefix + str.setNum(val) + m_Suffix); } cutesdr-1.0.5/gui/editnetdlg.h0000644000175000017500000000153111546075374016222 0ustar bottomsbottoms////////////////////////////////////////////////////////////////////// // editnetdlg.h: interface for the CEditNetDlg class. // // History: // 2010-09-15 Initial creation MSW // 2011-03-27 Initial release ///////////////////////////////////////////////////////////////////// #ifndef EDITNETDLG_H #define EDITNETDLG_H #include #include "gui/mainwindow.h" #include "ui_editnetdlg.h" class CEditNetDlg : public QDialog { Q_OBJECT public: explicit CEditNetDlg(QWidget *parent = 0); ~CEditNetDlg(); void InitDlg(); bool eventFilter(QObject* o, QEvent* e); QHostAddress m_IPAdr; quint32 m_Port; QString m_ActiveDevice; bool m_DirtyFlag; signals: public slots: void accept(); void FindSdrs(); private: Ui::EditNetDlg ui; QIntValidator* m_pPortAddressValidator; }; #endif // EDITNETDLG_H cutesdr-1.0.5/gui/editnetdlg.cpp0000644000175000017500000001073311546075374016561 0ustar bottomsbottoms///////////////////////////////////////////////////////////////////// // ceditnetdlg.cpp: implementation of the CEditNetDlg class. // // This class implements a dialog to setup the network parameters // and discover all network sdr devices // // History: // 2010-09-15 Initial creation MSW // 2011-03-27 Initial release ///////////////////////////////////////////////////////////////////// //========================================================================================== // + + + This Software is released under the "Simplified BSD License" + + + //Copyright 2010 Moe Wheatley. All rights reserved. // //Redistribution and use in source and binary forms, with or without modification, are //permitted provided that the following conditions are met: // // 1. Redistributions of source code must retain the above copyright notice, this list of // conditions and the following disclaimer. // // 2. Redistributions in binary form must reproduce the above copyright notice, this list // of conditions and the following disclaimer in the documentation and/or other materials // provided with the distribution. // //THIS SOFTWARE IS PROVIDED BY Moe Wheatley ``AS IS'' AND ANY EXPRESS OR IMPLIED //WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND //FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL Moe Wheatley OR //CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR //CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR //SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON //ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING //NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF //ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // //The views and conclusions contained in the software and documentation are those of the //authors and should not be interpreted as representing official policies, either expressed //or implied, of Moe Wheatley. //========================================================================================== #include #include #include #include "gui/editnetdlg.h" #include "gui/sdrdiscoverdlg.h" #include "ui_editnetdlg.h" #include CEditNetDlg::CEditNetDlg(QWidget *parent) : QDialog(parent) { ui.setupUi(this); qApp->installEventFilter(this); m_pPortAddressValidator = NULL; m_pPortAddressValidator = new QIntValidator(0, 65535, this); // ui.lineEdit_TCPPort->setValidator(m_pPortAddressValidator); } CEditNetDlg::~CEditNetDlg() { if(m_pPortAddressValidator) delete m_pPortAddressValidator; } //intercept return key and map it to the tab key so the dialog doesnt exit bool CEditNetDlg::eventFilter(QObject* o, QEvent* e) { if (e->type() == QEvent::KeyPress || e->type() == QEvent::KeyRelease) { QKeyEvent* ke = (QKeyEvent*)e; if (ke->key() == Qt::Key_Return || ke->key() == Qt::Key_Enter) { QKeyEvent rep(ke->type(), Qt::Key_Tab, Qt::NoModifier, QString::null, ke->isAutoRepeat(), ke->count()); *ke = rep; } } return QDialog::eventFilter(o, e); } //Fill in initial data void CEditNetDlg::InitDlg() { ui.IPEditwidget_IP->SetIP(m_IPAdr.toIPv4Address()); ui.lineEdit_TCPPort->setText(QString().number( m_Port )); ui.label_SDR->setText(m_ActiveDevice); m_DirtyFlag = FALSE; } void CEditNetDlg::FindSdrs() { CSdrDiscoverDlg dlg(this); quint32 ip; ui.IPEditwidget_IP->GetIP(ip); m_IPAdr.setAddress(ip); m_Port = ui.lineEdit_TCPPort->text().toUInt(); dlg.m_IPAdr = m_IPAdr.toIPv4Address(); dlg.m_Port = m_Port; // dlg.m_NameFilter = "SDR-14"; //if want to limit search to a specific device dlg.InitDlg(); if( dlg.exec() ) { //qDebug()<SetIP(m_IPAdr.toIPv4Address()); m_Port = dlg.m_Port; ui.lineEdit_TCPPort->setText(QString().number( m_Port )); m_ActiveDevice = dlg.m_Name; ui.label_SDR->setText(m_ActiveDevice); } } } //Called when OK button pressed so get all the dialog data void CEditNetDlg::accept() { //OK was pressed so get all data from edit controls quint32 ip; m_DirtyFlag |= ui.IPEditwidget_IP->GetIP(ip); m_IPAdr.setAddress(ip); if(ui.lineEdit_TCPPort->isModified()) { m_DirtyFlag = TRUE; m_Port = ui.lineEdit_TCPPort->text().toUInt(); } QDialog::accept(); } cutesdr-1.0.5/gui/plotter.cpp0000644000175000017500000005516111716016005016114 0ustar bottomsbottoms////////////////////////////////////////////////////////////////////// // plotter.cpp: implementation of the CPlotter class. // // This class creates and draws a combination spectral view using // a 2D and waterfall display and manages mouse events within the plot area // // History: // 2010-09-15 Initial creation MSW // 2011-03-27 Initial release // 2012-02-11 Fixed compiler warning ////////////////////////////////////////////////////////////////////// //========================================================================================== // + + + This Software is released under the "Simplified BSD License" + + + //Copyright 2010 Moe Wheatley. All rights reserved. // //Redistribution and use in source and binary forms, with or without modification, are //permitted provided that the following conditions are met: // // 1. Redistributions of source code must retain the above copyright notice, this list of // conditions and the following disclaimer. // // 2. Redistributions in binary form must reproduce the above copyright notice, this list // of conditions and the following disclaimer in the documentation and/or other materials // provided with the distribution. // //THIS SOFTWARE IS PROVIDED BY Moe Wheatley ``AS IS'' AND ANY EXPRESS OR IMPLIED //WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND //FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL Moe Wheatley OR //CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR //CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR //SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON //ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING //NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF //ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // //The views and conclusions contained in the software and documentation are those of the //authors and should not be interpreted as representing official policies, either expressed //or implied, of Moe Wheatley. //========================================================================================== #include "gui/plotter.h" #include #include #include "interface/perform.h" ////////////////////////////////////////////////////////////////////// // Local defines ////////////////////////////////////////////////////////////////////// #define MAX_SCREENSIZE 4096 #define CUR_CUT_DELTA 10 //cursor capture delta in pixels #define OVERLOAD_DISPLAY_LIMIT 10 ////////////////////////////////////////////////////////////////////// // Construction/Destruction ////////////////////////////////////////////////////////////////////// CPlotter::CPlotter(QWidget *parent) : QFrame(parent) { m_pSdrInterface = NULL; setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); setFocusPolicy(Qt::StrongFocus); setAttribute(Qt::WA_PaintOnScreen,false); setAutoFillBackground(false); setAttribute(Qt::WA_OpaquePaintEvent, false); setAttribute(Qt::WA_NoSystemBackground, true); setMouseTracking ( true ); //create a default waterfall color scheme // *** Need to read from file *** for( int i=0; i<256; i++) { if( (i<43) ) m_ColorTbl[i].setRgb( 0,0, 255*(i)/43); if( (i>=43) && (i<87) ) m_ColorTbl[i].setRgb( 0, 255*(i-43)/43, 255 ); if( (i>=87) && (i<120) ) m_ColorTbl[i].setRgb( 0,255, 255-(255*(i-87)/32)); if( (i>=120) && (i<154) ) m_ColorTbl[i].setRgb( (255*(i-120)/33), 255, 0); if( (i>=154) && (i<217) ) m_ColorTbl[i].setRgb( 255, 255 - (255*(i-154)/62), 0); if( (i>=217) ) m_ColorTbl[i].setRgb( 255, 0, 128*(i-217)/38); } m_CenterFreq = 680000; m_DemodCenterFreq = 610000; m_DemodHiCutFreq = 5000; m_DemodLowCutFreq = -5000; m_FLowCmin = -10000; m_FLowCmax = -100; m_FHiCmin = 100; m_FHiCmax = 10000; m_symetric = true; m_ClickResolution = 100; m_FilterClickResolution = 100; m_CursorCaptureDelta = CUR_CUT_DELTA; m_Span = 50000; m_MaxdB = 0; m_MindB = -130; m_dBStepSize = 10; m_FreqUnits = 1000; m_CursorCaptured = NONE; m_Running = false; m_ADOverloadOneShotCounter = 0; m_ADOverLoad = false; m_2DPixmap = QPixmap(0,0); m_OverlayPixmap = QPixmap(0,0); m_WaterfallPixmap = QPixmap(0,0); m_Size = QSize(0,0); m_GrabPosition = 0; m_Percent2DScreen = 50; //percent of screen used for 2D display m_RdsCall[0] = 0; m_RdsText[0] = 0; } CPlotter::~CPlotter() { } ////////////////////////////////////////////////////////////////////// // Sizing interface ////////////////////////////////////////////////////////////////////// QSize CPlotter::minimumSizeHint() const { return QSize(50, 50); } QSize CPlotter::sizeHint() const { return QSize(180, 180); } ////////////////////////////////////////////////////////////////////// // Called when mouse moves and does different things depending //on the state of the mouse buttons for dragging the demod bar or // filter edges. ////////////////////////////////////////////////////////////////////// void CPlotter::mouseMoveEvent(QMouseEvent* event) { QPoint pt = event->pos(); if( m_OverlayPixmap.rect().contains(pt) ) { //is in Overlay bitmap region if( event->buttons()==Qt::NoButton) { //if no mouse button monitor grab regions and change cursor icon if( IsPointCloseTo( pt.x(),(m_DemodHiCutFreqX+m_DemodLowCutFreqX)/2, (m_DemodHiCutFreqX-m_DemodLowCutFreqX/*-m_CursorCaptureDelta*/)/2) ) { //in move demod box center frequency region if(CENTER!=m_CursorCaptured) setCursor(QCursor(Qt::CrossCursor)); m_CursorCaptured = CENTER; } else if( IsPointCloseTo( pt.x(),m_DemodHiCutFreqX, m_CursorCaptureDelta) ) { //in move demod hicut region if(RIGHT!=m_CursorCaptured) setCursor(QCursor(Qt::SizeFDiagCursor)); m_CursorCaptured = RIGHT; } else if( IsPointCloseTo( pt.x(),m_DemodLowCutFreqX, m_CursorCaptureDelta) ) { //in move demod lowcut region if(LEFT!=m_CursorCaptured) setCursor(QCursor(Qt::SizeBDiagCursor)); m_CursorCaptured = LEFT; } else { //if not near any grab boundaries if(NONE!=m_CursorCaptured) { setCursor(QCursor(Qt::ArrowCursor)); m_CursorCaptured = NONE; } } m_GrabPosition = 0; } } else { //not in Overlay region if( event->buttons()==Qt::NoButton) { if(NONE!=m_CursorCaptured) setCursor(QCursor(Qt::ArrowCursor)); m_CursorCaptured = NONE; m_GrabPosition = 0; } } //process mouse moves while in cursor capture modes if(LEFT==m_CursorCaptured) { //moving in demod lowcut region if(event->buttons()&Qt::RightButton) { //moving in demod lowcut region with right button held if(m_GrabPosition!=0) { m_DemodLowCutFreq = FreqfromX(pt.x()-m_GrabPosition ) - m_DemodCenterFreq; m_DemodLowCutFreq = RoundFreq(m_DemodLowCutFreq, m_FilterClickResolution); DrawOverlay(); if(m_symetric) { m_DemodHiCutFreq = -m_DemodLowCutFreq; emit NewHighCutFreq(m_DemodHiCutFreq); } emit NewLowCutFreq(m_DemodLowCutFreq); } else { //save initial grab postion from m_DemodFreqX m_GrabPosition = pt.x()-m_DemodLowCutFreqX; } } else if(event->buttons() & ~Qt::NoButton) { setCursor(QCursor(Qt::ArrowCursor)); m_CursorCaptured = NONE; } } else if(RIGHT==m_CursorCaptured) { //moving in demod highcut region if(event->buttons()&Qt::RightButton) { //moving in demod highcut region with right button held if(m_GrabPosition!=0) { m_DemodHiCutFreq = FreqfromX( pt.x()-m_GrabPosition ) - m_DemodCenterFreq; m_DemodHiCutFreq = RoundFreq(m_DemodHiCutFreq, m_FilterClickResolution); DrawOverlay(); if(m_symetric) { m_DemodLowCutFreq = -m_DemodHiCutFreq; emit NewLowCutFreq(m_DemodLowCutFreq); } emit NewHighCutFreq(m_DemodHiCutFreq); } else { //save initial grab postion from m_DemodFreqX m_GrabPosition = pt.x()-m_DemodHiCutFreqX; } } else if(event->buttons() & ~Qt::NoButton) { setCursor(QCursor(Qt::ArrowCursor)); m_CursorCaptured = NONE; } } else if(CENTER==m_CursorCaptured) { //moving inbetween demod lowcut and highcut region if(event->buttons()&Qt::LeftButton) {//moving inbetween demod lowcut and highcut region with left button held if(m_GrabPosition!=0) { m_DemodCenterFreq = RoundFreq(FreqfromX( pt.x()-m_GrabPosition ),m_ClickResolution ); emit NewDemodFreq(m_DemodCenterFreq); } else { //save initial grab postion from m_DemodFreqX m_GrabPosition = pt.x()-m_DemodFreqX; } } else if(event->buttons() & ~Qt::NoButton) { setCursor(QCursor(Qt::ArrowCursor)); m_CursorCaptured = NONE; } } else //if cursor not captured { m_GrabPosition = 0; } if( !this->rect().contains(pt) ) { if(NONE != m_CursorCaptured) setCursor(QCursor(Qt::ArrowCursor)); m_CursorCaptured = NONE; } } ////////////////////////////////////////////////////////////////////// // Called when a mouse button is pressed ////////////////////////////////////////////////////////////////////// void CPlotter::mousePressEvent(QMouseEvent * event) { QPoint pt = event->pos(); if(event->buttons()==Qt::LeftButton) { if( IsPointCloseTo( pt.x(),(m_DemodHiCutFreqX+m_DemodLowCutFreqX)/2, (m_DemodHiCutFreqX-m_DemodLowCutFreqX/*-m_CursorCaptureDelta*/)/2) ) { //in move demod box center frequency region if(CENTER!=m_CursorCaptured) setCursor(QCursor(Qt::CrossCursor)); m_CursorCaptured = CENTER; m_GrabPosition = pt.x()-m_DemodFreqX; } if( CENTER!=m_CursorCaptured) { //if cursor not captured set demod frequency and start demod box capture m_DemodCenterFreq = RoundFreq(FreqfromX(pt.x()),m_ClickResolution ); emit NewDemodFreq(m_DemodCenterFreq); //save initial grab postion from m_DemodFreqX setCursor(QCursor(Qt::CrossCursor)); m_CursorCaptured = CENTER; m_GrabPosition = 1; } } else if(event->buttons()==Qt::MiddleButton) { if( NONE==m_CursorCaptured) { //if cursor not captured set center freq m_CenterFreq = RoundFreq(FreqfromX(pt.x()),m_ClickResolution ); m_DemodCenterFreq = m_CenterFreq; emit NewCenterFreq(m_CenterFreq); } } } ////////////////////////////////////////////////////////////////////// // Called when a mouse button is released ////////////////////////////////////////////////////////////////////// void CPlotter::mouseReleaseEvent(QMouseEvent * event) { QPoint pt = event->pos(); if( !m_OverlayPixmap.rect().contains(pt) ) { //not in Overlay region if(NONE!=m_CursorCaptured) setCursor(QCursor(Qt::ArrowCursor)); m_CursorCaptured = NONE; m_GrabPosition = 0; } } ////////////////////////////////////////////////////////////////////// // Called when a mouse wheel is turned ////////////////////////////////////////////////////////////////////// void CPlotter::wheelEvent( QWheelEvent * event ) { Q_UNUSED(event); int numDegrees = event->delta() / 8; int numSteps = numDegrees / 15; if(event->buttons()==Qt::RightButton) { //right button held while wheel is spun if(RIGHT==m_CursorCaptured) { //change demod high cut m_DemodHiCutFreq += (numSteps*m_FilterClickResolution); m_DemodHiCutFreq = RoundFreq(m_DemodHiCutFreq, m_FilterClickResolution); DrawOverlay(); if(m_symetric) { m_DemodLowCutFreq = -m_DemodHiCutFreq; emit NewLowCutFreq(m_DemodLowCutFreq); } emit NewHighCutFreq(m_DemodHiCutFreq); } else if(LEFT==m_CursorCaptured) { //change demod low cut m_DemodLowCutFreq += (numSteps*m_FilterClickResolution); m_DemodLowCutFreq = RoundFreq(m_DemodLowCutFreq, m_FilterClickResolution); DrawOverlay(); if(m_symetric) { m_DemodHiCutFreq = -m_DemodLowCutFreq; emit NewHighCutFreq(m_DemodHiCutFreq); } emit NewLowCutFreq(m_DemodLowCutFreq); } } else { //inc/dec demod frequency if right button NOT pressed m_DemodCenterFreq += (numSteps*m_ClickResolution); m_DemodCenterFreq = RoundFreq(m_DemodCenterFreq, m_ClickResolution ); emit NewDemodFreq(m_DemodCenterFreq); } } ////////////////////////////////////////////////////////////////////// // Called when screen size changes so must recalculate bitmaps ////////////////////////////////////////////////////////////////////// void CPlotter::resizeEvent(QResizeEvent* ) { if(!size().isValid()) return; if( m_Size != size() ) { //if changed, resize pixmaps to new screensize m_Size = size(); m_OverlayPixmap = QPixmap(m_Size.width(), m_Percent2DScreen*m_Size.height()/100); m_OverlayPixmap.fill(Qt::black); m_2DPixmap = QPixmap(m_Size.width(), m_Percent2DScreen*m_Size.height()/100); m_2DPixmap.fill(Qt::black); m_WaterfallPixmap = QPixmap(m_Size.width(), (100-m_Percent2DScreen)*m_Size.height()/100); } m_WaterfallPixmap.fill(Qt::black); DrawOverlay(); } ////////////////////////////////////////////////////////////////////// // Called by QT when screen needs to be redrawn ////////////////////////////////////////////////////////////////////// void CPlotter::paintEvent(QPaintEvent *) { QPainter painter(this); painter.drawPixmap(0,0,m_2DPixmap); painter.drawPixmap(0, m_Percent2DScreen*m_Size.height()/100,m_WaterfallPixmap); //tell interface that its ok to signal a new line of fft data m_pSdrInterface->ScreenUpdateDone(); return; } ////////////////////////////////////////////////////////////////////// // Called to update spectrum data for displaying on the screen ////////////////////////////////////////////////////////////////////// void CPlotter::draw() { int i; int w; int h; qint32 fftbuf[MAX_SCREENSIZE]; QPoint LineBuf[MAX_SCREENSIZE]; if(!m_Running) return; //StartPerformance(); //get/draw the waterfall w = m_WaterfallPixmap.width(); h = m_WaterfallPixmap.height(); //move current data down one line(must do before attaching a QPainter object) m_WaterfallPixmap.scroll(0,1,0,0, w, h); QPainter painter1(&m_WaterfallPixmap); //get scaled FFT data bool fftoverload = m_pSdrInterface->GetScreenIntegerFFTData( 255, w, m_MaxdB, m_MindB, -m_Span/2, m_Span/2, fftbuf ); //draw new line of fft data at top of waterfall bitmap for(i=0; iGetScreenIntegerFFTData( h, w, m_MaxdB, m_MindB, -m_Span/2, m_Span/2, fftbuf ); //draw the 2D spectrum if(m_ADOverLoad || fftoverload) { painter2.setPen( Qt::red ); if(m_ADOverloadOneShotCounter++ > OVERLOAD_DISPLAY_LIMIT) { m_ADOverloadOneShotCounter = 0; m_ADOverLoad = false; } } else painter2.setPen( Qt::green ); for(i=0; iGetStereoLock(&LockState); if( LockState ) painter.fillRect(m_DemodLowCutFreqX, 0,dw, h, Qt::green); else painter.fillRect(m_DemodLowCutFreqX, 0,dw, h, Qt::gray); painter.setPen(QPen(Qt::magenta, 2,Qt::DotLine)); painter.drawLine(m_DemodLowCutFreqX, 0, m_DemodLowCutFreqX, h); painter.drawLine(m_DemodHiCutFreqX, 0, m_DemodHiCutFreqX, h); painter.setPen(QPen(Qt::blue, 1,Qt::DashLine)); painter.drawLine(m_DemodFreqX, 0, m_DemodFreqX, h); painter.setOpacity(1); //create Font to use for scales QFont Font("Arial"); Font.setPointSize(12); QFontMetrics metrics(Font); y = h/VERT_DIVS; if(ydp; j--) { if(m_HDivText[i][j] != '0') break; } if( (j-dp) > max) max = j-dp; } //truncate all strings to maximum fractional length StartFreq = m_CenterFreq - m_Span/2; for( i=0; i<=HORZ_DIVS; i++) { freq = (float)StartFreq/(float)m_FreqUnits; m_HDivText[i].setNum(freq,'f', max); StartFreq += FreqPerDiv; } } ////////////////////////////////////////////////////////////////////// // Helper functions to convert to/from screen coordinates to frequency ////////////////////////////////////////////////////////////////////// int CPlotter::XfromFreq(qint64 freq) { float w = m_OverlayPixmap.width(); float StartFreq = (float)m_CenterFreq - (float)m_Span/2.; int x = (int) w * ((float)freq - StartFreq)/(float)m_Span; if(x<0 ) return 0; if(x>(int)w) return m_OverlayPixmap.width(); return x; } qint64 CPlotter::FreqfromX(int x) { float w = m_OverlayPixmap.width(); float StartFreq = (float)m_CenterFreq - (float)m_Span/2.; qint64 f = (int)(StartFreq + (float)m_Span * (float)x/(float)w ); return f; } ////////////////////////////////////////////////////////////////////// // Helper function to round frequency to click resolution value ////////////////////////////////////////////////////////////////////// qint64 CPlotter::RoundFreq(qint64 freq, int resolution) { qint64 delta = resolution; qint64 delta_2 = delta/2; if(freq>=0) return ( freq - (freq+delta_2)%delta + delta_2); else return ( freq - (freq+delta_2)%delta - delta_2); } ////////////////////////////////////////////////////////////////////// // Helper function clamps demod freqeuency limits of // m_DemodCenterFreq ////////////////////////////////////////////////////////////////////// void CPlotter::ClampDemodParameters() { if(m_DemodLowCutFreq < m_FLowCmin) m_DemodLowCutFreq = m_FLowCmin; if(m_DemodLowCutFreq > m_FLowCmax) m_DemodLowCutFreq = m_FLowCmax; if(m_DemodHiCutFreq < m_FHiCmin) m_DemodHiCutFreq = m_FHiCmin; if(m_DemodHiCutFreq > m_FHiCmax) m_DemodHiCutFreq = m_FHiCmax; } void CPlotter::SetDemodRanges(int FLowCmin, int FLowCmax, int FHiCmin, int FHiCmax, bool symetric) { m_FLowCmin=FLowCmin; m_FLowCmax=FLowCmax; m_FHiCmin=FHiCmin; m_FHiCmax=FHiCmax; m_symetric=symetric; ClampDemodParameters(); } cutesdr-1.0.5/gui/sliderctrl.ui0000644000175000017500000000603711546075374016442 0ustar bottomsbottoms sliderctrl 0 0 174 46 0 0 Frame QFrame::StyledPanel QFrame::Raised 10 10 101 26 50 Qt::Horizontal QSlider::TicksAbove 10 120 20 52 20 0 0 0 20 -100 dB Qt::AlignCenter 120 1 51 20 0 0 0 20 Name Qt::AlignCenter horizontalSlider valueChanged(int) sliderctrl valueChanged(int) 83 18 118 38 valueChanged(int) cutesdr-1.0.5/gui/sliderctrl.h0000644000175000017500000000243011546075374016245 0ustar bottomsbottoms////////////////////////////////////////////////////////////////////// // sliderctrl.h: interface for the CSliderCtrl class. // // History: // 2010-09-15 Initial creation MSW // 2011-03-27 Initial release ///////////////////////////////////////////////////////////////////// #ifndef SLIDERCTRL_H #define SLIDERCTRL_H #include #include "ui_sliderctrl.h" class CSliderCtrl : public QFrame { Q_OBJECT public: explicit CSliderCtrl(QWidget *parent = 0); ~CSliderCtrl(); //map slider functions to control void SetValue(int val){ui.horizontalSlider->setValue(val);} void setRange(int min, int max){ui.horizontalSlider->setRange(min,max);} void setTickInterval(int ti){ui.horizontalSlider->setTickInterval(ti);} void setPageStep(int ps){ui.horizontalSlider->setPageStep(ps);} void setSingleStep(int ss){ui.horizontalSlider->setSingleStep(ss);} void SetName(QString Name){ui.labelName->setText(Name);} void SetPrefix(QString Prefix){m_Prefix = Prefix;} void SetSuffix(QString Suffix){m_Suffix = Suffix;} signals: void sliderValChanged(int); public slots: private slots: void valueChanged(int); protected: private: void DisplayValue(int val); Ui::sliderctrl ui; QString m_Suffix; QString m_Prefix; }; #endif // SLIDERCTRL_H cutesdr-1.0.5/gui/plotter.h0000644000175000017500000000676411711303214015562 0ustar bottomsbottoms////////////////////////////////////////////////////////////////////// // plotter.h: interface for the CPlotter class. // // History: // 2010-09-15 Initial creation MSW // 2011-03-27 Initial release ///////////////////////////////////////////////////////////////////// #ifndef PLOTTER_H #define PLOTTER_H #include #include #include #include "interface/sdrinterface.h" #define VERT_DIVS 14 //specify grid screen divisions #define HORZ_DIVS 10 #define MAX_TXT 128 class CPlotter : public QFrame { Q_OBJECT public: explicit CPlotter(QWidget *parent = 0); ~CPlotter(); QSize minimumSizeHint() const; QSize sizeHint() const; void SetSdrInterface(CSdrInterface* ptr){m_pSdrInterface = ptr;} void draw(); //call to draw new fft data onto screen plot void SetRunningState(bool running){m_Running = running;} void SetClickResolution(int clickres){m_ClickResolution=clickres;} void SetFilterClickResolution(int clickres){m_FilterClickResolution=clickres;} void SetPercent2DScreen(int percent){m_Percent2DScreen=percent; m_Size = QSize(0,0);resizeEvent(NULL);} void SetCenterFreq(quint64 f){m_CenterFreq=f;} void SetDemodCenterFreq(quint64 f){m_DemodCenterFreq=f;} void SetHiLowCutFrequencies(int LowCut, int HiCut){m_DemodLowCutFreq = LowCut; m_DemodHiCutFreq = HiCut;} void SetDemodRanges(int FLowCmin, int FLowCmax, int FHiCmin, int FHiCmax, bool symetric); void SetSpanFreq(quint32 s){m_Span=(qint32)s;} void SetMaxdB(int max){m_MaxdB=max;} void SetADOverload(bool ADOverLoad){m_ADOverLoad = ADOverLoad;m_ADOverloadOneShotCounter=0;} void SetdBStepSize(int stepsz){m_dBStepSize=stepsz;} void UpdateOverlay(){DrawOverlay();} char m_RdsCall[MAX_TXT]; char m_RdsText[MAX_TXT]; signals: void NewCenterFreq(qint64 f); void NewDemodFreq(qint64 f); void NewLowCutFreq(int f); void NewHighCutFreq(int f); public slots: protected: //re-implemented widget event handlers void paintEvent(QPaintEvent *event); void resizeEvent(QResizeEvent* event); void mouseMoveEvent(QMouseEvent * event); void mousePressEvent(QMouseEvent * event); void mouseReleaseEvent(QMouseEvent * event); void wheelEvent( QWheelEvent * event ); private: enum eCapturetype { NONE, LEFT, CENTER, RIGHT }; void DrawOverlay(); void MakeFrequencyStrs(); int XfromFreq(qint64 freq); qint64 FreqfromX(int x); qint64 RoundFreq(qint64 freq, int resolution); bool IsPointCloseTo(int x, int xr, int delta){return ((x > (xr-delta) ) && ( x<(xr+delta)) );} void ClampDemodParameters(); eCapturetype m_CursorCaptured; QPixmap m_2DPixmap; QPixmap m_OverlayPixmap; QPixmap m_WaterfallPixmap; QColor m_ColorTbl[256]; QSize m_Size; QString m_Str; QString m_HDivText[HORZ_DIVS+1]; bool m_Running; bool m_ADOverLoad; qint64 m_CenterFreq; qint64 m_DemodCenterFreq; int m_DemodHiCutFreq; int m_DemodLowCutFreq; int m_DemodFreqX; //screen coordinate x position int m_DemodHiCutFreqX; //screen coordinate x position int m_DemodLowCutFreqX; //screen coordinate x position int m_CursorCaptureDelta; int m_GrabPosition; int m_Percent2DScreen; int m_ADOverloadOneShotCounter; int m_FLowCmin; int m_FLowCmax; int m_FHiCmin; int m_FHiCmax; bool m_symetric; qint32 m_Span; qint32 m_MaxdB; qint32 m_MindB; qint32 m_dBStepSize; qint32 m_FreqUnits; int m_ClickResolution; int m_FilterClickResolution; quint32 m_LastSampleRate; CSdrInterface* m_pSdrInterface; }; #endif // PLOTTER_H cutesdr-1.0.5/gui/mainwindow.h0000644000175000017500000000572311711303214016237 0ustar bottomsbottoms////////////////////////////////////////////////////////////////////// // mainwindow.h: interface for the MainWindow GUI class. // // History: // 2010-09-15 Initial creation MSW // 2011-03-27 Initial release ///////////////////////////////////////////////////////////////////// #ifndef MAINWINDOW_H #define MAINWINDOW_H #include #include #include "interface/sdrinterface.h" #include #include "gui/demodsetupdlg.h" #include "rdsdecode.h" namespace Ui { class MainWindow; } class MainWindow : public QMainWindow { Q_OBJECT public: explicit MainWindow(QWidget *parent = 0); ~MainWindow(); void SetupDemod(int index); void SetupNoiseProc(); tDemodInfo m_DemodSettings[NUM_DEMODS]; //not all fields are saved in Settings private slots: void closeEvent(QCloseEvent *event); void AlwaysOnTop(); void OnExit(); void OnAbout(); void OnDisplayDlg(); void OnSoundCardDlg(); void OnSdrDlg(); void OnNetworkDlg(); void OnDemodDlg(); void OnNoiseProcDlg(); void OnVolumeSlider(int value); void OnRun(); void OnSpanChanged(int spanKhz); void OnMaxdBChanged(int maxdb); void OnVertScaleChanged(int index); void OnTimer(); void OnStatus(int status); void OnNewInfoData(); void OnNewFftData(); void OnNewScreenDemodFreq(qint64 freq); void OnNewScreenCenterFreq(qint64 freq); void OnNewCenterFrequency(qint64 freq); //called when center frequency has changed void OnNewDemodFrequency(qint64 freq); //called when demod frequency has changed void OnNewLowCutFreq(int freq); void OnNewHighCutFreq(int freq); protected: void mousePressEvent(QMouseEvent *); private: void readSettings(); void writeSettings(); void UpdateInfoBox(); void InitDemodSettings(); ///////////////////////// //Persistant Settings Variables saved ///////////////////////// bool m_StereoOut; bool m_UseTestBench; bool m_AlwaysOnTop; bool m_AgcOn; bool m_InvertSpectrum; bool m_USFm; qint64 m_CenterFrequency; qint64 m_DemodFrequency; quint32 m_SpanFrequency; QHostAddress m_IPAdr; quint32 m_Port; qint32 m_RadioType; qint32 m_BandwidthIndex; qint32 m_SoundInIndex; qint32 m_SoundOutIndex; qint32 m_ClickResolution; qint32 m_MaxDisplayRate; qint32 m_VertScaleIndex; qint32 m_dBStepSize; qint32 m_MaxdB; qint32 m_RfGain; qint32 m_LastSpanKhz; qint32 m_FftAve; qint32 m_FftSize; qint32 m_Volume; qint32 m_Percent2DScreen; qint32 m_DemodMode; double m_NCOSpurOffsetI; //NCO spur reduction variables double m_NCOSpurOffsetQ; tNoiseProcdInfo m_NoiseProcSettings; bool m_FreqChanged; QRect m_TestBenchRect; QString m_Str; QString m_Str2; QString m_ActiveDevice; CSdrInterface::eStatus m_Status; CSdrInterface::eStatus m_LastStatus; QTimer *m_pTimer; CSdrInterface* m_pSdrInterface; CDemodSetupDlg* m_pDemodSetupDlg; CRdsDecode m_RdsDecode; qint32 m_KeepAliveTimer; Ui::MainWindow *ui; }; #endif // MAINWINDOW_H cutesdr-1.0.5/gui/noiseprocdlg.ui0000644000175000017500000000603411546075374016760 0ustar bottomsbottoms CNoiseProcDlg 0 0 331 142 Noise Processing Setup 10 0 311 101 Noise Blanker 10 40 61 17 On 90 20 191 41 0 0 QFrame::StyledPanel QFrame::Sunken 2 0 90 60 191 41 0 0 QFrame::StyledPanel QFrame::Sunken 2 0 CSliderCtrl QFrame
gui/sliderctrl.h
1 valueChanged(int)
checkBoxNBOn toggled(bool) CNoiseProcDlg OnNBOn(bool) 32 61 4 72 OnNBOn(bool)
cutesdr-1.0.5/gui/aboutdlg.ui0000644000175000017500000000121111546075374016061 0ustar bottomsbottoms CAboutDlg 0 0 457 185 About CuteSDR 20 10 421 151 cutesdr-1.0.5/gui/freqctrl.h0000644000175000017500000000735111546075374015727 0ustar bottomsbottoms////////////////////////////////////////////////////////////////////// // freqctrl.h: interface for the CFreqCtrl class. // // History: // 2010-09-15 Initial creation MSW // 2011-03-27 Initial release ///////////////////////////////////////////////////////////////////// #ifndef FREQCTRL_H #define FREQCTRL_H /////////////////////////////////////////////////////////////////////// // To use this control, add a frame using the QT designer editor. // Promote it to the CFreqCtrl class and include file freqctrl.h // Initilaize the control in the constructor of the controls parent // ex: ui->frameFreqCtrl->Setup(9, 10000U, 230000000U, 1, UNITS_MHZ ); // where 9 is the number of display digits, min freq is 10KHz , Max is 230MHz // the minimum step size is 1Hz and the freq is displayed as MHz // NOTE: the frequency is a qint64 64 bit integer value // to change frequency call SetFrequency() // ex: ui->frameFreqCtrl->SetFrequency(146000000); // // One signal is sent when the control frequency changes: //void NewFrequency(qint64 freq); //emitted when frequency has changed /////////////////////////////////////////////////////////////////////// #include #include #include enum FUNITS{UNITS_HZ, UNITS_KHZ, UNITS_MHZ, UNITS_GHZ,UNITS_SEC,UNITS_MSEC,UNITS_USEC,UNITS_NSEC }; #define MAX_DIGITS 12 #define MIN_DIGITS 4 class CFreqCtrl : public QFrame { Q_OBJECT public: explicit CFreqCtrl(QWidget *parent = 0); ~CFreqCtrl(); QSize minimumSizeHint() const; QSize sizeHint() const; //primary access routines void Setup(int NumDigits, qint64 Minf, qint64 Maxf,int MinStep, FUNITS UnitsType); void SetFrequency(qint64 freq); void SetUnits(FUNITS units); void SetDigitColor(QColor cr); void SetBkColor(QColor cr); void SetUnitsColor(QColor cr); void SetHighlightColor(QColor cr); qint64 GetFrequency(){return m_freq;} signals: void NewFrequency(qint64 freq); //emitted when frequency has changed public slots: protected: //overrides for this control void paintEvent(QPaintEvent *); void resizeEvent(QResizeEvent* ); void mouseMoveEvent(QMouseEvent * ); void mousePressEvent(QMouseEvent * ); void wheelEvent( QWheelEvent * ); void leaveEvent( QEvent * ); void keyPressEvent( QKeyEvent * ); private: void UpdateCtrl(bool all); void DrawBkGround(QPainter &Painter); void DrawDigits(QPainter &Painter); void IncDigit(); void DecDigit(); void IncFreq(); void DecFreq(); void CursorHome(); void CursorEnd(); void MoveCursorLeft(); void MoveCursorRight(); bool InRect(QRect &rect, QPoint &point); bool m_UpdateAll; bool m_ExternalKeyActive; bool m_LRMouseFreqSel; int m_FirstEditableDigit; int m_LastLeadZeroPos; int m_LeadZeroPos; int m_NumDigits; int m_DigStart; int m_ActiveEditDigit; int m_LastEditDigit; int m_DecPos; int m_NumSeps; qint64 m_MinStep; qint64 m_freq; qint64 m_Oldfreq; qint64 m_MinFreq; qint64 m_MaxFreq; QColor m_DigitColor; QColor m_BkColor; QColor m_UnitsColor; QColor m_HighlightColor; QPixmap m_Pixmap; QSize m_Size; FUNITS m_Units; QRect m_rectCtrl; //main control rectangle QRect m_UnitsRect; //rectangle where Units text goes QRect m_SepRect[MAX_DIGITS]; //separation rectangles for commas,dec pt, etc. QString m_UnitString; QFont m_DigitFont; QFont m_UnitsFont; struct DigStuct { qint64 weight; //decimal weight of this digit qint64 incval; //value this digit increments or decrements QRect dQRect; //Digit bounding rectangle int val; //value of this digit(0-9) bool modified; //set if this digit has been modified bool editmode; //set if this digit is selected for editing }m_DigitInfo[MAX_DIGITS]; }; #endif // FREQCTRL_H cutesdr-1.0.5/gui/mainwindow.ui0000644000175000017500000004207711546075374016453 0ustar bottomsbottoms MainWindow 0 0 721 262 0 0 50 false false MainWindow 0 0 500 100 QFrame::StyledPanel QFrame::Sunken 1 0 0 0 20 QFrame::NoFrame QFrame::Plain 2 Info Qt::AlignCenter 0 0 120 0 Vertical Scale true 0 0 200 30 Demod Center Frequency QFrame::StyledPanel QFrame::Sunken 1 0 0 120 0 Maximum Screen dB dB Max dB -150 10 0 0 160 40 QFrame::WinPanel QFrame::Sunken 1 Qt::Horizontal 18 17 Qt::Horizontal 18 20 0 0 120 0 MS Shell Dlg 2 50 false Frequency Span in KHz KHz Span 1 500 10 100 Qt::Horizontal QSizePolicy::Fixed 26 20 0 0 148 23 SDR Center Frequency QFrame::StyledPanel QFrame::Sunken 1 Qt::Horizontal QSizePolicy::Fixed 26 20 0 3 100 30 Run 0 0 160 25 Volume 53 53 Qt::Horizontal QSlider::TicksAbove 10 Qt::Horizontal 18 24 0 0 721 23 File File Setup Demod About Noise Exit Network SoundCard SDR Display true false AlwaysOnTop Demod Setup Info Info Noise Processing CFreqCtrl QFrame
gui/freqctrl.h
1
CPlotter QFrame
gui/plotter.h
1
CMeter QFrame
gui/meter.h
1
pushButtonRun clicked() MainWindow OnRun() 479 211 314 200 SpanspinBox valueChanged(int) MainWindow OnSpanChanged(int) 75 203 40 200 MaxdBspinBox valueChanged(int) MainWindow OnMaxdBChanged(int) 418 165 523 162 ScalecomboBox currentIndexChanged(int) MainWindow OnVertScaleChanged(int) 87 164 53 156 horizontalSliderVol valueChanged(int) MainWindow OnVolumeSlider(int) 665 209 509 215 OnClicked(bool) OnRun() OnConnect(bool) OnDisplaySetup(int) OnSpanChanged(int) OnMaxdBChanged(int) OnVertScaleChanged(int) OnNcoSpur() OnVolumeSlider(int) OnMenuBar(QAction*)
cutesdr-1.0.5/gui/displaydlg.ui0000644000175000017500000001360711711303214016405 0ustar bottomsbottoms CDisplayDlg 0 0 298 172 Display Setup 120 130 171 32 Qt::Horizontal QDialogButtonBox::Cancel|QDialogButtonBox::Ok 10 10 101 22 10 30 91 20 FFT Size Qt::AlignCenter 20 60 61 22 1 0 80 111 16 FFT Averaging Qt::AlignCenter 160 10 91 22 Hz 10 100000 10 100 110 30 171 20 Mouse Click Resolution Qt::AlignCenter 120 60 161 22 Updates/Sec 1 50 1 20 130 80 141 20 Max Display Rate Qt::AlignCenter 150 110 121 17 UseTestBench 30 120 61 22 % 10 90 10 50 10 140 121 16 2D Screen Size % Qt::AlignCenter buttonBox accepted() CDisplayDlg accept() 290 141 157 274 buttonBox rejected() CDisplayDlg reject() 290 141 286 274 ActivateTestBench() cutesdr-1.0.5/gui/mainwindow.cpp0000644000175000017500000012043111716016005016570 0ustar bottomsbottoms///////////////////////////////////////////////////////////////////// // mainwindow.cpp: implementation of the mainwindow class. // // This class implements the top level GUI control and is the primary // object instantiated by main(). All other classes and threads are // spawned from this module. // // History: // 2010-09-15 Initial creation MSW // 2011-03-27 Initial release // 2011-04-16 Added Frequency range logic // 2011-05-26 Added support for In Use Status allowed wider sideband filters // 2011-08-07 Added WFM Support and spectrum inversion // 2012-01-05 Changed CW offset limits, changed scope resolution operators in downconverter module // 2012-02-11 ver 1.05 Updated to QT 4.8 and fixed issue with not remembering the span setting ///////////////////////////////////////////////////////////////////// //========================================================================================== // + + + This Software is released under the "Simplified BSD License" + + + //Copyright 2010 Moe Wheatley. All rights reserved. // //Redistribution and use in source and binary forms, with or without modification, are //permitted provided that the following conditions are met: // // 1. Redistributions of source code must retain the above copyright notice, this list of // conditions and the following disclaimer. // // 2. Redistributions in binary form must reproduce the above copyright notice, this list // of conditions and the following disclaimer in the documentation and/or other materials // provided with the distribution. // //THIS SOFTWARE IS PROVIDED BY Moe Wheatley ``AS IS'' AND ANY EXPRESS OR IMPLIED //WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND //FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL Moe Wheatley OR //CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR //CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR //SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON //ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING //NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF //ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // //The views and conclusions contained in the software and documentation are those of the //authors and should not be interpreted as representing official policies, either expressed //or implied, of Moe Wheatley. //========================================================================================== #include "gui/mainwindow.h" #include "ui_mainwindow.h" #include #include "gui/freqctrl.h" #include "gui/editnetdlg.h" #include "gui/sdrsetupdlg.h" #include "gui/noiseprocdlg.h" #include "gui/sounddlg.h" #include "gui/displaydlg.h" #include "gui/aboutdlg.h" #include "interface/perform.h" #define DISCOVER_CLIENT_PORT 48322 /* PC client Rx port, SDR Server Tx Port */ /*---------------------------------------------------------------------------*/ /*--------------------> L O C A L D E F I N E S <--------------------------*/ /*---------------------------------------------------------------------------*/ #define PROGRAM_TITLE_VERSION "CuteSdr 1.05" #define MAX_FFTDB 60 #define MIN_FFTDB -170 ///////////////////////////////////////////////////////////////////// // Constructor/Destructor ///////////////////////////////////////////////////////////////////// MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent), ui(new Ui::MainWindow) { ui->setupUi(this); setWindowTitle(PROGRAM_TITLE_VERSION); //create SDR interface class m_pSdrInterface = new CSdrInterface; //give GUI plotter access to the sdr interface object ui->framePlot->SetSdrInterface(m_pSdrInterface); //create the global Testbench object if(!g_pTestBench) g_pTestBench = new CTestBench(this); readSettings(); //read persistent settings ui->actionAlwaysOnTop->setChecked(m_AlwaysOnTop); AlwaysOnTop(); //create Demod setup menu for non-modal use(can leave up and still access rest of program) m_pDemodSetupDlg = new CDemodSetupDlg(this); m_pTimer = new QTimer(this); //connect a bunch of signals to the GUI objects connect(m_pTimer, SIGNAL(timeout()), this, SLOT(OnTimer())); connect(ui->frameFreqCtrl, SIGNAL(NewFrequency(qint64)), this, SLOT(OnNewCenterFrequency(qint64))); connect(ui->frameDemodFreqCtrl, SIGNAL(NewFrequency(qint64)), this, SLOT(OnNewDemodFrequency(qint64))); connect(m_pSdrInterface, SIGNAL(NewStatus(int)), this, SLOT( OnStatus(int) ) ); connect(m_pSdrInterface, SIGNAL(NewInfoData()), this, SLOT( OnNewInfoData() ) ); connect(m_pSdrInterface, SIGNAL(NewFftData()), this, SLOT( OnNewFftData() ) ); connect(ui->actionExit, SIGNAL(triggered()), this, SLOT(OnExit())); connect(ui->actionNetwork, SIGNAL(triggered()), this, SLOT(OnNetworkDlg())); connect(ui->actionSoundCard, SIGNAL(triggered()), this, SLOT(OnSoundCardDlg())); connect(ui->actionSDR, SIGNAL(triggered()), this, SLOT(OnSdrDlg())); connect(ui->actionDisplay, SIGNAL(triggered()), this, SLOT(OnDisplayDlg())); connect(ui->actionAlwaysOnTop, SIGNAL(triggered()), this, SLOT(AlwaysOnTop())); connect(ui->actionDemod_Setup, SIGNAL(triggered()), this, SLOT(OnDemodDlg())); connect(ui->actionNoise_Processing, SIGNAL(triggered()), this, SLOT(OnNoiseProcDlg())); connect(ui->actionAbout, SIGNAL(triggered()), this, SLOT(OnAbout())); connect(ui->framePlot, SIGNAL(NewDemodFreq(qint64)), this, SLOT( OnNewScreenDemodFreq(qint64) ) ); connect(ui->framePlot, SIGNAL(NewCenterFreq(qint64)), this, SLOT( OnNewScreenCenterFreq(qint64) ) ); connect(ui->framePlot, SIGNAL(NewLowCutFreq(int)), this, SLOT( OnNewLowCutFreq(int) ) ); connect(ui->framePlot, SIGNAL(NewHighCutFreq(int)), this, SLOT( OnNewHighCutFreq(int) ) ); m_pTimer->start(200); //start up status timer m_pSdrInterface->SetRadioType(m_RadioType); quint32 maxspan = m_pSdrInterface->GetMaxBWFromIndex(m_BandwidthIndex); ui->framePlot->SetPercent2DScreen(m_Percent2DScreen); //initialize controls and limits qint32 tmpspan = m_SpanFrequency; //save since setting range triggers control to update ui->SpanspinBox->setMaximum(maxspan/1000); m_SpanFrequency = tmpspan; if(m_SpanFrequency>maxspan) m_SpanFrequency = maxspan; ui->SpanspinBox->setValue(m_SpanFrequency/1000); m_LastSpanKhz = m_SpanFrequency/1000; //tmp save demod freq since gets set to center freq by center freq control inititalization qint64 tmpdemod = m_DemodFrequency; ui->frameFreqCtrl->Setup(9, 100U, 500000000U, 1, UNITS_KHZ ); ui->frameFreqCtrl->SetBkColor(Qt::darkBlue); ui->frameFreqCtrl->SetDigitColor(Qt::cyan); ui->frameFreqCtrl->SetUnitsColor(Qt::lightGray); ui->frameFreqCtrl->SetHighlightColor(Qt::darkGray); ui->frameFreqCtrl->SetFrequency(m_CenterFrequency); m_DemodFrequency = tmpdemod; ui->frameDemodFreqCtrl->Setup(9, 100U, 500000000U, 1, UNITS_KHZ ); ui->frameDemodFreqCtrl->SetBkColor(Qt::darkBlue); ui->frameDemodFreqCtrl->SetDigitColor(Qt::white); ui->frameDemodFreqCtrl->SetUnitsColor(Qt::lightGray); ui->frameDemodFreqCtrl->SetHighlightColor(Qt::darkGray); //limit demod frequency to Center Frequency +/-span frequency ui->frameDemodFreqCtrl->Setup(9, m_CenterFrequency-m_SpanFrequency/2, m_CenterFrequency+m_SpanFrequency/2, 1, UNITS_KHZ ); ui->frameDemodFreqCtrl->SetFrequency(m_DemodFrequency); ui->framePlot->SetSpanFreq( m_SpanFrequency ); ui->framePlot->SetCenterFreq( m_CenterFrequency ); ui->framePlot->SetClickResolution(m_ClickResolution); m_FreqChanged = false; ui->horizontalSliderVol->setValue(m_Volume); m_pSdrInterface->SetVolume(m_Volume); ui->ScalecomboBox->addItem("10 dB/Div", 10); ui->ScalecomboBox->addItem("5 dB/Div", 5); ui->ScalecomboBox->addItem("3 dB/Div", 3); ui->ScalecomboBox->addItem("1 dB/Div", 1); m_dBStepSize = (int)ui->ScalecomboBox->itemData(m_VertScaleIndex).toInt(); ui->ScalecomboBox->setCurrentIndex(m_VertScaleIndex); ui->framePlot->SetdBStepSize(m_dBStepSize); ui->MaxdBspinBox->setValue(m_MaxdB); ui->MaxdBspinBox->setSingleStep(m_dBStepSize); ui->MaxdBspinBox->setMinimum(MIN_FFTDB+VERT_DIVS*m_dBStepSize); ui->MaxdBspinBox->setMaximum(MAX_FFTDB); ui->framePlot->SetMaxdB(m_MaxdB); m_pSdrInterface->SetFftSize( m_FftSize); m_pSdrInterface->SetFftAve( m_FftAve); m_pSdrInterface->SetMaxDisplayRate(m_MaxDisplayRate); m_pSdrInterface->SetSdrBandwidthIndex(m_BandwidthIndex); m_pSdrInterface->SetSdrRfGain( m_RfGain ); m_pSdrInterface->ManageNCOSpurOffsets(CSdrInterface::NCOSPUR_CMD_SET, &m_NCOSpurOffsetI, &m_NCOSpurOffsetQ); m_pSdrInterface->SetSoundCardSelection(m_SoundInIndex, m_SoundOutIndex, m_StereoOut); m_pSdrInterface->SetSpectrumInversion(m_InvertSpectrum); m_pSdrInterface->SetUSFmVersion(m_USFm); InitDemodSettings(); ui->framePlot->SetDemodCenterFreq( m_DemodFrequency ); SetupDemod(m_DemodMode); m_RdsDecode.DecodeReset(m_USFm); SetupNoiseProc(); UpdateInfoBox(); m_ActiveDevice = ""; m_pSdrInterface->SetupNetwork(m_IPAdr,m_Port); m_Status = CSdrInterface::NOT_CONNECTED; m_LastStatus = m_Status; m_pSdrInterface->StartIO(); m_KeepAliveTimer = 0; if(m_UseTestBench) { //make sure top of dialog is visable(0,0 doesn't include menu bar.Qt bug?) if(m_TestBenchRect.top()<30) m_TestBenchRect.setTop(30); g_pTestBench->setGeometry(m_TestBenchRect); g_pTestBench->show(); g_pTestBench->Init(); } } MainWindow::~MainWindow() { if(g_pTestBench) delete g_pTestBench; if(m_pSdrInterface) { m_pSdrInterface->StopIO(); delete m_pSdrInterface; } if(m_pDemodSetupDlg) delete m_pDemodSetupDlg; delete ui; } ///////////////////////////////////////////////////////////////////// // Called when program is closed to save persistant data ///////////////////////////////////////////////////////////////////// void MainWindow::closeEvent(QCloseEvent *) { writeSettings(); if(m_pSdrInterface) m_pSdrInterface->StopIO(); } ///////////////////////////////////////////////////////////////////// // Called to set "Always on Top" Main Window state ///////////////////////////////////////////////////////////////////// void MainWindow::AlwaysOnTop() { m_AlwaysOnTop = ui->actionAlwaysOnTop->isChecked(); Qt::WindowFlags flags = this->windowFlags(); if (m_AlwaysOnTop) { this->setWindowFlags(flags | Qt::CustomizeWindowHint | Qt::WindowStaysOnTopHint); this->show(); } else { this->setWindowFlags( (flags | Qt::CustomizeWindowHint) & ~Qt::WindowStaysOnTopHint ); this->show(); } } ///////////////////////////////////////////////////////////////////// // Program persistant data save/recall methods ///////////////////////////////////////////////////////////////////// void MainWindow::writeSettings() { QSettings settings( QSettings::UserScope,"MoeTronix", "CuteSdr"); settings.beginGroup("MainWindow"); settings.setValue("geometry", saveGeometry()); settings.setValue("minstate",isMinimized()); if(g_pTestBench->isVisible()) { m_TestBenchRect = g_pTestBench->geometry(); settings.setValue("TestBenchRect",m_TestBenchRect); } settings.endGroup(); settings.beginGroup("Common"); settings.setValue("RadioType", m_RadioType); settings.setValue("CenterFrequency",(int)m_CenterFrequency); settings.setValue("SpanFrequency",(int)m_SpanFrequency); settings.setValue("IPAdr",m_IPAdr.toIPv4Address()); settings.setValue("Port",m_Port); settings.setValue("RfGain",m_RfGain); settings.setValue("BandwidthIndex", m_BandwidthIndex ); settings.setValue("SoundInIndex",m_SoundInIndex); settings.setValue("SoundOutIndex",m_SoundOutIndex); settings.setValue("StereoOut",m_StereoOut); settings.setValue("VertScaleIndex",m_VertScaleIndex); settings.setValue("MaxdB",m_MaxdB); settings.setValue("FftSize",m_FftSize); settings.setValue("FftAve",m_FftAve); settings.setValue("MaxDisplayRate",m_MaxDisplayRate); settings.setValue("ClickResolution",m_ClickResolution); settings.setValue("UseTestBench",m_UseTestBench); settings.setValue("AlwaysOnTop",m_AlwaysOnTop); settings.setValue("Volume",m_Volume); settings.setValue("Percent2DScreen",m_Percent2DScreen); settings.setValue("InvertSpectrum",m_InvertSpectrum); settings.setValue("USFm",m_USFm); //Get NCO spur offsets and save m_pSdrInterface->ManageNCOSpurOffsets(CSdrInterface::NCOSPUR_CMD_READ, &m_NCOSpurOffsetI, &m_NCOSpurOffsetQ); settings.setValue("NCOSpurOffsetI",m_NCOSpurOffsetI); settings.setValue("NCOSpurOffsetQ",m_NCOSpurOffsetQ); settings.setValue("DemodFrequency",(int)m_DemodFrequency); settings.setValue("DemodMode",m_DemodMode); settings.setValue("NBOn",m_NoiseProcSettings.NBOn); settings.setValue("NBThreshold",m_NoiseProcSettings.NBThreshold); settings.setValue("NBWidth",m_NoiseProcSettings.NBWidth); settings.endGroup(); settings.beginGroup("Testbench"); settings.setValue("SweepStartFrequency",g_pTestBench->m_SweepStartFrequency); settings.setValue("SweepStopFrequency",g_pTestBench->m_SweepStopFrequency); settings.setValue("SweepRate",g_pTestBench->m_SweepRate); settings.setValue("DisplayRate",g_pTestBench->m_DisplayRate); settings.setValue("VertRange",g_pTestBench->m_VertRange); settings.setValue("TrigIndex",g_pTestBench->m_TrigIndex); settings.setValue("TimeDisplay",g_pTestBench->m_TimeDisplay); settings.setValue("HorzSpan",g_pTestBench->m_HorzSpan); settings.setValue("TrigLevel",g_pTestBench->m_TrigLevel); settings.setValue("Profile",g_pTestBench->m_Profile); settings.setValue("GenOn",g_pTestBench->m_GenOn); settings.setValue("PeakOn",g_pTestBench->m_PeakOn); settings.setValue("PulseWidth",g_pTestBench->m_PulseWidth); settings.setValue("PulsePeriod",g_pTestBench->m_PulsePeriod); settings.setValue("SignalPower",g_pTestBench->m_SignalPower); settings.setValue("NoisePower",g_pTestBench->m_NoisePower); settings.setValue("UseFmGen",g_pTestBench->m_UseFmGen); settings.endGroup(); settings.beginWriteArray("Demod"); //save demod settings for (int i = 0; i < NUM_DEMODS; i++) { settings.setArrayIndex(i); settings.setValue("HiCut", m_DemodSettings[i].HiCut); settings.setValue("LowCut", m_DemodSettings[i].LowCut); settings.setValue("FilterClickResolution", m_DemodSettings[i].FilterClickResolution); settings.setValue("Offset", m_DemodSettings[i].Offset); settings.setValue("SquelchValue", m_DemodSettings[i].SquelchValue); settings.setValue("AgcSlope", m_DemodSettings[i].AgcSlope); settings.setValue("AgcThresh", m_DemodSettings[i].AgcThresh); settings.setValue("AgcManualGain", m_DemodSettings[i].AgcManualGain); settings.setValue("AgcDecay", m_DemodSettings[i].AgcDecay); settings.setValue("AgcOn", m_DemodSettings[i].AgcOn); settings.setValue("AgcHangOn", m_DemodSettings[i].AgcHangOn); } settings.endArray(); } void MainWindow::readSettings() { QSettings settings(QSettings::UserScope,"MoeTronix", "CuteSdr"); settings.beginGroup("MainWindow"); restoreGeometry(settings.value("geometry").toByteArray()); bool ismin = settings.value("minstate", false).toBool(); m_TestBenchRect = settings.value("TestBenchRect", QRect(0,0,500,200)).toRect(); settings.endGroup(); settings.beginGroup("Common"); m_CenterFrequency = (qint64)settings.value("CenterFrequency", 15000000).toUInt(); m_SpanFrequency = settings.value("SpanFrequency", 100000).toUInt(); m_IPAdr.setAddress(settings.value("IPAdr", 0xC0A80164).toInt() ); m_Port = settings.value("Port", 50000).toUInt(); m_RfGain = settings.value("RfGain", 0).toInt(); m_BandwidthIndex = settings.value("BandwidthIndex", 0).toInt(); m_SoundInIndex = settings.value("SoundInIndex", 0).toInt(); m_SoundOutIndex = settings.value("SoundOutIndex", 0).toInt(); m_StereoOut = settings.value("StereoOut", false).toBool(); m_VertScaleIndex = settings.value("VertScaleIndex", 0).toInt(); m_MaxdB = settings.value("MaxdB", 0).toInt(); m_FftAve = settings.value("FftAve", 0).toInt(); m_FftSize = settings.value("FftSize", 4096).toInt(); m_MaxDisplayRate = settings.value("MaxDisplayRate", 10).toInt(); m_RadioType = settings.value("RadioType", 0).toInt(); m_ClickResolution = settings.value("ClickResolution",100).toInt(); m_Volume = settings.value("Volume",100).toInt(); m_Percent2DScreen = settings.value("Percent2DScreen",50).toInt(); m_NCOSpurOffsetI = settings.value("NCOSpurOffsetI",0.0).toDouble(); m_NCOSpurOffsetQ = settings.value("NCOSpurOffsetQ",0.0).toDouble(); m_UseTestBench = settings.value("UseTestBench", false).toBool(); m_AlwaysOnTop = settings.value("AlwaysOnTop", false).toBool(); m_InvertSpectrum = settings.value("InvertSpectrum", false).toBool(); m_USFm = settings.value("USFm", true).toBool(); m_NoiseProcSettings.NBOn = settings.value("NBOn", false).toBool(); m_NoiseProcSettings.NBThreshold = settings.value("NBThreshold",0).toInt(); m_NoiseProcSettings.NBWidth = settings.value("NBWidth",50).toInt(); m_DemodMode = settings.value("DemodMode", DEMOD_AM).toInt(); m_DemodFrequency = (qint64)settings.value("DemodFrequency", 15000000).toUInt(); settings.endGroup(); settings.beginGroup("Testbench"); g_pTestBench->m_SweepStartFrequency = settings.value("SweepStartFrequency",0.0).toDouble(); g_pTestBench->m_SweepStopFrequency = settings.value("SweepStopFrequency",1.0).toDouble(); g_pTestBench->m_SweepRate = settings.value("SweepRate",0.0).toDouble(); g_pTestBench->m_DisplayRate = settings.value("DisplayRate",10).toInt(); g_pTestBench->m_VertRange = settings.value("VertRange",10000).toInt(); g_pTestBench->m_TrigIndex = settings.value("TrigIndex",0).toInt(); g_pTestBench->m_TrigLevel = settings.value("TrigLevel",100).toInt(); g_pTestBench->m_HorzSpan = settings.value("HorzSpan",100).toInt(); g_pTestBench->m_Profile = settings.value("Profile",0).toInt(); g_pTestBench->m_TimeDisplay = settings.value("TimeDisplay",false).toBool(); g_pTestBench->m_GenOn = settings.value("GenOn",false).toBool(); g_pTestBench->m_PeakOn = settings.value("PeakOn",false).toBool(); g_pTestBench->m_PulseWidth = settings.value("PulseWidth",0.0).toDouble(); g_pTestBench->m_PulsePeriod = settings.value("PulsePeriod",0.0).toDouble(); g_pTestBench->m_SignalPower = settings.value("SignalPower",0.0).toDouble(); g_pTestBench->m_NoisePower = settings.value("NoisePower",0.0).toDouble(); g_pTestBench->m_UseFmGen = settings.value("UseFmGen",false).toBool(); settings.endGroup(); settings.beginReadArray("Demod"); //get demod settings for (int i = 0; i < NUM_DEMODS; i++) { settings.setArrayIndex(i); m_DemodSettings[i].HiCut = settings.value("HiCut", 5000).toInt(); m_DemodSettings[i].LowCut = settings.value("LowCut", -5000).toInt(); m_DemodSettings[i].FilterClickResolution = settings.value("FilterClickResolution", 100).toInt(); m_DemodSettings[i].Offset = settings.value("Offset", 0).toInt(); m_DemodSettings[i].SquelchValue = settings.value("SquelchValue", 0).toInt(); m_DemodSettings[i].AgcSlope = settings.value("AgcSlope", 0).toInt(); m_DemodSettings[i].AgcThresh = settings.value("AgcThresh", -100).toInt(); m_DemodSettings[i].AgcManualGain = settings.value("AgcManualGain", 30).toInt(); m_DemodSettings[i].AgcDecay = settings.value("AgcDecay", 200).toInt(); m_DemodSettings[i].AgcOn = settings.value("AgcOn",true).toBool(); m_DemodSettings[i].AgcHangOn = settings.value("AgcHangOn",false).toBool(); } settings.endArray(); if(ismin) showMinimized(); } ///////////////////////////////////////////////////////////////////// // Status Timer event handler ///////////////////////////////////////////////////////////////////// void MainWindow::OnTimer() { OnStatus(m_Status); if(++m_KeepAliveTimer>5) { m_KeepAliveTimer = 0; if( (CSdrInterface::RUNNING == m_Status) || ( CSdrInterface::CONNECTED == m_Status) ) m_pSdrInterface->KeepAlive(); } ui->frameMeter->SetdBmLevel( m_pSdrInterface->GetSMeterAve() ); if(DEMOD_WFM == m_DemodMode) //if in WFM mode manage stereo status display { bool update = false; if( m_FreqChanged ) { m_FreqChanged = false; m_RdsDecode.DecodeReset(m_USFm); ui->framePlot->m_RdsCall[0] = 0; ui->framePlot->m_RdsText[0] = 0; update = true; } else { tRDS_GROUPS RdsGroups; if( m_pSdrInterface->GetStereoLock(NULL) ) //if Stereo pilot state changed update = true; if( m_pSdrInterface->GetNextRdsGroupData(&RdsGroups) ) //if new data in RDS queue { if( 0 != RdsGroups.BlockA) { //if valid data in que then decode it m_RdsDecode.DecodeRdsGroup(&RdsGroups); if( m_RdsDecode.GetRdsString(ui->framePlot->m_RdsText) ) update = true; if( m_RdsDecode.GetRdsCallString(ui->framePlot->m_RdsCall) ) update = true; } else { //a zero data block means loss of signal so clear display and reset decoder m_RdsDecode.DecodeReset(m_USFm); ui->framePlot->m_RdsCall[0] = 0; ui->framePlot->m_RdsText[0] = 0; update = true; } } } if(update) ui->framePlot->UpdateOverlay(); } } ///////////////////////////////////////////////////////////////////// // Mouse Right button event handler brings up Demod dialog ///////////////////////////////////////////////////////////////////// void MainWindow::mousePressEvent(QMouseEvent *event) { if(Qt::RightButton==event->button() ) { OnDemodDlg(); } } ///////////////////////////////////////////////////////////////////// // About Dialog Menu Bar action item handler. ///////////////////////////////////////////////////////////////////// void MainWindow::OnAbout() { CAboutDlg dlg(this,PROGRAM_TITLE_VERSION); dlg.exec(); } ///////////////////////////////////////////////////////////////////// // Menu Bar action item handler. //Exit menu ///////////////////////////////////////////////////////////////////// void MainWindow::OnExit() { qApp->exit(0); } ///////////////////////////////////////////////////////////////////// // Menu Bar action item handler. //Display Setup Menu ///////////////////////////////////////////////////////////////////// void MainWindow::OnDisplayDlg() { CDisplayDlg dlg(this); dlg.m_FftSize = m_FftSize; dlg.m_FftAve = m_FftAve; dlg.m_ClickResolution = m_ClickResolution; dlg.m_MaxDisplayRate = m_MaxDisplayRate; dlg.m_UseTestBench = m_UseTestBench; dlg.m_Percent2DScreen = m_Percent2DScreen; dlg.InitDlg(); if(QDialog::Accepted == dlg.exec() ) { if(dlg.m_NeedToStop) { if(CSdrInterface::RUNNING == m_Status) { m_pSdrInterface->StopSdr(); ui->framePlot->SetRunningState(false); } } if(m_Percent2DScreen != dlg.m_Percent2DScreen) { //if 2D screen size changed then update it m_Percent2DScreen = dlg.m_Percent2DScreen; ui->framePlot->SetPercent2DScreen(m_Percent2DScreen); } m_FftSize = dlg.m_FftSize; m_FftAve = dlg.m_FftAve; m_ClickResolution = dlg.m_ClickResolution; m_MaxDisplayRate = dlg.m_MaxDisplayRate; m_UseTestBench = dlg.m_UseTestBench; m_pSdrInterface->SetFftAve( m_FftAve); m_pSdrInterface->SetFftSize( m_FftSize); m_pSdrInterface->SetMaxDisplayRate(m_MaxDisplayRate); ui->framePlot->SetClickResolution(m_ClickResolution); if(m_UseTestBench) { //make TestBench visable if not already if(!g_pTestBench->isVisible()) { //make sure top of dialog is visable(0,0 doesn't include menu bar.Qt bug?) if(m_TestBenchRect.top()<30) m_TestBenchRect.setTop(30); g_pTestBench->setGeometry(m_TestBenchRect); g_pTestBench->show(); g_pTestBench->Init(); } g_pTestBench->activateWindow(); } else { //hide testbench if(g_pTestBench->isVisible()) g_pTestBench->hide(); } } } ///////////////////////////////////////////////////////////////////// // Menu Bar action item handler. //Sound Card Setup Menu ///////////////////////////////////////////////////////////////////// void MainWindow::OnSoundCardDlg() { CSoundDlg dlg(this); dlg.SetInputIndex(m_SoundInIndex); dlg.SetOutputIndex(m_SoundOutIndex); dlg.SetStereo(m_StereoOut); if(QDialog::Accepted == dlg.exec() ) { if(CSdrInterface::RUNNING == m_Status) { m_pSdrInterface->StopSdr(); ui->framePlot->SetRunningState(false); } m_StereoOut = dlg.GetStereo(); m_SoundInIndex = dlg.GetInputIndex(); m_SoundOutIndex = dlg.GetOutputIndex(); m_pSdrInterface->SetSoundCardSelection(m_SoundInIndex, m_SoundOutIndex, m_StereoOut); } } ///////////////////////////////////////////////////////////////////// // Menu Bar action item handler. //SDR Setup Menu ///////////////////////////////////////////////////////////////////// void MainWindow::OnSdrDlg() { CSdrSetupDlg dlg(this,m_pSdrInterface); dlg.m_BandwidthIndex = m_BandwidthIndex; dlg.m_USFm = m_USFm; dlg.InitDlg(); dlg.SetSpectrumInversion(m_InvertSpectrum); if( dlg.exec() ) { if( m_BandwidthIndex != dlg.m_BandwidthIndex ) { m_BandwidthIndex = dlg.m_BandwidthIndex; if(CSdrInterface::RUNNING == m_Status) { m_pSdrInterface->StopSdr(); ui->framePlot->SetRunningState(false); } } SetupNoiseProc(); m_RfGain = dlg.m_RfGain; m_USFm = dlg.m_USFm; m_pSdrInterface->SetSdrRfGain( dlg.m_RfGain); m_pSdrInterface->SetUSFmVersion(m_USFm); m_pSdrInterface->SetSdrBandwidthIndex(m_BandwidthIndex); quint32 maxspan = m_pSdrInterface->GetMaxBWFromIndex(m_BandwidthIndex); ui->SpanspinBox->setMaximum(maxspan/1000); if(m_SpanFrequency>maxspan) m_SpanFrequency = maxspan; ui->SpanspinBox->setValue(m_SpanFrequency/1000); ui->framePlot->SetSpanFreq( m_SpanFrequency ); //limit demod frequency to Center Frequency +/-span frequency ui->frameDemodFreqCtrl->Setup(9, m_CenterFrequency-m_SpanFrequency/2, m_CenterFrequency+m_SpanFrequency/2, 1, UNITS_KHZ ); ui->frameDemodFreqCtrl->SetFrequency(m_DemodFrequency); m_pSdrInterface->SetDemod(m_DemodMode, m_DemodSettings[m_DemodMode]); m_InvertSpectrum = dlg.GetSpectrumInversion(); m_pSdrInterface->SetSpectrumInversion(m_InvertSpectrum); } } ///////////////////////////////////////////////////////////////////// // Menu Bar action item handler. //Network Setup Menu ///////////////////////////////////////////////////////////////////// void MainWindow::OnNetworkDlg() { CEditNetDlg dlg(this); dlg.m_IPAdr = m_IPAdr; dlg.m_Port = m_Port; dlg.m_ActiveDevice = m_ActiveDevice; dlg.InitDlg(); if( dlg.exec() ) { if( dlg.m_DirtyFlag ) { if(CSdrInterface::RUNNING == m_Status) { m_pSdrInterface->StopSdr(); ui->framePlot->SetRunningState(false); } m_IPAdr = dlg.m_IPAdr; m_Port = dlg.m_Port; m_ActiveDevice = dlg.m_ActiveDevice; m_pSdrInterface->SetupNetwork(m_IPAdr,m_Port); } } } ///////////////////////////////////////////////////////////////////// // Menu Bar action item handler. //Demod Setup Menu (Non-Modal ie allows user to continue in other windows) ///////////////////////////////////////////////////////////////////// void MainWindow::OnDemodDlg() { m_pDemodSetupDlg->m_DemodMode = m_DemodMode; m_pDemodSetupDlg->InitDlg(); m_pDemodSetupDlg->show(); } ///////////////////////////////////////////////////////////////////// // Menu Bar action item handler. //Noise Processing Setup Menu ///////////////////////////////////////////////////////////////////// void MainWindow::OnNoiseProcDlg() { CNoiseProcDlg dlg(this); dlg.InitDlg(&m_NoiseProcSettings); dlg.exec(); } ///////////////////////////////////////////////////////////////////// // Called to update the information text box ///////////////////////////////////////////////////////////////////// void MainWindow::UpdateInfoBox() { //display filter cutoffs m_Str = QString("%1 %2 %3 %4 %5") .arg(m_DemodSettings[m_DemodMode].txt) .arg("Lo=").arg(m_DemodSettings[m_DemodMode].LowCut) .arg("Hi=").arg(m_DemodSettings[m_DemodMode].HiCut); ui->InfoText->setText(m_Str); } ///////////////////////////////////////////////////////////////////// // Called by Start/Stop Button event ///////////////////////////////////////////////////////////////////// void MainWindow::OnRun() { if( CSdrInterface::CONNECTED == m_Status) { m_CenterFrequency = m_pSdrInterface->SetRxFreq(m_CenterFrequency); m_pSdrInterface->SetDemodFreq(m_CenterFrequency - m_DemodFrequency); m_pSdrInterface->StartSdr(); m_pSdrInterface->m_MissedPackets = 0; ui->framePlot->SetRunningState(true); InitPerformance(); m_RdsDecode.DecodeReset(m_USFm); } else if(CSdrInterface::RUNNING == m_Status) { m_pSdrInterface->StopSdr(); ui->framePlot->SetRunningState(false); ReadPerformance(); } } ///////////////////////////////////////////////////////////////////// // Event: New FFT Display Data is available so draw it // Called when new spectrum data is available to be displayed ///////////////////////////////////////////////////////////////////// void MainWindow::OnNewFftData() { if( CSdrInterface::RUNNING == m_Status) ui->framePlot->draw(); } ///////////////////////////////////////////////////////////////////// // Manage Status states. Called periodically by Timer event ///////////////////////////////////////////////////////////////////// void MainWindow::OnStatus(int status) { m_Status = (CSdrInterface::eStatus)status; //qDebug()<<"Status"<< status; switch(status) { case CSdrInterface::NOT_CONNECTED: if( m_LastStatus == CSdrInterface::RUNNING) { m_pSdrInterface->StopSdr(); ui->framePlot->SetRunningState(false); } ui->statusBar->showMessage(tr("SDR Not Connected"), 0); ui->pushButtonRun->setText("Run"); ui->pushButtonRun->setEnabled(false); break; case CSdrInterface::CONNECTED: if( m_LastStatus == CSdrInterface::RUNNING) { m_pSdrInterface->StopSdr(); ui->framePlot->SetRunningState(false); } ui->statusBar->showMessage( m_ActiveDevice + tr(" Connected"), 0); if( m_LastStatus == CSdrInterface::NOT_CONNECTED) m_pSdrInterface->GetSdrInfo(); ui->pushButtonRun->setText("Run"); ui->pushButtonRun->setEnabled(true); break; case CSdrInterface::RUNNING: m_Str.setNum(m_pSdrInterface->GetRateError()); m_Str.append(" ppm Missed Pkts="); m_Str2.setNum(m_pSdrInterface->m_MissedPackets); m_Str.append(m_Str2); ui->statusBar->showMessage(m_ActiveDevice + tr(" Running ") + m_Str, 0); ui->pushButtonRun->setText("Stop"); ui->pushButtonRun->setEnabled(TRUE); break; case CSdrInterface::ERROR: if( m_LastStatus == CSdrInterface::RUNNING) { m_pSdrInterface->StopSdr(); ui->framePlot->SetRunningState(false); } ui->statusBar->showMessage(tr("SDR Not Connected"), 0); ui->pushButtonRun->setText("Run"); ui->pushButtonRun->setEnabled(FALSE); break; case CSdrInterface::ADOVR: if( m_LastStatus == CSdrInterface::RUNNING) { m_Status = CSdrInterface::RUNNING; ui->framePlot->SetADOverload(true); } break; default: break; } m_LastStatus = m_Status; } ///////////////////////////////////////////////////////////////////// // Event handler that New SDR Info is available // Called when a radio is first conencted and it reports its information ///////////////////////////////////////////////////////////////////// void MainWindow::OnNewInfoData() { m_ActiveDevice = m_pSdrInterface->m_DeviceName; m_RadioType = m_pSdrInterface->GetRadioType(); m_pSdrInterface->SetSdrBandwidthIndex(m_BandwidthIndex); //update span limits to new radio attached quint32 maxspan = m_pSdrInterface->GetMaxBWFromIndex(m_BandwidthIndex); ui->SpanspinBox->setMaximum(maxspan/1000); if(m_SpanFrequency>maxspan) m_SpanFrequency = maxspan; ui->SpanspinBox->setValue(m_SpanFrequency/1000); m_LastSpanKhz = m_SpanFrequency/1000; ui->framePlot->SetSpanFreq( m_SpanFrequency ); m_pSdrInterface->SetDemod(m_DemodMode, m_DemodSettings[m_DemodMode]); } ///////////////////////////////////////////////////////////////////// // Handle change event for center frequency control ///////////////////////////////////////////////////////////////////// void MainWindow::OnNewCenterFrequency(qint64 freq) { //qDebug()<<"F = "<SetRxFreq(freq); if(m_CenterFrequency!=freq) //if freq was clamped by sdr range then update control again ui->frameFreqCtrl->SetFrequency(m_CenterFrequency); m_DemodFrequency = m_CenterFrequency; m_pSdrInterface->SetDemodFreq(m_CenterFrequency - m_DemodFrequency); ui->framePlot->SetCenterFreq( m_CenterFrequency ); ui->framePlot->SetDemodCenterFreq( m_DemodFrequency ); //limit demod frequency to Center Frequency +/-span frequency ui->frameDemodFreqCtrl->Setup(9, m_CenterFrequency-m_SpanFrequency/2, m_CenterFrequency+m_SpanFrequency/2, 1, UNITS_KHZ ); ui->frameDemodFreqCtrl->SetFrequency(m_DemodFrequency); m_FreqChanged = true; ui->framePlot->UpdateOverlay(); } ///////////////////////////////////////////////////////////////////// // Handle change event for demod frequency control ///////////////////////////////////////////////////////////////////// void MainWindow::OnNewDemodFrequency(qint64 freq) { m_DemodFrequency = freq; ui->framePlot->SetDemodCenterFreq( m_DemodFrequency ); ui->framePlot->UpdateOverlay(); m_pSdrInterface->SetDemodFreq(m_CenterFrequency - m_DemodFrequency); m_FreqChanged = true; } ///////////////////////////////////////////////////////////////////// // Handle plot mouse change event for center frequency ///////////////////////////////////////////////////////////////////// void MainWindow::OnNewScreenCenterFreq(qint64 freq) { m_CenterFrequency = freq; ui->frameFreqCtrl->SetFrequency(m_CenterFrequency); } ///////////////////////////////////////////////////////////////////// // Handle plot mouse change event for demod frequency ///////////////////////////////////////////////////////////////////// void MainWindow::OnNewScreenDemodFreq(qint64 freq) { m_DemodFrequency = freq; ui->frameDemodFreqCtrl->SetFrequency(m_DemodFrequency); } ///////////////////////////////////////////////////////////////////// // Handle plot mouse change event for filter cutoff frequencies ///////////////////////////////////////////////////////////////////// void MainWindow::OnNewLowCutFreq(int freq) { m_DemodSettings[m_DemodMode].LowCut = freq; UpdateInfoBox(); m_pSdrInterface->SetDemod(m_DemodMode, m_DemodSettings[m_DemodMode]); } void MainWindow::OnNewHighCutFreq(int freq) { m_DemodSettings[m_DemodMode].HiCut = freq; UpdateInfoBox(); m_pSdrInterface->SetDemod(m_DemodMode, m_DemodSettings[m_DemodMode]); } ///////////////////////////////////////////////////////////////////// // Handle Span Spin Control change event ///////////////////////////////////////////////////////////////////// void MainWindow::OnSpanChanged(int spanKhz) { if( (spanKhz>m_LastSpanKhz) && (spanKhz==10)) {//if going higher and is 10KHz //change stepsize to 10KHz ui->SpanspinBox->setSingleStep(10); } else if( (spanKhzSpanspinBox->setSingleStep(1); } m_LastSpanKhz = spanKhz; m_SpanFrequency = spanKhz*1000; ui->framePlot->SetSpanFreq(m_SpanFrequency); ui->framePlot->UpdateOverlay(); //limit demod frequency to Center Frequency +/-span frequency ui->frameDemodFreqCtrl->Setup(9, m_CenterFrequency-m_SpanFrequency/2, m_CenterFrequency+m_SpanFrequency/2, 1, UNITS_KHZ ); ui->frameDemodFreqCtrl->SetFrequency(m_DemodFrequency); } ///////////////////////////////////////////////////////////////////// // Handle Max dB Spin Control change event ///////////////////////////////////////////////////////////////////// void MainWindow::OnMaxdBChanged(int maxdb) { m_MaxdB = maxdb; ui->framePlot->SetMaxdB(m_MaxdB); ui->framePlot->UpdateOverlay(); } ///////////////////////////////////////////////////////////////////// // Handle Vertical scale COmbo box Control change event ///////////////////////////////////////////////////////////////////// void MainWindow::OnVertScaleChanged(int index) { //hack to ignor event when control is initialized if(ui->ScalecomboBox->count() != 4) return; m_VertScaleIndex = index; int LastdBStepsize = m_dBStepSize; int LastMaxdB = m_MaxdB; m_dBStepSize = (int)ui->ScalecomboBox->itemData(m_VertScaleIndex).toInt(); ui->framePlot->SetdBStepSize(m_dBStepSize); ui->MaxdBspinBox->setSingleStep(m_dBStepSize); ui->MaxdBspinBox->setMinimum(MIN_FFTDB+VERT_DIVS*m_dBStepSize); ui->MaxdBspinBox->setMaximum(MAX_FFTDB); //adjust m_MaxdB to try and keep signal roughly centered at bottom of screen if(m_dBStepSize!=LastdBStepsize) { m_MaxdB = LastMaxdB + 11*(m_dBStepSize-LastdBStepsize); m_MaxdB = (m_MaxdB/m_dBStepSize)*m_dBStepSize; } ui->MaxdBspinBox->setValue(m_MaxdB); ui->framePlot->SetMaxdB(m_MaxdB); ui->framePlot->UpdateOverlay(); //qDebug()<<"m_VertScaleIndex="<SetDemod(m_DemodMode, m_DemodSettings[m_DemodMode]); } cutesdr-1.0.5/gui/sdrdiscoverdlg.ui0000644000175000017500000000744711711303214017274 0ustar bottomsbottoms CSdrDiscoverDlg 0 0 412 300 SDR Discover 30 260 341 32 Qt::Horizontal QDialogButtonBox::Cancel|QDialogButtonBox::Ok 0 50 411 191 Sans Serif 9 75 true Double Click on Entry to Select 150 10 91 31 Press to Find all SDR's on Network Find SDR 20 240 361 20 10 75 true false Double Click on Entry to Select Qt::AlignCenter buttonBox accepted() CSdrDiscoverDlg accept() 248 254 157 274 buttonBox rejected() CSdrDiscoverDlg reject() 316 260 286 274 pushButtonFind clicked() CSdrDiscoverDlg OnFind() 171 21 137 22 listWidget itemDoubleClicked(QListWidgetItem*) CSdrDiscoverDlg OnItemDoubleClick(QListWidgetItem*) 281 114 282 27 OnFind() OnItemDoubleClick(QListWidgetItem*) cutesdr-1.0.5/gui/meter.h0000644000175000017500000000207011546075374015212 0ustar bottomsbottoms////////////////////////////////////////////////////////////////////// // meter.h: interface for the CMeter class. // // History: // 2010-12-28 Initial creation MSW // 2011-03-27 Initial release ///////////////////////////////////////////////////////////////////// #ifndef METER_H #define METER_H #include #include #include #include "interface/sdrinterface.h" class CMeter : public QFrame { Q_OBJECT public: explicit CMeter(QWidget *parent = 0); ~CMeter(); QSize minimumSizeHint() const; QSize sizeHint() const; void draw(); //call to draw new fft data onto screen plot void UpdateOverlay(){DrawOverlay();} signals: public slots: void SetdBmLevel(double dbm); protected: //re-implemented widget event handlers void paintEvent(QPaintEvent *event); void resizeEvent(QResizeEvent* event); private: void DrawOverlay(); QPixmap m_2DPixmap; QPixmap m_OverlayPixmap; QSize m_Size; QString m_Str; int m_Slevel; int m_dBm; CSdrInterface* m_pSdrInterface; }; #endif // METER_H cutesdr-1.0.5/gui/noiseprocdlg.h0000644000175000017500000000147711546075374016600 0ustar bottomsbottoms////////////////////////////////////////////////////////////////////// // noiseprocdlg.h: interface for the CNoiseProcDlg class. // // History: // 2011-02-6 Initial creation MSW // 2011-03-27 Initial release ///////////////////////////////////////////////////////////////////// #ifndef NOISEPROCDLG_H #define NOISEPROCDLG_H #include #include "dsp/noiseproc.h" namespace Ui { class CNoiseProcDlg; } class CNoiseProcDlg : public QDialog { Q_OBJECT public: explicit CNoiseProcDlg(QWidget *parent = 0); ~CNoiseProcDlg(); void InitDlg(tNoiseProcdInfo* pNoiseProcSettings); private slots: void OnNBThresh(int); void OnNBWidth(int); void OnNBOn(bool); private: Ui::CNoiseProcDlg *ui; tNoiseProcdInfo* m_pNoiseProcSettings; }; #endif // NOISEPROCDLG_H cutesdr-1.0.5/gui/sdrdiscoverdlg.cpp0000644000175000017500000002021411716016005017430 0ustar bottomsbottoms///////////////////////////////////////////////////////////////////// // sdrdiscoverdlg.cpp: implementation of the CSdrDiscoverDlg class. // // This class implements a discover widget to find RFSPACE SDR's //connected to a network using a simple UDP broadcast method. // // History: // 2010-09-15 Initial creation MSW // 2011-03-27 Initial release // 2011-05-26 Added support for In Use Status // 2012-02-11 Qt 4.8 finally fixed UDP socket close rebind bug ///////////////////////////////////////////////////////////////////// //========================================================================================== // + + + This Software is released under the "Simplified BSD License" + + + //Copyright 2010 Moe Wheatley. All rights reserved. // //Redistribution and use in source and binary forms, with or without modification, are //permitted provided that the following conditions are met: // // 1. Redistributions of source code must retain the above copyright notice, this list of // conditions and the following disclaimer. // // 2. Redistributions in binary form must reproduce the above copyright notice, this list // of conditions and the following disclaimer in the documentation and/or other materials // provided with the distribution. // //THIS SOFTWARE IS PROVIDED BY Moe Wheatley ``AS IS'' AND ANY EXPRESS OR IMPLIED //WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND //FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL Moe Wheatley OR //CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR //CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR //SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON //ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING //NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF //ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // //The views and conclusions contained in the software and documentation are those of the //authors and should not be interpreted as representing official policies, either expressed //or implied, of Moe Wheatley. //========================================================================================== #include "gui/sdrdiscoverdlg.h" #include /*---------------------------------------------------------------------------*/ /*--------------------> L O C A L D E F I N E S <--------------------------*/ /*---------------------------------------------------------------------------*/ /* UDP port numbers for Discover */ #define DISCOVER_SERVER_PORT 48321 /* PC client Tx port, SDR Server Rx Port */ #define DISCOVER_CLIENT_PORT 48322 /* PC client Rx port, SDR Server Tx Port */ #define KEY0 0x5A #define KEY1 0xA5 #define MSG_REQ 0 #define MSG_RESP 1 #define MSG_SET 2 ////////////////////////////////////////////////////////////////////////////// //Constructor/Destructor ////////////////////////////////////////////////////////////////////////////// CSdrDiscoverDlg::CSdrDiscoverDlg(QWidget *parent) : QDialog(parent), ui(new Ui::CSdrDiscoverDlg) { ui->setupUi(this); ui->listWidget->clear(); m_Name = ""; m_NameFilter = ""; m_pUdpDiscoverSocket = new QUdpSocket(this); connect(m_pUdpDiscoverSocket, SIGNAL(readyRead()), this, SLOT(ReadUDPMessages())); m_UdpOpen = false; } CSdrDiscoverDlg::~CSdrDiscoverDlg() { if(m_pUdpDiscoverSocket) delete m_pUdpDiscoverSocket; delete ui; } //Fill in initial data void CSdrDiscoverDlg::InitDlg() { OnFind(); } ////////////////////////////////////////////////////////////////////////////// //Called when Find button pressed ////////////////////////////////////////////////////////////////////////////// void CSdrDiscoverDlg::OnFind() { ui->listWidget->clear(); //clear screen then delay sending to create visible clear m_Timer.singleShot(250, this, SLOT( SendDiscoverRequest()) ); } void CSdrDiscoverDlg::CloseUdp() { if(m_UdpOpen) { m_pUdpDiscoverSocket->close(); m_UdpOpen = false; qDebug("UDP Close"); } } void CSdrDiscoverDlg::SendDiscoverRequest() { tDiscover_COMMONMSG reqmsg; //bind UDP socket with receive port value if(!m_UdpOpen) m_pUdpDiscoverSocket->bind(DISCOVER_CLIENT_PORT); m_UdpOpen = true; qint64 length = sizeof(tDiscover_COMMONMSG); memset((void*)&reqmsg, 0, length); reqmsg.length[0] = length&0xFF; reqmsg.length[1] = (length>>8)&0xFF; reqmsg.key[0] = KEY0; reqmsg.key[1] = KEY1; reqmsg.op = MSG_REQ; for(int i=0; iwriteDatagram( (const char*)&reqmsg, length, QHostAddress::Broadcast, DISCOVER_SERVER_PORT); m_CloseTimer.singleShot(250, this, SLOT( CloseUdp()) ); } //Called when UDP messages are received on the client port for parsing void CSdrDiscoverDlg::ReadUDPMessages() { qint64 totallength; qint64 length; int index=0; bool InUse; tDiscover_COMMONMSG tmpmsg; char Buf[2048]; //buffer to hold received UDP packet while( m_pUdpDiscoverSocket->hasPendingDatagrams() ) { //loop and get all UDP packets availaable totallength = m_pUdpDiscoverSocket->pendingDatagramSize(); m_pUdpDiscoverSocket->readDatagram( Buf, totallength); //read entire UDP packet //see if msg cam from same device that is already in the list //if so then delete old one so new one will get added index = ui->listWidget->count(); //get index to next free position in list length = sizeof(tDiscover_COMMONMSG); memcpy((void*)&tmpmsg, (void*)Buf, length ); //get tmp copy of new message for(int i=0; ilistWidget->takeItem(i); //remove old duplicate break; } } index = ui->listWidget->count(); //get index to next free position in list //copy just the common fixed size block into m_DiscovermsgCommon memcpy((void*)&m_DiscovermsgCommon[index], (void*)Buf, length ); if( (KEY0 == m_DiscovermsgCommon[index].key[0]) && (KEY1 == m_DiscovermsgCommon[index].key[1]) && (indexlistWidget->addItem(str); //place formated information in listbox } } } ////////////////////////////////////////////////////////////////////////////// //Called when someone double clicks on a listbox entry for viewing/editing ////////////////////////////////////////////////////////////////////////////// void CSdrDiscoverDlg::OnItemDoubleClick( QListWidgetItem * item ) { Q_UNUSED(item); accept(); } void CSdrDiscoverDlg::accept() { //OK was pressed so get all data from edit controls int index = ui->listWidget->currentRow(); if(index>=0) { m_Name = m_DiscovermsgCommon[index].name; m_Port = m_DiscovermsgCommon[index].port[1]; m_Port <<= 8; m_Port |= m_DiscovermsgCommon[index].port[0]; m_IPAdr = m_DiscovermsgCommon[index].ipaddr[3]; m_IPAdr<<=8; m_IPAdr += m_DiscovermsgCommon[index].ipaddr[2]; m_IPAdr<<=8; m_IPAdr += m_DiscovermsgCommon[index].ipaddr[1]; m_IPAdr<<=8; m_IPAdr += m_DiscovermsgCommon[index].ipaddr[0]; } else { m_Name = ""; } QDialog::accept(); //call base class to exit } cutesdr-1.0.5/gui/testbench.h0000644000175000017500000001210111711303214016026 0ustar bottomsbottoms////////////////////////////////////////////////////////////////////// // testbench.h: interface of the CTestBench class. // // This class creates a test bench dialog that generates complex // signals and displays complex data and spectrum for testing and debug // // History: // 2010-12-18 Initial creation MSW // 2011-03-27 Initial release ////////////////////////////////////////////////////////////////////// #ifndef TESTBENCH_H #define TESTBENCH_H #include #include #include #include #include "dsp/datatypes.h" #include "dsp/fft.h" #include "dsp/wfmmod.h" extern double g_TestValue; ////////////////////////////////////////////////////////////////////// // global defines ////////////////////////////////////////////////////////////////////// // Profile Defines. Used to select various test points //within the program #define PROFILE_OFF 0 #define PROFILE_1 1 #define PROFILE_2 2 #define PROFILE_3 3 #define PROFILE_4 4 #define PROFILE_5 5 #define PROFILE_6 6 #define PROFILE_7 7 #define NUM_PROFILES 8 #define TEST_FFTSIZE 2048 #define TB_HORZ_DIVS 10 #define TB_MAX_SCREENSIZE 2048 typedef union { struct bs { unsigned char b0; unsigned char b1; unsigned char b2; unsigned char b3; }bytes; int all; }tBtoL2; namespace Ui { class CTestBench; } class CTestBench : public QDialog { Q_OBJECT public: explicit CTestBench(QWidget *parent = 0); ~CTestBench(); void Init(); //called to initialize controls after setting all class variables void CreateGeneratorSamples(int length, TYPECPX* pBuf, double samplerate); void CreateGeneratorSamples(int length, TYPEREAL* pBuf, double samplerate); // overloaded data display routines void DisplayData(int n, TYPEREAL* pBuf, double samplerate, int profile); void DisplayData(int n, TYPECPX* pBuf, double samplerate, int profile); void DisplayData(int n, TYPEMONO16* pBuf, double samplerate, int profile); void DisplayData(int n, TYPESTEREO16* pBuf, double samplerate, int profile); void SendDebugTxt(QString Str){ if(m_Active) emit SendTxt(Str);} //Exposed Dialog Class variables for persistant saving/restoring by parent bool m_TimeDisplay; bool m_GenOn; bool m_PeakOn; bool m_NewDataIsCpx; bool m_CurrentDataIsCpx; bool m_UseFmGen; int m_Profile; int m_TrigIndex; int m_DisplayRate; int m_HorzSpan; int m_VertRange; int m_TrigLevel; double m_PulseWidth; double m_PulsePeriod; double m_SignalPower; double m_NoisePower; double m_SweepStartFrequency; double m_SweepStopFrequency; double m_SweepRate; public slots: void Reset(); //called by GUI Reset button void DrawFftPlot(); //called to draw new fft data onto screen plot void DrawTimePlot(); //called to draw new Time data onto screen plot void GotTxt(QString); void OnTimer(); void OnGenOn(bool On); void OnFmGen(bool On); void OnTimeDisplay(bool timemode); void OnEnablePeak(bool enablepeak); void OnSweepStart(int start); void OnSweepStop(int stop); void OnSweepRate(int rate); void OnDisplayRate(int rate); void OnVertRange(int range); void OnHorzSpan(int span); void OnTrigLevel(int level); void OnTriggerMode(int trigindex); void OnProfile(int profindex); void OnPulseWidth(int pwidth); void OnPulsePeriod(int pperiod); void OnSignalPwr(int pwr); void OnNoisePwr(int pwr); void OnTestSlider1(int val); signals: void ResetSignal(); //internal signals from worker thread called functions void NewFftData(); void NewTimeData(); void SendTxt(QString); protected: //re-implemented widget event handlers void resizeEvent(QResizeEvent* event); void paintEvent(QPaintEvent *event); void closeEvent(QCloseEvent *event); void showEvent(QShowEvent *event); private: Ui::CTestBench *ui; void DrawFreqOverlay(); void DrawTimeOverlay(); void MakeFrequencyStrs(); void ChkForTrigger(qint32 sample); quint64 rdtsctime(); QPixmap m_2DPixmap; QPixmap m_OverlayPixmap; QSize m_Size; QRect m_Rect; QTimer *m_pTimer; bool m_Active; qint32 m_Span; qint32 m_MaxdB; qint32 m_MindB; qint32 m_dBStepSize; qint32 m_FreqUnits; qint64 m_CenterFreq; double m_GenSampleRate; double m_DisplaySampleRate; QString m_Str; QString m_HDivText[TB_HORZ_DIVS+1]; TYPECPX m_FftInBuf[TEST_FFTSIZE]; qint32 m_FftPkBuf[TB_MAX_SCREENSIZE]; qint32 m_TimeBuf1[TB_MAX_SCREENSIZE]; qint32 m_TimeBuf2[TB_MAX_SCREENSIZE]; qint32 m_TimeScrnBuf1[TB_MAX_SCREENSIZE]; qint32 m_TimeScrnBuf2[TB_MAX_SCREENSIZE]; qint32 m_PreviousSample; int m_FftBufPos; qint32 m_DisplaySkipValue; qint32 m_DisplaySkipCounter; int m_TimeScrnPos; int m_TimeInPos; int m_TrigBufPos; int m_TrigState; int m_TrigCounter; int m_PostScrnCaptureLength; double m_TimeScrnPixel; double m_SweepFrequency; double m_SweepFreqNorm; double m_SweepAcc; double m_SweepRateInc; double m_SignalAmplitude; double m_NoiseAmplitude; double m_PulseTimer; CFft m_Fft; QFile m_File; CWFmMod* m_pWFmMod; }; extern CTestBench* g_pTestBench; #endif // TESTBENCH_H cutesdr-1.0.5/gui/main.cpp0000644000175000017500000000047611546075374015365 0ustar bottomsbottoms#include #include "gui/mainwindow.h" int main(int argc, char *argv[]) { // QApplication::setGraphicsSystem("native"); QApplication::setGraphicsSystem("raster"); // QApplication::setGraphicsSystem("opengl"); QApplication a(argc, argv); MainWindow w; w.show(); return a.exec(); } cutesdr-1.0.5/gui/editnetdlg.ui0000644000175000017500000001260711711303214016373 0ustar bottomsbottoms EditNetDlg 0 0 419 141 0 0 10 75 true Network Setup 30 100 341 32 Qt::Horizontal QDialogButtonBox::Cancel|QDialogButtonBox::Ok 10 20 111 40 75 true TCP IP Adr Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter 130 70 71 20 10 TCP Control Port Number 5 40 70 81 21 75 true TCP Port Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter 130 20 170 40 8 50 false Qt::NoFocus TCP Control IP Address QFrame::StyledPanel QFrame::Sunken 1 310 20 101 41 Find SDRs 210 70 101 20 SDRxx Qt::AlignCenter CIPEditWidget QFrame
gui/ipeditwidget.h
1
IPEditwidget_IP lineEdit_TCPPort buttonBox buttonBox accepted() EditNetDlg accept() 248 254 157 140 buttonBox rejected() EditNetDlg reject() 316 260 286 140 pushButtonFindSDR clicked() EditNetDlg FindSdrs() 369 43 387 73 FindSdrs()
cutesdr-1.0.5/gui/aboutdlg.h0000644000175000017500000000116611546075374015704 0ustar bottomsbottoms////////////////////////////////////////////////////////////////////// // aboutdlg.h: interface for the CAboutDlg class. // // History: // 2010-09-15 Initial creation MSW // 2011-03-27 Initial release ///////////////////////////////////////////////////////////////////// #ifndef ABOUTDLG_H #define ABOUTDLG_H #include namespace Ui { class CAboutDlg; } class CAboutDlg : public QDialog { Q_OBJECT public: explicit CAboutDlg(QWidget *parent=0, QString Revision=""); ~CAboutDlg(); private: QString m_Revision; Ui::CAboutDlg *ui; QString m_Str; }; #endif // ABOUTDLG_H cutesdr-1.0.5/gui/ipeditwidget.h0000644000175000017500000000237611546075374016571 0ustar bottomsbottoms////////////////////////////////////////////////////////////////////// // ipeditwidget.h: interface for the CIPEditWidget class. // // History: // 2010-09-15 Initial creation MSW // 2011-03-27 Initial release ///////////////////////////////////////////////////////////////////// #ifndef IPEDITWIDGET_H #define IPEDITWIDGET_H #include #include "ui_ipeditframe.h" class CIPEditWidget : public QFrame { Q_OBJECT public: explicit CIPEditWidget(QWidget *parent = 0); ~CIPEditWidget(); void SetIP(quint32 val); void SetIP(quint8 b3, quint8 b2, quint8 b1, quint8 b0); bool GetIP(quint8& b3, quint8& b2, quint8& b1, quint8& b0); bool GetIP(quint32& ip); quint32 GetIP32(){GetControlValues(); return m_ip;} quint8 GetIP0(){GetControlValues(); return m_ip0;} quint8 GetIP1(){GetControlValues(); return m_ip1;} quint8 GetIP2(){GetControlValues(); return m_ip2;} quint8 GetIP3(){GetControlValues(); return m_ip3;} signals: public slots: private slots: protected: private: Ui::FrameIPdit ui; void UpdateControls(); void GetControlValues(); QIntValidator* m_pIPAddressValidator; bool m_Dirty; quint32 m_ip; quint8 m_ip0; quint8 m_ip1; quint8 m_ip2; quint8 m_ip3; }; #endif // IPEDITWIDGET_H cutesdr-1.0.5/gui/testbench.cpp0000644000175000017500000011213011711303214016364 0ustar bottomsbottoms////////////////////////////////////////////////////////////////////// // testbench.cpp: implementation of the CTestBench class. // // This class creates a test bench dialog that generates complex // signals and displays complex data and spectrum for testing and debug // // History: // 2010-12-18 Initial creation MSW // 2011-03-27 Initial release // 2011-08-07 Added WFM test generator ////////////////////////////////////////////////////////////////////// //========================================================================================== // + + + This Software is released under the "Simplified BSD License" + + + //Copyright 2010 Moe Wheatley. All rights reserved. // //Redistribution and use in source and binary forms, with or without modification, are //permitted provided that the following conditions are met: // // 1. Redistributions of source code must retain the above copyright notice, this list of // conditions and the following disclaimer. // // 2. Redistributions in binary form must reproduce the above copyright notice, this list // of conditions and the following disclaimer in the documentation and/or other materials // provided with the distribution. // //THIS SOFTWARE IS PROVIDED BY Moe Wheatley ``AS IS'' AND ANY EXPRESS OR IMPLIED //WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND //FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL Moe Wheatley OR //CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR //CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR //SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON //ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING //NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF //ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // //The views and conclusions contained in the software and documentation are those of the //authors and should not be interpreted as representing official policies, either expressed //or implied, of Moe Wheatley. //========================================================================================== #include "testbench.h" #include "ui_testbench.h" #include CTestBench* g_pTestBench = NULL; //pointer to this class is global so everybody can access double g_TestValue= 0.0; #define USE_FILE 0 //#define FILE_NAME "SSB-7210000Hz_001.wav" #define FILE_NAME "fmstereo250Khz.wav" #define USE_SVFILE 1 #define USE_PERSEUSFILE 0 ////////////////////////////////////////////////////////////////////// // Local Defines ////////////////////////////////////////////////////////////////////// #define TB_VERT_DIVS 14 //18 //specify grid screen divisions #define TB_TIMEVERT_DIVS 10 //specify time display grid screen divisions #define MAX_AMPLITUDE 32767.0 #define FFT_AVE 2 #define TRIG_OFF 0 #define TRIG_PNORM 1 #define TRIG_PSINGLE 2 #define TRIG_NNORM 3 #define TRIG_NSINGLE 4 #define TRIGSTATE_WAIT 0 #define TRIGSTATE_CAPTURE 1 #define TRIGSTATE_DISPLAY 2 #define TRIGSTATE_WAITDISPLAY 3 //list of defined profiles const char* PROF_STR[NUM_PROFILES] = { "Off", "PreFilter", "PostFilter", "PostAGC", "PostDemod", "Soundcard", "Profile 6", "Noise Blanker" }; ////////////////////////////////////////////////////////////////////// // Construction/Destruction ////////////////////////////////////////////////////////////////////// CTestBench::CTestBench(QWidget *parent) : QDialog(parent), ui(new Ui::CTestBench) { m_Active = false; m_2DPixmap = QPixmap(0,0); m_OverlayPixmap = QPixmap(0,0); m_Size = QSize(0,0); m_Rect = QRect(0,0,100,100); m_MaxdB = 0; m_MindB = -120; m_dBStepSize = 10; m_FreqUnits = 1; m_CenterFreq = 0; m_GenSampleRate = 1; m_DisplaySampleRate = 1; m_Span = m_DisplaySampleRate/2; m_FftBufPos = 0; m_GenOn = false; m_PeakOn = false; m_NewDataIsCpx = false; m_CurrentDataIsCpx = true; m_TimeDisplay = false; m_DisplayRate = 10; m_HorzSpan = 100; m_VertRange = 65000; m_TrigLevel = 100; m_TrigBufPos = 0; m_TrigCounter = 0; m_TrigState = TRIGSTATE_WAIT; m_PulseWidth = .01; m_PulsePeriod = .5; m_PulseTimer = 0.0; m_pWFmMod = NULL; connect(this, SIGNAL(ResetSignal()), this, SLOT( Reset() ) ); connect(this, SIGNAL(NewFftData()), this, SLOT( DrawFftPlot() ) ); connect(this, SIGNAL(NewTimeData()), this, SLOT( DrawTimePlot() ) ); connect( this, SIGNAL( SendTxt(QString)), this, SLOT( GotTxt(QString) ) ); m_Fft.SetFFTParams( 2048, FALSE, 0.0, m_GenSampleRate); m_Fft.SetFFTAve(FFT_AVE); ui->setupUi(this); setWindowTitle("CuteSDR Test Bench"); ui->textEdit->clear(); m_pTimer = new QTimer(this); connect(m_pTimer, SIGNAL(timeout()), this, SLOT(OnTimer())); #if USE_FILE //test file reading kludge QDir::setCurrent("d:/"); m_File.setFileName(FILE_NAME); if(m_File.open(QIODevice::ReadOnly)) { qDebug()<<"file Opend OK"; if(USE_SVFILE) m_File.seek(0x7e); //SV else if(USE_PERSEUSFILE) m_File.seek(0x7A); //perseus } else qDebug()<<"file Failed to Open"; #endif m_pWFmMod = new CWFmMod(); } CTestBench::~CTestBench() { if(m_File.isOpen()) m_File.close(); if(m_pWFmMod) delete m_pWFmMod; delete ui; } ////////////////////////////////////////////////////////////////////// // Handle window close and show events ////////////////////////////////////////////////////////////////////// void CTestBench::closeEvent(QCloseEvent *event) { Q_UNUSED(event); m_Active = false; m_pTimer->stop(); //stop timer } void CTestBench::showEvent(QShowEvent *event) { Q_UNUSED(event); m_Active = true; m_pTimer->start(500); //start up timer } void CTestBench::OnTestSlider1(int val) { g_TestValue = (double)val/100.0; qDebug()<<"Test Val = "<comboBoxTrig->clear(); ui->comboBoxTrig->addItem("Free Run"); ui->comboBoxTrig->addItem("Pos Edge"); ui->comboBoxTrig->addItem("Pos Single"); ui->comboBoxTrig->addItem("Neg Edge"); ui->comboBoxTrig->addItem("Neg Single"); m_TrigIndex = tmp; ui->comboBoxTrig->setCurrentIndex(m_TrigIndex); tmp = m_Profile; for(int i=0; icomboBoxProfile->addItem(PROF_STR[i],i); m_Profile = tmp; ui->comboBoxProfile->setCurrentIndex(m_Profile); ui->spinBoxStart->setValue(m_SweepStartFrequency/1000.0); ui->spinBoxStop->setValue(m_SweepStopFrequency/1000.0); ui->spinBoxSweep->setValue(m_SweepRate); ui->checkBoxTime->setChecked(m_TimeDisplay); ui->spinBoxHorzSpan->setValue(m_HorzSpan); ui->spinBoxThresh->setValue(m_TrigLevel); ui->spinBoxVertRange->setValue(m_VertRange); ui->spinBoxRate->setValue(m_DisplayRate); ui->checkBoxGen->setChecked(m_GenOn); ui->checkBoxFm->setChecked(m_UseFmGen); ui->checkBoxPeak->setChecked(m_PeakOn); ui->spinBoxAmp->setValue((int)m_SignalPower); ui->spinBoxNoise->setValue((int)m_NoisePower); ui->spinBoxPulseWidth->setValue((int)(1000.0*m_PulseWidth)); ui->spinBoxPulsePeriod->setValue((int)(1000.0*m_PulsePeriod)); Reset(); } ////////////////////////////////////////////////////////////////////// // Bunch of Control Slot functions called when a control value changes ////////////////////////////////////////////////////////////////////// void CTestBench::OnSweepStart(int start) { m_SweepStartFrequency = (double)start*1000.0; m_SweepFrequency = m_SweepStartFrequency; m_SweepAcc = 0.0; if(m_UseFmGen) m_pWFmMod->SetSweep(m_SweepFreqNorm,m_SweepFrequency,m_SweepStopFrequency,m_SweepRateInc); } void CTestBench::OnSweepStop(int stop) { m_SweepStopFrequency = (double)stop*1000.0; m_SweepFrequency = m_SweepStartFrequency; m_SweepAcc = 0.0; if(m_UseFmGen) m_pWFmMod->SetSweep(m_SweepFreqNorm,m_SweepFrequency,m_SweepStopFrequency,m_SweepRateInc); } void CTestBench::OnSweepRate(int rate) { m_SweepRate = (double)rate; // Hz/sec m_SweepAcc = 0.0; m_SweepRateInc = m_SweepRate/m_GenSampleRate; if(m_UseFmGen) m_pWFmMod->SetSweep(m_SweepFreqNorm,m_SweepFrequency,m_SweepStopFrequency,m_SweepRateInc); } void CTestBench::OnDisplayRate(int rate) { m_DisplayRate = rate; if(m_TimeDisplay) { double capturesize = ( (double)m_HorzSpan*m_DisplaySampleRate/1000.0); m_DisplaySkipValue = m_DisplaySampleRate/(capturesize*m_DisplayRate); } else { m_DisplaySkipValue = m_DisplaySampleRate/(TEST_FFTSIZE*m_DisplayRate); } } void CTestBench::OnVertRange(int range ) { m_VertRange = range; ui->spinBoxThresh->setMaximum(m_VertRange/2); ui->spinBoxThresh->setMinimum(-m_VertRange/2); if(m_TimeDisplay) DrawTimeOverlay(); } void CTestBench::OnHorzSpan(int span) { m_HorzSpan = span; if(m_TimeDisplay) { DrawTimeOverlay(); double capturesize = ( (double)m_HorzSpan*m_DisplaySampleRate/1000.0); m_DisplaySkipValue = m_DisplaySampleRate/(capturesize*m_DisplayRate); m_TimeScrnPixel = .001* (double)((double)m_HorzSpan/(double)m_Rect.width()); //time per pixel on screen in seconds } } void CTestBench::OnTimeDisplay(bool timemode) { m_TimeDisplay = timemode; Reset(); } void CTestBench::OnTriggerMode(int trigindex) { m_TrigIndex = trigindex; Reset(); } void CTestBench::OnTrigLevel(int level) { m_TrigLevel = level; if(m_TimeDisplay) DrawTimeOverlay(); } void CTestBench::OnProfile(int profindex) { m_Profile = profindex; } void CTestBench::OnGenOn(bool On) { m_GenOn = On; } void CTestBench::OnFmGen(bool On) { m_UseFmGen = On; } void CTestBench::OnPulseWidth(int pwidth) { m_PulseWidth = (double)pwidth * .001; } void CTestBench::OnPulsePeriod(int pperiod) { m_PulsePeriod = (double)pperiod * .001; } void CTestBench::OnSignalPwr(int pwr) { m_SignalPower = pwr; m_SignalAmplitude = MAX_AMPLITUDE*pow(10.0, m_SignalPower/20.0); } void CTestBench::OnNoisePwr(int pwr) { m_NoisePower = pwr; m_NoiseAmplitude = MAX_AMPLITUDE*pow(10.0, m_NoisePower/20.0); } void CTestBench::OnEnablePeak(bool enablepeak) { for( int i=0; iSetSampleRate(m_GenSampleRate); m_pWFmMod->SetSweep(m_SweepFreqNorm,m_SweepFrequency,m_SweepStopFrequency,m_SweepRateInc); } emit ResetSignal(); } #if USE_FILE //test file reading kludge char buf[16384]; if(m_File.atEnd()) { if(USE_SVFILE) m_File.seek(0x7e); //SV else if(USE_PERSEUSFILE) m_File.seek(0x7A); //perseus return; } #if 1 //24 bit data if(m_File.read(buf,6*length)<=0) return; int j=0; for(i=0; iGenerateData(length, m_SignalAmplitude, pBuf); for(i=0; i 0.0) { //if pulse width is >0 create pulse modulation m_PulseTimer += (1.0/m_GenSampleRate); if(m_PulseTimer > m_PulsePeriod) m_PulseTimer = 0.0; if(m_PulseTimer > m_PulseWidth) amp = 0.0; } #if 0 //way to skip over passband for filter alias testing if( (m_SweepFrequency>-31250) && (m_SweepFrequency<31250) ) { m_SweepFrequency = 31250; amp = 0.0; m_Fft.ResetFFT(); m_DisplaySkipCounter = -2; } #endif if(!m_UseFmGen) { //create complex sin/cos signal pBuf[i].re = amp*cos(m_SweepAcc); pBuf[i].im = amp*sin(m_SweepAcc); //inc phase accummulator with normalized freqeuency step m_SweepAcc += ( m_SweepFrequency*m_SweepFreqNorm ); m_SweepFrequency += m_SweepRateInc; //inc sweep frequency if(m_SweepFrequency >= m_SweepStopFrequency) //reached end of sweep? m_SweepRateInc = 0.0; //stop sweep when end is reached // m_SweepFrequency = m_SweepStartFrequency; //restart sweep when end is reached } ////////////////// Gaussian Noise generator // Generate two uniform random numbers between -1 and +1 // that are inside the unit circle if(m_NoisePower > -160.0) { //create and add noise samples to signal do { u1 = 1.0 - 2.0 * (double)rand()/(double)RAND_MAX ; u2 = 1.0 - 2.0 * (double)rand()/(double)RAND_MAX ; r = u1*u1 + u2*u2; } while(r >= 1.0 || r == 0.0); rad = sqrt(-2.0*log(r)/r); //add noise samples to generator output pBuf[i].re += (m_NoiseAmplitude*u1*rad); pBuf[i].im += (m_NoiseAmplitude*u2*rad); } } m_SweepAcc = (double)fmod((double)m_SweepAcc, K_2PI); //keep radian counter bounded } ////////////////////////////////////////////////////////////////////// // Call to Create 'length' real sweep/pulse/noise generator samples //and place in users pBuf. //Called by thread so no GUI calls! ////////////////////////////////////////////////////////////////////// void CTestBench::CreateGeneratorSamples(int length, TYPEREAL* pBuf, double samplerate) { int i; double rad; double r; double u1; double u2; if(!m_Active || !m_GenOn) return; if(m_GenSampleRate != samplerate) { //reset things if sample rate changes on the fly m_GenSampleRate = samplerate; emit ResetSignal(); } for(i=0; i 0.0) { //if pulse width is >0 create pulse modulation m_PulseTimer += (1.0/m_GenSampleRate); if(m_PulseTimer > m_PulsePeriod) m_PulseTimer = 0.0; if(m_PulseTimer > m_PulseWidth) amp = 0.0; } #if 0 //way to skip over passband for filter alias testing if( (m_SweepFrequency>-31250) && (m_SweepFrequency<31250) ) { m_SweepFrequency = 31250; amp = 0.0; m_Fft.ResetFFT(); m_DisplaySkipCounter = -2; } #endif //create cos signal pBuf[i] = 3.0*amp*cos(m_SweepAcc); //inc phase accummulator with normalized freqeuency step m_SweepAcc += ( m_SweepFrequency*m_SweepFreqNorm ); m_SweepFrequency += m_SweepRateInc; //inc sweep frequency if(m_SweepFrequency >= m_SweepStopFrequency) //reached end of sweep? m_SweepRateInc = 0.0; //stop sweep when end is reached // m_SweepFrequency = m_SweepStartFrequency; //restart sweep when end is reached ////////////////// Gaussian Noise generator // Generate two uniform random numbers between -1 and +1 // that are inside the unit circle if(m_NoisePower > -160.0) { //create and add noise samples to signal do { u1 = 1.0 - 2.0 * (double)rand()/(double)RAND_MAX ; u2 = 1.0 - 2.0 * (double)rand()/(double)RAND_MAX ; r = u1*u1 + u2*u2; } while(r >= 1.0 || r == 0.0); rad = sqrt(-2.0*log(r)/r); //add noise samples to generator output pBuf[i] += (m_NoiseAmplitude*u1*rad); } } m_SweepAcc = (double)fmod((double)m_SweepAcc, K_2PI); //keep radian counter bounded } ////////////////////////////////////////////////////////////////////// // Called to reset the generator and display peaks //recalculates a bunch of variables based on settings ////////////////////////////////////////////////////////////////////// void CTestBench::Reset() { int i; //initialize sweep generator values m_SweepFrequency = m_SweepStartFrequency; m_SweepFreqNorm = K_2PI/m_GenSampleRate; m_SweepAcc = 0.0; m_SweepRateInc = m_SweepRate/m_GenSampleRate; if(m_UseFmGen) m_pWFmMod->SetSweep(m_SweepFreqNorm,m_SweepFrequency,m_SweepStopFrequency,m_SweepRateInc); m_SignalAmplitude = MAX_AMPLITUDE*pow(10.0, m_SignalPower/20.0); m_NoiseAmplitude = MAX_AMPLITUDE*pow(10.0, m_NoisePower/20.0); //init FFT values m_Fft.SetFFTParams( TEST_FFTSIZE, FALSE, 0.0, m_DisplaySampleRate); m_FftBufPos = 0; m_Span = m_DisplaySampleRate; m_Span = ( m_Span - (m_Span+5)%10 + 5); //init time display values m_TimeScrnPixel = .001* (double)((double)m_HorzSpan/(double)m_Rect.width()); //time per pixel on screen in seconds m_TimeScrnPos = 0; m_TimeInPos = 0; m_PreviousSample = 0; m_PostScrnCaptureLength = (7*m_Rect.width())/10; //sets trigger position m_TrigState = TRIGSTATE_WAIT; for(i=0; itextEdit->clear(); m_Fft.ResetFFT(); m_DisplaySkipCounter = -2; m_PulseTimer = 0.0; } ////////////////////////////////////////////////////////////////////// // Called to display the input pBuf. //Called by thread so no GUI calls! // COMPLEX Data version. ////////////////////////////////////////////////////////////////////// void CTestBench::DisplayData(int length, TYPECPX* pBuf, double samplerate, int profile) { if(!m_Active || (profile!=m_Profile) ) return; if(m_DisplaySampleRate != samplerate) { m_DisplaySampleRate = samplerate; emit ResetSignal(); return; } m_NewDataIsCpx = true; if(! m_TimeDisplay) { //if displaying frequency domain data //accumulate samples into m_FftInBuf until have enough to perform an FFT for(int i=0; i= TEST_FFTSIZE ) { m_FftBufPos = 0; if(++m_DisplaySkipCounter >= m_DisplaySkipValue ) { m_DisplaySkipCounter = 0; m_Fft.PutInDisplayFFT( TEST_FFTSIZE, m_FftInBuf); emit NewFftData(); } } } } else { //if displaying time domain data for(int i=0; i= scrntime) { ChkForTrigger( (int)pBuf[i].re ); m_TimeBuf1[m_TimeScrnPos] = (int)pBuf[i].re; m_TimeBuf2[m_TimeScrnPos++] = (int)pBuf[i].im; scrntime = (double)m_TimeScrnPos*m_TimeScrnPixel; if( m_TimeScrnPos >= m_Rect.width() ) { m_TimeScrnPos = 0; m_TimeInPos = 0; break; } } } } } ////////////////////////////////////////////////////////////////////// // Called to display the input pBuf. //Called by thread so no GUI calls! // REAL Data version. ////////////////////////////////////////////////////////////////////// void CTestBench::DisplayData(int length, TYPEREAL* pBuf, double samplerate, int profile) { if(!m_Active || (profile!=m_Profile) ) return; if(m_DisplaySampleRate != samplerate) { m_DisplaySampleRate = samplerate; emit ResetSignal(); return; } m_NewDataIsCpx = false; if(!m_TimeDisplay) { //if displaying frequency domain data //accumulate samples into m_FftInBuf until have enough to perform an FFT for(int i=0; i= TEST_FFTSIZE ) { m_FftBufPos = 0; if(++m_DisplaySkipCounter >= m_DisplaySkipValue ) { m_DisplaySkipCounter = 0; m_Fft.PutInDisplayFFT( TEST_FFTSIZE, m_FftInBuf); emit NewFftData(); } } } } else { for(int i=0; i= scrntime) { ChkForTrigger( (int)pBuf[i] ); m_TimeBuf1[m_TimeScrnPos] = (int)pBuf[i]; m_TimeBuf2[m_TimeScrnPos++] = 0; scrntime = (double)m_TimeScrnPos*m_TimeScrnPixel; if( m_TimeScrnPos >= m_Rect.width() ) { m_TimeScrnPos = 0; m_TimeInPos = 0; break; } } } } } ////////////////////////////////////////////////////////////////////// // Called to display the input pBuf. //Called by thread so no GUI calls! // MONO 16 bit Data version. ////////////////////////////////////////////////////////////////////// void CTestBench::DisplayData(int length, TYPEMONO16* pBuf, double samplerate, int profile) { if(!m_Active || (profile!=m_Profile) ) return; if(m_DisplaySampleRate != samplerate) { m_DisplaySampleRate = samplerate; emit ResetSignal(); return; } m_NewDataIsCpx = false; if(!m_TimeDisplay) { //if displaying frequency domain data //accumulate samples into m_FftInBuf until have enough to perform an FFT for(int i=0; i= TEST_FFTSIZE ) { m_FftBufPos = 0; if(++m_DisplaySkipCounter >= m_DisplaySkipValue ) { m_DisplaySkipCounter = 0; m_Fft.PutInDisplayFFT( TEST_FFTSIZE, m_FftInBuf); emit NewFftData(); } } } } else { for(int i=0; i= scrntime) { ChkForTrigger( (int)pBuf[i<<1] ); m_TimeBuf1[m_TimeScrnPos] = (int)pBuf[i]; m_TimeBuf2[m_TimeScrnPos++] = 0; scrntime = (double)m_TimeScrnPos*m_TimeScrnPixel; if( m_TimeScrnPos >= m_Rect.width() ) { m_TimeScrnPos = 0; m_TimeInPos = 0; break; } } } } } ////////////////////////////////////////////////////////////////////// // Called to display the input pBuf. //Called by thread so no GUI calls! // STEREO 16 bit Data version. ////////////////////////////////////////////////////////////////////// void CTestBench::DisplayData(int length, TYPESTEREO16* pBuf, double samplerate, int profile) { if(!m_Active || (profile!=m_Profile) ) return; if(m_DisplaySampleRate != samplerate) { m_DisplaySampleRate = samplerate; emit ResetSignal(); return; } m_NewDataIsCpx = true; if(! m_TimeDisplay) { //if displaying frequency domain data //accumulate samples into m_FftInBuf until have enough to perform an FFT for(int i=0; i= TEST_FFTSIZE ) { m_FftBufPos = 0; if(++m_DisplaySkipCounter >= m_DisplaySkipValue ) { m_DisplaySkipCounter = 0; m_Fft.PutInDisplayFFT( TEST_FFTSIZE, m_FftInBuf); emit NewFftData(); } } } } else { //if displaying time domain data for(int i=0; i= scrntime) { ChkForTrigger( (int)pBuf[i].re ); m_TimeBuf1[m_TimeScrnPos] = pBuf[i].re; m_TimeBuf2[m_TimeScrnPos++] = pBuf[i].im; scrntime = (double)m_TimeScrnPos*m_TimeScrnPixel; if( m_TimeScrnPos >= m_Rect.width() ) { m_TimeScrnPos = 0; m_TimeInPos = 0; break; } } } } } ////////////////////////////////////////////////////////////////////// // Called to check for O'Scope display trigger condition and manage trigger capture logic. ////////////////////////////////////////////////////////////////////// void CTestBench::ChkForTrigger(qint32 sample) { switch(m_TrigIndex) { case TRIG_OFF: if( 0 == m_TimeScrnPos ) { if(( ++m_DisplaySkipCounter >= m_DisplaySkipValue ) && ( m_DisplaySkipCounter > 2) ) { m_DisplaySkipCounter = 0; m_TrigBufPos = 0; m_TrigState = TRIGSTATE_DISPLAY; } } break; case TRIG_PNORM: case TRIG_PSINGLE: if(TRIGSTATE_WAIT == m_TrigState) { //look for positive transition through trigger level if( (sample>=m_TrigLevel) && (m_PreviousSample= m_PostScrnCaptureLength ) { m_TrigState = TRIGSTATE_DISPLAY; m_TrigCounter = 0; } } break; case TRIG_NNORM: case TRIG_NSINGLE: if(TRIGSTATE_WAIT == m_TrigState) { //look for negative transition through trigger level if( (sample<=m_TrigLevel) && (m_PreviousSample>m_TrigLevel) ) { m_TrigBufPos = m_TimeScrnPos; m_TrigState = TRIGSTATE_CAPTURE; m_TrigCounter = 0; } } else if(TRIGSTATE_CAPTURE == m_TrigState) { if(++m_TrigCounter >= m_PostScrnCaptureLength ) { m_TrigState = TRIGSTATE_DISPLAY; m_TrigCounter = 0; } } break; default: break; } if(TRIGSTATE_DISPLAY == m_TrigState) { m_TrigState = TRIGSTATE_WAITDISPLAY; //copy into screen display buffers int w = m_2DPixmap.width(); int bufpos = m_TrigBufPos + m_PostScrnCaptureLength - w; if(bufpos<0) bufpos = w + bufpos; for(int i=0; i=w) bufpos = 0; } emit NewTimeData(); } m_PreviousSample = sample; } ////////////////////////////////////////////////////////////////////// // Called to display text in debug text box. ////////////////////////////////////////////////////////////////////// void CTestBench::GotTxt(QString Str) { ui->textEdit->append(Str); } ///////////////////////////////////////////////////////////////////// // Status Timer event handler ///////////////////////////////////////////////////////////////////// void CTestBench::OnTimer() { //update current sweep frequency text ui->labelFreq->setText(QString().setNum((int)m_SweepFrequency)); } ////////////////////////////////////////////////////////////////////// // Called when screen size changes so must recalculate bitmaps ////////////////////////////////////////////////////////////////////// void CTestBench::resizeEvent(QResizeEvent* ) { if(!ui->frameGraph->size().isValid()) return; if( m_Rect != ui->frameGraph->geometry()) { //if changed, resize pixmaps to new frame size m_Rect = ui->frameGraph->geometry(); m_OverlayPixmap = QPixmap(m_Rect.width(), m_Rect.height()); m_OverlayPixmap.fill(Qt::black); m_2DPixmap = QPixmap(m_Rect.width(), m_Rect.height()); m_2DPixmap.fill(Qt::black); Reset(); } if(m_TimeDisplay) DrawTimeOverlay(); else DrawFreqOverlay(); } ////////////////////////////////////////////////////////////////////// // Called by QT when screen needs to be redrawn ////////////////////////////////////////////////////////////////////// void CTestBench::paintEvent(QPaintEvent *) { QPainter painter(this); painter.drawPixmap(m_Rect, m_2DPixmap); return; } ////////////////////////////////////////////////////////////////////// // Called to update time domain data for displaying on the screen ////////////////////////////////////////////////////////////////////// void CTestBench::DrawTimePlot() { int i; int w; int h; QPoint LineBuf[TB_MAX_SCREENSIZE]; if(m_2DPixmap.isNull()) return; if(m_NewDataIsCpx != m_CurrentDataIsCpx) { m_CurrentDataIsCpx = m_NewDataIsCpx; } //get/draw the 2D spectrum w = m_2DPixmap.width(); h = m_2DPixmap.height(); //first copy into 2Dbitmap the overlay bitmap. m_2DPixmap = m_OverlayPixmap.copy(0,0,w,h); QPainter painter2(&m_2DPixmap); //draw the time points int c = h/2; for(i=0; idp; j--) { if(m_HDivText[i][j] != '0') break; } if( (j-dp) > max) max = j-dp; } //truncate all strings to maximum fractional length StartFreq = m_CenterFreq - m_Span/2; for( i=0; i<=TB_HORZ_DIVS; i++) { freq = (float)StartFreq/(float)m_FreqUnits; m_HDivText[i].setNum(freq,'f', max); StartFreq += FreqPerDiv; } } cutesdr-1.0.5/gui/sounddlg.h0000644000175000017500000000173611546075374015725 0ustar bottomsbottoms////////////////////////////////////////////////////////////////////// // sounddlg.h: interface for the CSoundDlg class. // // History: // 2010-09-15 Initial creation MSW // 2011-03-27 Initial release ///////////////////////////////////////////////////////////////////// #ifndef SOUNDDLG_H #define SOUNDDLG_H #include #include "ui_sounddlg.h" class CSoundDlg : public QDialog { Q_OBJECT public: CSoundDlg(QWidget *parent=0); ~CSoundDlg(); void SetInputIndex(int x){ui.comboBoxSndIn->setCurrentIndex(x); } void SetOutputIndex(int x){ui.comboBoxSndOut->setCurrentIndex(x); } int GetInputIndex(){return ui.comboBoxSndIn->currentIndex(); } int GetOutputIndex(){return ui.comboBoxSndOut->currentIndex(); } void SetStereo(bool Stereo){ui.checkBoxStereo->setChecked(Stereo);} bool GetStereo(){return ui.checkBoxStereo->checkState();} public slots: signals: private slots: private: Ui::DialogSndCard ui; }; #endif // SOUNDDLG_H cutesdr-1.0.5/gui/sdrsetupdlg.ui0000644000175000017500000001611211711303214016603 0ustar bottomsbottoms CSdrSetupDlg 0 0 356 202 SDR Setup 150 150 191 32 Qt::Horizontal QDialogButtonBox::Cancel|QDialogButtonBox::Ok 10 40 201 111 Bandwidth / Sample Rate 20 60 171 17 500 KHz 20 40 171 17 100 KHz 20 20 171 17 50 KHz 20 80 171 17 2000 KHz 220 10 120 111 RF Gain/Attn 20 20 82 17 0 dB 20 40 82 17 -10 dB 20 60 82 17 -20 dB 20 80 82 17 -30 dB 40 10 121 21 QFrame::NoFrame Radio Type Qt::AlignCenter 230 130 121 17 Invert Spectrum 20 170 111 17 US FM Version buttonBox accepted() CSdrSetupDlg accept() 248 254 157 201 buttonBox rejected() CSdrSetupDlg reject() 316 260 286 201 radioButtonAttn10 clicked() CSdrSetupDlg RfGainChanged() 281 61 215 123 radioButtonAttn20 clicked() CSdrSetupDlg RfGainChanged() 247 81 230 129 radioButtonAttn30 clicked() CSdrSetupDlg RfGainChanged() 294 96 303 127 radioButtonAttn0 clicked() CSdrSetupDlg RfGainChanged() 300 36 346 53 SelectNetSDR() SelectSDRIQ() RfGainChanged() cutesdr-1.0.5/gui/aboutdlg.cpp0000644000175000017500000000544011546075374016236 0ustar bottomsbottoms///////////////////////////////////////////////////////////////////// // aboutdlg.cpp: implementation of the CDemodSetupDlg class. // // This class implements a dialog to displayprogram information // // History: // 2011-01-25 Initial creation MSW // 2011-03-27 Initial release ///////////////////////////////////////////////////////////////////// //========================================================================================== // + + + This Software is released under the "Simplified BSD License" + + + //Copyright 2010 Moe Wheatley. All rights reserved. // //Redistribution and use in source and binary forms, with or without modification, are //permitted provided that the following conditions are met: // // 1. Redistributions of source code must retain the above copyright notice, this list of // conditions and the following disclaimer. // // 2. Redistributions in binary form must reproduce the above copyright notice, this list // of conditions and the following disclaimer in the documentation and/or other materials // provided with the distribution. // //THIS SOFTWARE IS PROVIDED BY Moe Wheatley ``AS IS'' AND ANY EXPRESS OR IMPLIED //WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND //FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL Moe Wheatley OR //CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR //CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR //SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON //ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING //NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF //ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // //The views and conclusions contained in the software and documentation are those of the //authors and should not be interpreted as representing official policies, either expressed //or implied, of Moe Wheatley. //========================================================================================== #include "aboutdlg.h" #include "ui_aboutdlg.h" CAboutDlg::CAboutDlg(QWidget *parent, QString Revision) : QDialog(parent), m_Revision(Revision), ui(new Ui::CAboutDlg) { ui->setupUi(this); ui->labelTxt->clear(); m_Str = m_Revision; m_Str += "\n\r Copyright 2010 Moe Wheatley\n\r\n\r"; m_Str += "Example Interface Program for Networked\n\r"; m_Str += "RFSPACE Software Defined Receivers.\n\r"; m_Str += "Source Code and Documentation on http://sourceforge.net/projects/cutesdr/ \n\r"; m_Str += "Licensed under Simplified BSD License.\n\r"; ui->labelTxt->setText(m_Str); } CAboutDlg::~CAboutDlg() { delete ui; } cutesdr-1.0.5/gui/ipeditwidget.cpp0000644000175000017500000001036311546075374017117 0ustar bottomsbottoms///////////////////////////////////////////////////////////////////// // ipeditwidget.cpp: implementation of the CIPEditWidget class. // // This class implements a widget to set/modify an IPV4 address // // History: // 2010-09-15 Initial creation MSW // 2011-03-27 Initial release ///////////////////////////////////////////////////////////////////// //========================================================================================== // + + + This Software is released under the "Simplified BSD License" + + + //Copyright 2010 Moe Wheatley. All rights reserved. // //Redistribution and use in source and binary forms, with or without modification, are //permitted provided that the following conditions are met: // // 1. Redistributions of source code must retain the above copyright notice, this list of // conditions and the following disclaimer. // // 2. Redistributions in binary form must reproduce the above copyright notice, this list // of conditions and the following disclaimer in the documentation and/or other materials // provided with the distribution. // //THIS SOFTWARE IS PROVIDED BY Moe Wheatley ``AS IS'' AND ANY EXPRESS OR IMPLIED //WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND //FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL Moe Wheatley OR //CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR //CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR //SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON //ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING //NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF //ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // //The views and conclusions contained in the software and documentation are those of the //authors and should not be interpreted as representing official policies, either expressed //or implied, of Moe Wheatley. //========================================================================================== #include "gui/ipeditwidget.h" #include CIPEditWidget::CIPEditWidget(QWidget *parent) : QFrame(parent) { ui.setupUi(this); m_pIPAddressValidator = new QIntValidator(0, 255, this); ui.lineEdit_B0->setValidator(m_pIPAddressValidator); ui.lineEdit_B1->setValidator(m_pIPAddressValidator); ui.lineEdit_B2->setValidator(m_pIPAddressValidator); ui.lineEdit_B3->setValidator(m_pIPAddressValidator); m_Dirty = FALSE; m_ip = 0; } CIPEditWidget::~CIPEditWidget() { delete m_pIPAddressValidator; } void CIPEditWidget::SetIP(quint32 val) {//initialize control with IP address data in val m_ip = val; m_ip0 = val&0xFF; m_ip1 = (val>>8)&0xFF; m_ip2 = (val>>16)&0xFF; m_ip3 = (val>>24)&0xFF; UpdateControls(); } void CIPEditWidget::SetIP(quint8 b3, quint8 b2, quint8 b1, quint8 b0) { m_ip0 = b0; m_ip1 = b1; m_ip2 = b2; m_ip3 = b3; m_ip = m_ip3; m_ip<<=8; m_ip += m_ip2; m_ip<<=8; m_ip += m_ip1; m_ip<<=8; m_ip += m_ip0; UpdateControls(); } bool CIPEditWidget::GetIP(quint8& b3, quint8& b2, quint8& b1, quint8& b0) { GetControlValues(); b3 = m_ip3; b2 = m_ip2; b1 = m_ip1; b0 = m_ip0; return m_Dirty; } bool CIPEditWidget::GetIP(quint32& ip) { GetControlValues(); ip = m_ip; return m_Dirty; } void CIPEditWidget::UpdateControls() { ui.lineEdit_B3->setText( QString().number( m_ip3 )); ui.lineEdit_B2->setText( QString().number( m_ip2 )); ui.lineEdit_B1->setText( QString().number( m_ip1 )); ui.lineEdit_B0->setText( QString().number( m_ip0 )); } void CIPEditWidget::GetControlValues() { if(ui.lineEdit_B3->isModified() && ui.lineEdit_B3->hasAcceptableInput() ) { m_Dirty = TRUE; m_ip3 = ui.lineEdit_B3->text().toUInt(); } if(ui.lineEdit_B2->isModified() && ui.lineEdit_B2->hasAcceptableInput() ) { m_Dirty = TRUE; m_ip2 = ui.lineEdit_B2->text().toUInt(); } if(ui.lineEdit_B1->isModified() && ui.lineEdit_B1->hasAcceptableInput() ) { m_Dirty = TRUE; m_ip1 = ui.lineEdit_B1->text().toUInt(); } if(ui.lineEdit_B0->isModified() && ui.lineEdit_B0->hasAcceptableInput() ) { m_Dirty = TRUE; m_ip0 = ui.lineEdit_B0->text().toUInt(); } m_ip = m_ip3; m_ip<<=8; m_ip += m_ip2; m_ip<<=8; m_ip += m_ip1; m_ip<<=8; m_ip += m_ip0; } cutesdr-1.0.5/gui/sounddlg.ui0000644000175000017500000000547511546075374016117 0ustar bottomsbottoms DialogSndCard 0 0 400 180 SoundCard Setup 30 140 341 31 Qt::Horizontal QDialogButtonBox::Cancel|QDialogButtonBox::Ok 10 40 301 22 10 100 301 22 110 20 121 16 Input Sound Device 110 80 121 16 Output Sound Device 320 100 70 17 Stereo buttonBox accepted() DialogSndCard accept() 248 254 157 274 buttonBox rejected() DialogSndCard reject() 316 260 286 274 cutesdr-1.0.5/gui/displaydlg.cpp0000644000175000017500000000730011546075374016566 0ustar bottomsbottoms///////////////////////////////////////////////////////////////////// // displaydlg.cpp: implementation of the CDisplayDlg class. // // This class implements a dialog to setup the display parameters // // History: // 2010-09-15 Initial creation MSW // 2011-03-27 Initial release ///////////////////////////////////////////////////////////////////// //========================================================================================== // + + + This Software is released under the "Simplified BSD License" + + + //Copyright 2010 Moe Wheatley. All rights reserved. // //Redistribution and use in source and binary forms, with or without modification, are //permitted provided that the following conditions are met: // // 1. Redistributions of source code must retain the above copyright notice, this list of // conditions and the following disclaimer. // // 2. Redistributions in binary form must reproduce the above copyright notice, this list // of conditions and the following disclaimer in the documentation and/or other materials // provided with the distribution. // //THIS SOFTWARE IS PROVIDED BY Moe Wheatley ``AS IS'' AND ANY EXPRESS OR IMPLIED //WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND //FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL Moe Wheatley OR //CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR //CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR //SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON //ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING //NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF //ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // //The views and conclusions contained in the software and documentation are those of the //authors and should not be interpreted as representing official policies, either expressed //or implied, of Moe Wheatley. //========================================================================================== #include "displaydlg.h" #include "ui_displaydlg.h" #include CDisplayDlg::CDisplayDlg(QWidget *parent) : QDialog(parent), ui(new Ui::CDisplayDlg) { ui->setupUi(this); m_FftAve = 0; m_FftSize = 4096; m_MaxDisplayRate = 10; m_Percent2DScreen = 50; } CDisplayDlg::~CDisplayDlg() { delete ui; } //Fill in initial data void CDisplayDlg::InitDlg() { ui->fftSizecomboBox->addItem("2048 pts", 2048); ui->fftSizecomboBox->addItem("4096 pts", 4096); ui->fftSizecomboBox->addItem("8192 pts", 8192); ui->fftSizecomboBox->addItem("16384 pts", 16384); ui->fftSizecomboBox->addItem("32768 pts", 32768); int index = ui->fftSizecomboBox->findData(m_FftSize); if(index<0) { index = 1; m_FftSize = 4096; } ui->fftSizecomboBox->setCurrentIndex(index); ui->fftAvespinBox->setValue(m_FftAve); ui->ClickResolutionspinBox->setValue(m_ClickResolution); ui->MaxDisplayRatespinBox->setValue(m_MaxDisplayRate); ui->Screen2DSizespinBox->setValue(m_Percent2DScreen); ui->checkBoxTestBench->setChecked(m_UseTestBench); m_NeedToStop = false; } //Called when OK button pressed so get all the dialog data void CDisplayDlg::accept() { m_FftSize = ui->fftSizecomboBox->itemData(ui->fftSizecomboBox->currentIndex()).toInt(); int tmpsz = ui->Screen2DSizespinBox->value(); if( tmpsz!=m_Percent2DScreen) { m_NeedToStop = true; m_Percent2DScreen = tmpsz; } m_FftAve = ui->fftAvespinBox->value(); m_ClickResolution = ui->ClickResolutionspinBox->value(); m_MaxDisplayRate = ui->MaxDisplayRatespinBox->value(); m_UseTestBench = ui->checkBoxTestBench->isChecked(); QDialog::accept(); //need to call base class } cutesdr-1.0.5/gui/displaydlg.h0000644000175000017500000000155611546075374016242 0ustar bottomsbottoms////////////////////////////////////////////////////////////////////// // displaydlg.h: interface for the CDisplaydlg class. // // History: // 2010-09-15 Initial creation MSW // 2011-03-27 Initial release ///////////////////////////////////////////////////////////////////// #ifndef DISPLAYDLG_H #define DISPLAYDLG_H #include #include "gui/mainwindow.h" #include "ui_displaydlg.h" #include "gui/testbench.h" namespace Ui { class CDisplayDlg; } class CDisplayDlg : public QDialog { Q_OBJECT public: explicit CDisplayDlg(QWidget *parent = 0); ~CDisplayDlg(); void InitDlg(); int m_FftAve; int m_FftSize; int m_ClickResolution; int m_MaxDisplayRate; int m_Percent2DScreen; bool m_NeedToStop; bool m_UseTestBench; public slots: void accept(); private: Ui::CDisplayDlg *ui; }; #endif // DISPLAYDLG_H cutesdr-1.0.5/CMakeLists.txt0000644000175000017500000001417111711303214015663 0ustar bottomsbottomsproject(cutesdr) cmake_minimum_required(VERSION 2.8.0) set(CMAKE_VERBOSE_MAKEFILE OFF) # Versions set(cutesdr_VERSION_MAJOR 1) set(cutesdr_VERSION_MINOR 0) set(cutesdr_VERSION_PATCH 4) set(cutesdr_VERSION ${cutesdr_VERSION_MAJOR}.${cutesdr_VERSION_MINOR}.${cutesdr_VERSION_PATCH}) # Source add_subdirectory(siqs) # Dependencies find_package(PkgConfig REQUIRED) find_package(Qt4 REQUIRED) ADD_DEFINITIONS ( -D_REENTRANT -D_TTY_POSIX_ -D_TTY_LINUX_ -DQT_NO_DEBUG -DQT_MULTIMEDIA_LIB -DQT_GUI_LIB -DQT_NETWORK_LIB -DQT_CORE_LIB ) set(CuteSDR_SRCS gui/main.cpp gui/sounddlg.cpp gui/sdrsetupdlg.cpp gui/sdrdiscoverdlg.cpp gui/plotter.cpp gui/mainwindow.cpp gui/ipeditwidget.cpp gui/freqctrl.cpp gui/displaydlg.cpp gui/demodsetupdlg.cpp gui/editnetdlg.cpp gui/testbench.cpp gui/meter.cpp gui/sliderctrl.cpp gui/noiseprocdlg.cpp gui/aboutdlg.cpp gui/rdsdecode.cpp interface/soundout.cpp interface/sdrinterface.cpp interface/netiobase.cpp interface/ad6620.cpp interface/perform.cpp dsp/fractresampler.cpp dsp/fastfir.cpp dsp/downconvert.cpp dsp/demodulator.cpp dsp/fft.cpp dsp/agc.cpp dsp/amdemod.cpp dsp/samdemod.cpp dsp/ssbdemod.cpp dsp/smeter.cpp dsp/fmdemod.cpp dsp/fir.cpp dsp/iir.cpp dsp/noiseproc.cpp dsp/wfmdemod.cpp dsp/wfmmod.cpp ) set(CuteSDR_INC gui/mainwindow.h gui/sounddlg.h gui/sdrsetupdlg.h gui/sdrdiscoverdlg.h gui/plotter.h gui/ipeditwidget.h gui/freqctrl.h gui/sliderctrl.h gui/editnetdlg.h gui/displaydlg.h gui/demodsetupdlg.h gui/testbench.h gui/meter.h gui/noiseprocdlg.h gui/aboutdlg.h gui/rdsdecode.h interface/soundout.h interface/sdrinterface.h interface/protocoldefs.h interface/netiobase.h interface/ad6620.h interface/ascpmsg.h interface/perform.h dsp/fractresampler.h dsp/fastfir.h dsp/filtercoef.h dsp/downconvert.h dsp/demodulator.h dsp/datatypes.h dsp/fft.h dsp/agc.h dsp/amdemod.h dsp/samdemod.h dsp/ssbdemod.h dsp/smeter.h dsp/fmdemod.h dsp/fir.h dsp/iir.h dsp/noiseproc.h dsp/wfmdemod.h dsp/wfmmod.h dsp/rbdsconstants.h ) set (CuteSDR_UIS gui/mainwindow.ui gui/sdrdiscoverdlg.ui gui/sounddlg.ui gui/sdrsetupdlg.ui gui/ipeditframe.ui gui/editnetdlg.ui gui/displaydlg.ui gui/demodsetupdlg.ui gui/testbench.ui gui/sliderctrl.ui gui/aboutdlg.ui gui/noiseprocdlg.ui ) #-------------------------------------------------------------- # # Check for Ubuntu non standard installation # message ("qmake: ${QT_QMAKE_EXECUTABLE}") # get the path of the Qt header # execute_process( COMMAND ${QT_QMAKE_EXECUTABLE} -query QT_INSTALL_HEADERS OUTPUT_VARIABLE QT_INSTALL_HEADERS RESULT_VARIABLE rm_retval ) # trim the last directory in path string(REGEX REPLACE "(.*/)[^/]+$" "\\1" QT_INSTALL_HEADERS_UP ${QT_INSTALL_HEADERS}) # trim the end of line character string (REGEX REPLACE "\n" "" QT_INSTALL_HEADERS ${QT_INSTALL_HEADERS}) message("DIR INCLUDE: ${QT_INSTALL_HEADERS}") message("DIR INSTALL HEADERS UP: ${QT_INSTALL_HEADERS_UP}") # get the path of the Qt librarires # execute_process( COMMAND ${QT_QMAKE_EXECUTABLE} -query QT_INSTALL_LIBS OUTPUT_VARIABLE QT_INSTALL_LIBS RESULT_VARIABLE rm_retval ) # trim the end of line character string (REGEX REPLACE "\n" "" QT_INSTALL_LIBS ${QT_INSTALL_LIBS}) # default for standard QtSDK and binary packages before U11.04 # set (QT_ADDITIONAL_INCLUDE_PATH "") set (QT_ADDITIONAL_INCLUDE_PATH2 "") set (QT_ADDITIONAL_LDFLAG "QtMultimedia" ) # if there is a suspicious QtMultimediaKit (sibling of the standard Qt include path) # we have hit an Ubuntu binary package (11.04) # if ( EXISTS ${QT_INSTALL_HEADERS_UP}QtMultimediaKit/QAudio ) set (QT_ADDITIONAL_INCLUDE_PATH ${QT_INSTALL_HEADERS_UP}/QtMultimediaKit/ ) set (QT_ADDITIONAL_LDFLAG QtMultimediaKit) set (QT_ADDITIONAL_INCLUDE_PATH2 ${QT_INSTALL_HEADERS_UP}/QtMobility/ ) else ( EXISTS ${QT_INSTALL_HEADERS_UP}QtMultimediaKit/QAudio ) #set (QT_ADDITIONAL_INCLUDE_PATH ${QT_INSTALL_HEADERS} ) set (QT_ADDITIONAL_INCLUDE_PATH ${QT_INSTALL_HEADERS}/QtMultimedia/ ) set (QT_ADDITIONAL_LINK_DIR ${QT_INSTALL_LIBS}) endif ( EXISTS ${QT_INSTALL_HEADERS_UP}QtMultimediaKit/QAudio ) message("QT_ADDITIONAL_INCLUDE_PATH: ${QT_ADDITIONAL_INCLUDE_PATH}") message("QT_ADDITIONAL_INCLUDE_PATH2: ${QT_ADDITIONAL_INCLUDE_PATH2}") message("QT_ADDITIONAL_LDFLAG: ${QT_ADDITIONAL_LDFLAG}") #-------------------------------------------------------------- # # Build # include_directories( ${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_SOURCE_DIR}/dsp ${CMAKE_CURRENT_SOURCE_DIR}/gui ${CMAKE_CURRENT_SOURCE_DIR}/interface ${CMAKE_CURRENT_BINARY_DIR} ${QT_INCLUDE_DIR} ${QT_QTCORE_INCLUDE_DIR} ${QT_QTGUI_INCLUDE_DIR} ${QT_MULTIMEDIA_INCLUDE_DIR} ${QT_QTNETWORK_INCLUDE_DIR} # # non standard include path in order to workaround # Ubuntu binary packages strangeness # ${QT_ADDITIONAL_INCLUDE_PATH} ${QT_ADDITIONAL_INCLUDE_PATH2} ) link_directories( # # non standard library path in order to workaround # Ubuntu binary packages strangeness # ${QT_ADDITIONAL_LINK_DIR} ) QT4_WRAP_UI( CuteSDR_UI_HDRS ${CuteSDR_UIS} ) qt4_wrap_cpp (CuteSDR_MOCS ${CuteSDR_INC} ) add_executable(CuteSDR ${CuteSDR_SRCS} ${CuteSDR_UI_HDRS} ${CuteSDR_MOCS} ) target_link_libraries ( CuteSDR ${QT_QTCORE_LIBRARY} ${QT_QTGUI_LIBRARY} ${QT_QTNETWORK_LIBRARY} ${QT_QTOPENGL_LIBRARY} ${QT_MULTIMEDIA_LIBRARY} # # additional library in order to workaround # Ubuntu binary packages strangeness # ${QT_ADDITIONAL_LDFLAG} ) INSTALL_TARGETS(/bin CuteSDR) # # uninstall target # workaround as found in http://www.cmake.org/Wiki/CMake_FAQ#Can_I_do_.22make_uninstall.22_with_CMake.3F # configure_file( "${CMAKE_CURRENT_SOURCE_DIR}/cmake_uninstall.cmake.in" "${CMAKE_CURRENT_BINARY_DIR}/cmake_uninstall.cmake" IMMEDIATE @ONLY) add_custom_target(uninstall COMMAND ${CMAKE_COMMAND} -P ${CMAKE_CURRENT_BINARY_DIR}/cmake_uninstall.cmake)