DaweiShen∗
July13,2005
Abstract
Let’scontinueourdiscussionontheFMexamplewfm_rcv_gui.py.TheusageoftheGUItoolsinGNURadio,whicharebuiltuponwxPython,willbeshown.Wewillalsointroducesomeusefulprogrammingtipsonargumentparsing.IfyouareinterestedinusingorevendevelopingtheGUItools,thisarticlewouldbehelpful.
1Overview
Inthisarticle,wewillcompleteourdiscussionontheFMreceivercodewfm_rcv_gui.py.OneexcitingfeatureofGNURadio,isitincorporatespowerfulGUItoolsfordisplayingandanalyzingthesignal,emulatingtherealspectrumanalyzerandtheoscillograph.WewillintroducetheusageoftheGUItools,whicharebuiltuponwxPython.NextwewilltalkabouthowtohandlethecommandlineargumentsinPython.
2GUItoolsinGNURadio
Themostintuitiveandstraightforwardwaytoanalyzeasignalistodisplayitgraphically,bothintimedomainandfrequencydomain.Fortheapplicationsintherealworld,wehavethespectrumanalyzerandtheoscillographtofacilitateus.Fortunately,inthesoftwareradioworld,wealsohavesuchnicetools,thankstowxPython,whichprovidesaflexiblewaytoconstructGUItools.
2.1The‘SpectrumAnalyzer’-fftsink
Let’scontinuetoreadthecode:
if1:
pre_demod,fft_win1=\\
fftsink.make_fft_sink_c(self,panel,\"Pre-Demodulation\
authorisaffiliatedwithNetworkingCommunicationsandInformationProcessing(NCIP)Laboratory,
DepartmentofElectricalEngineering,UniversityofNotreDame.Welcometosendmeyourcommentsorinquiries.Email:dshen@nd.edu
∗The
1
2
512,quad_rate)
self.connect(src,pre_demod)
vbox.Add(fft_win1,1,wx.EXPAND)
Thisisthe‘softspectrumanalyzer’,basedonfastFouriertransformation(FFT)ofthedigitalsequence.This‘softspectrumanalyzer’isusedasthesignalsink.That’swhyitisnamedas‘fftsink’.It’sdefinedinthemodulewxgui.fftsink.py.Thefunctionmake_fft_sink_c()servesasthepublicinterfacetocreateaninstanceofthefftsink:
/site-packages/gnuradio/wxgui/fftsink.py...
defmake_fft_sink_c(fg,parent,title,fft_size,input_rate,ymin=0,ymax=100):
block=fft_sink_c(fg,parent,title=title,fft_size=fft_size,
sample_rate=input_rate,y_per_div=(ymax-ymin)/8,ref_level=ymax)
return(block,block.win)First,weshouldpointoutthatinPython,afunctioncouldreturnmultiplevalues.make_fft_sink_c()returnstwovalues:blockisaninstanceoftheclassfft_sink_c,definedinthesamemodulewxgui.fftsink.py.AnotherspecialfeatureofPythonneedstobeemphasized:Pythonsupportsmultipleinheritance.fft_sink_cisderivedfromtwoclasses:gr.hier_blockandfft_sink_base.Beinga‘son’classofgr.hier_blockimpliesthatfft_sink_ccanbetreatedasanormalblock,whichcanbeplacedandconnectedinaflowgraph,asthenextlineshows:
self.connect(src,pre_demod)
block.winisobviouslyanattributeofblock.Inthedefinitionoftheclassfft_sink_c,wecanfinditsdatatypeistheclassfft_window,asubclassofwx.Window,alsodefinedinthemodulewxgui.fftsink.py.Wecanthinkofitasawindowthatisgoingtobehanguponyourscreen.Thiswindowblock.winwillbeusedastheargumentofthemethodvbox.Add.
2.2HowwxPythonplaysitsrole
Tounderstandtheotherpartsthoroughly,weneedtoknowalittlebitaboutwxPython,aGUItoolkitforPython.InterestedreadersmayvisitwxPython’swebsiteortutorials’pageformoreinformation.ItmightbetimeconsumingtoexploreallthetechnicaldetailsaboutwxPython.ThegoodnewsisinGNURadio,wecanusethespectrumanalyzerandoscillographprettymuchasitis.Justcopythoselinesanywhereyouwantwithonlyafewchanges.
Let’sinvestsometimeinwxPython’sorganism.ThefirstthingtodoiscertainlytoimportallwxPython’scomponentsintocurrentworkspace,liketheline‘importwx’does.EverywxPythonapplicationneedstoderiveaclassfromwx.AppandprovideanOnInit()methodforit.Thesystemcallsthismethodaspartofitsstartup/initializationsequence,inwx.App.__init()__.TheprimarypurposeofOnInit()istocreatetheframes,windows,etc.necessaryfortheprogramtobeginoperation.Afterdefiningsuchaclass,weneedtoinstantiateanobjectofthisclassandstarttheapplicationbycallingitsMainLoop()method,whoseroleistohandletheevents.InourFMreceiverexample,whereissuchaclassdefined?Let’slookatthelastthreelines:if__name__==’__main__’:
app=stdgui.stdapp(wfm_rx_graph,\"WFMRX\")app.MainLoop()
Infact,suchaclass,calledstdapphasbeencreatedwhenweimportthestdguimodule.fromgnuradio.wxguiimportstdgui,fftsink
3
ThefinaltwolinesagainwillprobablybethesameforallyourwxPythonapplications.Wesimplycreateaninstanceofourapplicationclass,andthencallitsMainLoop()method.MainLoop()istheheartoftheapplicationandiswhereeventsareprocessedanddispatchedtothevariouswindowsintheapplication.Therearesometricksbehindthescene.Don’tworryaboutthat.Let’slookatthedefinitionofstdappin/gnuradio/wxgui/stugui.py:
classstdapp(wx.App):
def__init__(self,flow_graph_maker,title=\"GNURadio\"):
self.flow_graph_maker=flow_graph_makerself.title=title
#Allourinitializationmustcomebeforecallingwx.App.__init__.#OnInitiscalledfromsomewhereinthegutsof__init__.wx.App.__init__(self)
defOnInit(self):
frame=stdframe(self.flow_graph_maker,self.title)frame.Show(True)
self.SetTopWindow(frame)returnTrue
stdappisrighttheclassderivedfromwx.App.Itsinitializationmethod__init__()takestwoarguments:flow_graph_maker,aclassbelongingtotheflowgraphfamily(rememberthebiggestclasswfm_rx_graphwecreatedisderivedfromgr.flow_graph?);title,thetitleofthewholeapplication(WFMRXinourexample).InOnInit()method,thesetwoargumentsarefurtherusedtocreatetheobjectofstdframe.
classstdframe(wx.Frame):
def__init__(self,flow_graph_maker,title=\"GNURadio\"):
#print\"stdframe.__init__\"
wx.Frame.__init__(self,None,-1,title)
self.CreateStatusBar(2)mainmenu=wx.MenuBar()
menu=wx.Menu()
item=menu.Append(200,’E&xit’,’Exit’)
self.Bind(wx.EVT_MENU,self.OnCloseWindow,item)mainmenu.Append(menu,\"&File\")self.SetMenuBar(mainmenu)
self.Bind(wx.EVT_CLOSE,self.OnCloseWindow)
self.panel=stdpanel(self,self,flow_graph_maker)vbox=wx.BoxSizer(wx.VERTICAL)vbox.Add(self.panel,1,wx.EXPAND)self.SetSizer(vbox)
self.SetAutoLayout(True)vbox.Fit(self)
defOnCloseWindow(self,event):
self.flow_graph().stop()self.Destroy()defflow_graph(self):
returnself.panel.fg
4
It’sworthtakingawhiletounderstandthelayoutofawxPythonGUI.InwxPython,awx.Windowisanythingwhichcantakeupvisualspaceonthecomputerscreen.Thus,thewx.Windowclassisthebaseclassfromwhichallvisualelementsarederived–includinginputfields,pull-downmenus,buttons,etc.Thewx.WindowclassdefinesallthebehaviorcommontoallvisualGUIelements,includingpositioning,sizing,showing,givingfocus,etc.Ifwe’relookingforanobjecttorepresentawindowonthecomputerscreen,don’tlookatwx.Window,lookatwx.Frameinstead.wx.Frameisderivedfromwx.Window.Itimplementsallbehaviorspecifictowindowsonthecomputer’sscreen.A‘Frame’isawindowwhosesizeandpositioncan(usually)bechangedbytheuser.Itusuallyhasthickbordersandatitlebar,andcanoptionallycontainamenubar,toolbarandstatusbar.A‘Frame’cancontainanywindowthatisnotaframeordialog.Sotocreatea‘window’onthecomputerscreen,youcreateawx.Frame(oroneofitssub-classes,suchaswx.Dialog),ratherthanawx.Window.Withinaframe,you’lluseanumberofwx.Windowsub-classestofleshouttheframe’scon-tents,suchaswx.MenuBar,wx.StatusBar,wx.ToolBar,sub-classesofwx.Control(eg.wx.Button,wx.StaticText,wx.TextCtrl,wx.ComboBox,etc.),orwx.Panel,whichisacontainertoholdyourvariouswx.Controlobjects.Allvisualelements(wx.Windowobjectsandtheirsubclasses)canholdsub-elements.Awx.Framemightholdanumberofwx.Panelobjects,whichinturnholdanumberofwx.Button,wx.StaticTextandwx.TextCtrlobjects.
Inourexample,stdframe,thesubclassofwx.Frame,isusedtocreatethe‘frame’.Wemakethisframeappearby‘showing’itusingframe.Show(True).TheSetTopWindow()methodsimplytellsthatthisframeisoneofthemainframes(inthiscasetheonlyone)fortheapplication.Noticetheshapeoftheconstructorofwx.Frame:
wx.Frame(Parent,Id,\"title\")
MostoftheconstructorsinwxPythonhavethisshape:AparentobjectasafirstparameterandanIdinasecondparameter.Asshownintheexample,it’spossibletouserespectivelyNoneand-1asdefaultparameters,meaningtheobjecthasnoparentandrespectivelyasystem-definedId.Instdframe.__init__(),wecreateapanelandplaceinsidetheframe.self.panel=stdpanel(self,self,flow_graph_maker)Theclassstdpanelisderivedfromwx.Panel:
classstdpanel(wx.Panel):
def__init__(self,parent,frame,flow_graph_maker):
wx.Panel.__init__(self,parent,-1)self.frame=frame
vbox=wx.BoxSizer(wx.VERTICAL)
self.fg=flow_graph_maker(frame,self,vbox,sys.argv)self.SetSizer(vbox)
self.SetAutoLayout(True)vbox.Fit(self)self.fg.start()
Notethatpanel’sparentistheframeobjectwecreatejustnow,meaningthispanelisasubcom-ponentoftheframe.Theframeplacesthepanelinsideitselfusingawx.BoxSizer,vbox.Thebasicideabehindaboxsizeristhatwindowswillmostoftenbelaidoutinrathersimplebasicgeometry,typicallyinaroworacolumnornestedhierarchiesofeither.Awx.BoxSizerwilllayoutitsitemsinasimpleroworcolumn,dependingontheorientationparameterpassedtotheconstructor.Inourexample,vbox=wx.BoxSizer(wx.VERTICAL)tellstheconstructoralltheitemswillbeplacedvertically.TheSetSizer()calltellstheframewhichsizertouse.ThecalltoSetAutoLayout()tells
5
theframetousethesizertopositionandsizeyourcomponents.Andfinally,thecalltosizer.Fit()tellsthesizertocalculatetheinitialsizeandpositionforallitselements.Ifyouareusingsizers,thisisthenormalprocessyouwouldgothroughtosetupyourwindoworframe’scontentsbeforeitisdisplayedforthefirsttime.Themostimportantandusefulmethodforasizerisadd(),itappendsanitemtothesizer.Itssyntaxlookslike:
Add(self,item,proportion=0,flag=0)
‘item’iswhatyouwishtoappendtothesizer,usuallyawx.Windowobject.Itcanalsobeachildsizer.proportionisrelatedtowhetherachildofasizercanchangeitssizeinthemainorientationofthewx.BoxSizer.Thereareseveralflagsdefinedinwx,andtheyareusedtodeterminehowthesizeritemsbehaveandtheborderofthewindow.wx.EXPANDusedinourexamplemeanstheitemwillbeexpandedtofillthespaceallottedtotheitem.Refertothispageforacompletedescription.Haveyoueverhadthisconfusion:wedefinea‘big’classwfm_rcv_graph,butwheredoweuseit?whydowenevercreateaninstanceofthisclass?Thesecretisrevealedinstdpanel.__init__().Theinstanceofwfm_rcv_graphiscreatedhereandtheflowgraphisstarted.classstdpanel(wx.Panel):
def__init__(self,parent,frame,flow_graph_maker):
......
vbox=wx.BoxSizer(wx.VERTICAL)
self.fg=flow_graph_maker(frame,self,vbox,sys.argv)self.SetSizer(vbox)
self.SetAutoLayout(True)vbox.Fit(self)self.fg.start()
Weputapanelintheframe,butwhatdoweputinthepanel?Wefirstcreateanewsizervboxforthepanel.Notethatthisvboxisdifferentfromtheonedefinedinstdframe.Thenwecreateaninstanceofflow_graph_maker(wfm_rcv_graph)withvboxasanargumentpassedtoit(alsowithframeandthepanelitself).Inwfm_rcv_graph.__init__(),thisvboxwillappendseveralspectrumanalyzersoroscillograph(wx.Windowobjects)tothesizerbyusingvbox.Add().Thenthepanelusesthesizervboxpositionandsizeallthesechild-windows.Finally,westarttheflowgraph:self.fg.start(),thecorrespondingdatawouldbedisplayedonthescreendynamically.Let’sgobacktothecodeofourFMreceiverexample.
if1:
pre_demod,fft_win1=\\
fftsink.make_fft_sink_c(self,panel,\"Pre-Demodulation\
512,quad_rate)
self.connect(src,pre_demod)
vbox.Add(fft_win1,1,wx.EXPAND)andthedefinitionofthemake_fft_sink_c()method:
/site-packages/gnuradio/wxgui/fftsink.py...
defmake_fft_sink_c(fg,parent,title,fft_size,input_rate,ymin=0,ymax=100):
block=fft_sink_c(fg,parent,title=title,fft_size=fft_size,
sample_rate=input_rate,y_per_div=(ymax-ymin)/8,ref_level=ymax)
return(block,block.win)Everythingismuchclearernow,right?panelispassedtomake_fft_sink_c()asthe‘parent’ofthisfftsink(block.win,awx.Windowobject).Thereturnvalueofblock.winissavedinfft_win1andthenappendedtovbox.
6
make_fft_sink_c()takessevenparameters.fft_sizeisthenumberofsamplesusedtoperformFFT.input_rateisthesamplefrequency.yminandymaxgivetheverticalrangeoftheplotyouwishtodisplayonthescreen.
Notethatthereisacomplicatedstorybehindtheclassfft_sink_c.Wedidn’ttalkabouthowfastFouriertransformisperformedandhowitisusedasablock,butfocusingonitsinterfacetoPythonandwxPython.Infact,anotherPythonpackagecalled‘Numeric’helpsalothere.However,wedon’tneedtoknowallthedetails.UnderstandinghowitinteractswithwxPythonandotherblocksatthePythonlevelwouldbesufficient.
2.3The‘Oscillograph’-scopesinkAnotherimportantGUItoolinGNURadioisthe‘softoscillograph’-scope_sink.It’snotusedinourexample,butitwouldbeveryhelpfulifyouwishtoseethewaveformsinthetimedomain.Itsusageisquitesimilartothefft_sink:
if1:
scope_input,scope_win1=\\
scopesink.make_scope_sink_f(self,panel,\"Title\self.fs)
self.connect(signal,scope_input)vbox.Add(scope_win1,1,wx.EXPAND)
Notethatheresignalshouldbearealfloatsignal.IfyouwishtodisplayacomplexsignalwithI/Qchannels,make_scope_sink_c()istherightchoice.Copytheselineswhereveryouthinkascope
shouldappear,thenconnectittothesignalasablock.Referto/site-packages/gnuradio/wxgui/scopesink.pyformoredetails.
3Handlingcommandlinearguments
Pythonfullysupportscreatingprogramsthatcanberunonthecommandline,completewithcommand-lineargumentsandeithershort-orlong-styleflagstospecifyvariousoptions.Remem-berwhenwecreateaninstanceofwfm_rcv_graphinstdpanel.__init__(),weuse:
self.fg=flow_graph_maker(frame,self,vbox,sys.argv)
Eachcommandlineargumentpassedtotheprogramwillbesavedinsys.argv,whichisjusta‘list’.Inthislist,sys.argv[0]isjustthecommanditself(wfm_rcv_gui.pyinourexample).Soactuallyalltheargumentsaresavedinsys.argv[1:].ThatexplainswhyweuseIF_freq=parseargs(argv[1:])toprocessthearguments.
Youmaywanttouseshort-orlong-styleflagstoaddvariousoptionslike‘-v’,or‘--help’.Thentheoptparsemoduleisexactlywhatyouwouldliketouse.optparseisapowerfulandflexiblecommandlineinterpreter.Youmayseethispagetostudyit.Anotherexample,locatedat
gnuradio-examples/python/usrp/fsk_r(t)x.pygivesaverygooddemonstrationonhowtousethisparser.
4conclusion
ThistutorialcompletesourdiscussionontheFMreceiverexample.WemainlytalkabouthowwxPythonplaysitsroleinGNURadio.Itmightbealittlebitinvolvedtounderstandhowthose
7
classesareorganizedtogether,butitwon’tbethatdifficultifwearepatientenough.Besides,thegoodnewsiswecansimpleusethosecodesastemplates,withoutworryingtoomuchabouttheimplementationdetails.
References
[1]Pythonon-linetutorials,http://www.python.org/doc/current/tut/[2]PythonLibraryReference-optparse,
http://www.python.org/doc/2.3/lib/module-optparse.html[3]wxPythonon-linetutorials,http://wxpython.org/tutorial.php
[4]wxPythonwiki,Gettingstarted,http://wiki.wxpython.org/index.cgi/Getting20Started
因篇幅问题不能全部显示,请点此查看更多更全内容