搜索
您的当前位置:首页正文

GNURAdioDoc-8

2024-01-22 来源:抵帆知识网
Tutorial8:GettingPreparedforPythoninGNURadiobyReadingtheFMReceiverCodeLinebyLine–PartII

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

因篇幅问题不能全部显示,请点此查看更多更全内容

Top