From e832b4012429b666f049aaa108cd98caaa958ead Mon Sep 17 00:00:00 2001 From: Thierry Zamofing Date: Thu, 22 Nov 2018 12:32:43 +0100 Subject: [PATCH] update gui and triggering: speed sync works! --- Readme.md | 4 + python/MXMotion.py | 117 +++++++++++++------- python/PBMotionAnalyzer/MAError.py | 4 +- python/PBMotionAnalyzer/MAVelocity.py | 4 +- python/PBMotionAnalyzer/MAxyPlot.py | 47 ++++---- python/PBMotionAnalyzer/PBMotionAnalyzer.py | 74 ++++++++----- python/shapepath.py | 46 +++++--- 7 files changed, 183 insertions(+), 113 deletions(-) diff --git a/Readme.md b/Readme.md index d4b87a1..6d6a948 100644 --- a/Readme.md +++ b/Readme.md @@ -1167,6 +1167,10 @@ adapt speed make longer acquisition time by enhancing the gather server + + + + ``` diff --git a/python/MXMotion.py b/python/MXMotion.py index 0001e8c..b720633 100644 --- a/python/MXMotion.py +++ b/python/MXMotion.py @@ -38,7 +38,8 @@ class MotionBase: def setup_sync(self, crdId=1, prgId=2, plcId=2, mode=0, **kwargs): '''setup the timing synchronization for the motion program mode=0 : no sync at all - mode=1 : first sync test + mode=1 : synchronize start + mode=2 : synchronize start and adapt motion speed this function generates the code blocks: self.sync_wait and self.sync_run sync_wait can be put in the program to force a timing sync @@ -51,52 +52,14 @@ class MotionBase: pass self.sync_run = '&{crdId}b{prgId}r'''.format(prgId=prgId, crdId=crdId) - elif mode == 1: - # changes the Timebase of coord system - # modifies the timebase to start/stop a running program - # this can also be used to adjust the execution speed - # the plc for now - # - waits untis is false - # - waits raising edge of and the sets DesTimeBase=ServoPeriod - - # flag ='PowerBrick[0].GpioData[0].0.1==1' - flag = 'Gate3[1].Chan[0].UserFlag==0' - prg = '''close all buffers -disable plc {plcId} -open plc {plcId} -L0=Sys.PhaseCount -send 1"sync0 %d:%d\\n",Sys.PhaseCount,Sys.PhaseCount-L0 -while({flag}){{}} -send 1"sync1 %d:%d\\n",Sys.PhaseCount,Sys.PhaseCount-L0 -L1=Sys.PhaseCount -while(1) -{{ - if({flag}) - {{ - PowerBrick[0].GpioData[0].16.1=1 - Coord[{crdId}].DesTimeBase=Sys.ServoPeriod - send 1"sync2 %d:%d\\n",Sys.PhaseCount,Sys.PhaseCount-L1 - break - }} -}} -disable plc {plcId} -close -'''.format(plcId=plcId, crdId=crdId, flag=flag) - comm=self.comm - if comm is not None: - gpascii=comm.gpascii - gpascii.send_block(prg) - self.sync_prg = 'Coord[{crdId}].DesTimeBase=0'.format(prgId=prgId, plcId=plcId, crdId=crdId) - self.sync_run = 'enable plc {plcId};&{crdId}b{prgId}r'''.format(prgId=prgId, plcId=plcId, crdId=crdId) - - elif mode == 2: + elif mode in (1,2): # code block to insert in the program # - waits untis is false # - waits raising edge of and the sets DesTimeBase=ServoPeriod # flag ='PowerBrick[0].GpioData[0].0.1==1' - flag0 = 'Gate3[1].Chan[0].UserFlag==0' - flag1 = 'Gate3[1].Chan[1].UserFlag==0' + flag0='Gate3[1].Chan[0].UserFlag==0' + flag1='Gate3[1].Chan[1].UserFlag==0' prg = ''' //L0=Sys.PhaseCount //send 1"sync0 %d:%d\\n",Sys.PhaseCount,Sys.PhaseCount-L0 @@ -109,6 +72,76 @@ close '''.format(plcId=plcId, crdId=crdId, flag0=flag0, flag1=flag1) self.sync_prg = prg self.sync_run = '&{crdId}b{prgId}r'''.format(prgId=prgId, plcId=plcId, crdId=crdId) + if mode==2: + notFlag1 = 'Gate3[1].Chan[1].UserFlag!=0' + try: + prop=kwargs['prop'] #proportional value to adapt speed + except KeyError: + prop = 1E-4 + try: + maxJitter = kwargs['maxJitter'] # proportional value to adapt speed + except KeyError: + maxJitter=100 + prg='''close all buffers +disable plc {plcId} +open plc {plcId} +Coord[1].DesTimeBase=Sys.ServoPeriod //reset to 100% timebase +Coord[1].Q[0]=0 //set CryPos counter to 0 + +while({flag0}){{}} //wait Jungfrau start Trigger +while({flag1}){{}} //ESx detector trigger + + +L0=0 //CryPos counter +//L1 ServoCount event 1 +//L2 jitter: neg:motion lags trigger,pos:motion leads trigger + +while(L0>=0) //as long we are not at the last point +{{ + L0=Coord[1].Q[0] //CryPos counter + while({flag1} && L0==Coord[1].Q[0]){{}} //wait for event + L1=Sys.ServoCount //a event happened + L2=1000 + //send 1"event\\n" + if(L0==Coord[1].Q[0]) + {{//it was a trigger + //send 1"trigger %d %d\\n",L0,L1 + while(Sys.ServoCounttrigger + ts=fh['meta'].item()['timebase'] + jitter=idxTrigger[:idxInPos.shape[0]]-idxInPos + import matplotlib.pyplot as plt # used for the colormaps + plt.figure('jitter inPos -> trigger: scaling of DesTimeBase: %f'%(float(idxInPos[-1])/idxTrigger[idxInPos.shape[0]-1])) + plt.plot(jitter*ts) + plt.ylabel('jitter in ms') + plt.ylabel('point idx') + plt.show() + + def DispJitter(self,idxInPos,idxTrigger): + doc=self.doc + ts=doc.fh['meta'].item()['timebase'] + self.SetStatusText("Jitter: %.2gms scaling: %.6g"%((idxTrigger-idxInPos)*ts,float(idxInPos)/idxTrigger)) + def OnOpen(self, event): dlg = wx.FileDialog(self, "Choose a file", os.getcwd(), '','numpy files (*.npz;*.npy)|*.npz;*.npy|all (*.*)|*.*', wx.OPEN|wx.FD_CHANGE_DIR) if dlg.ShowModal() == wx.ID_OK: @@ -287,40 +308,43 @@ rec pass @staticmethod - def OnSetTime(usrData, value, msg): + def OnSetTime(usrData, idxRec, msg): 'called when the time slider has been changed' #print('OnSetTime', usrData, value, msg) view=usrData.slider.Parent doc=view.doc - idx=np.argmin(abs(doc.idxInPos-value)) - view.wxPosCtrl.SetValue(idx) - idx=np.argmin(abs(doc.idxTrigger-value)) - view.wxTrigCtrl.SetValue(idx) - doc.Update(view,0,value) + idxInPos=np.argmin(abs(doc.idxInPos-idxRec)) + view.wxPosCtrl.SetValue(idxInPos) + idxTrigger=np.argmin(abs(doc.idxTrigger-idxRec)) + view.wxTrigCtrl.SetValue(idxTrigger) + view.DispJitter(doc.idxInPos[idxInPos],doc.idxTrigger[idxTrigger]) + doc.Update(view,0,idxRec) @staticmethod - def OnSetIdxInPos(usrData, value, msg): + def OnSetIdxInPos(usrData, idxInPos, msg): 'called when the idxInPos slider has been changed' #print('OnSetPosIdx', usrData, value, msg) view = usrData.slider.Parent doc = view.doc - value=doc.idxInPos[value] - view.wxTimeCtrl.SetValue(value) - idx=np.argmin(abs(doc.idxTrigger-value)) - view.wxTrigCtrl.SetValue(idx) - doc.Update(view, 0, value) + idxRec=doc.idxInPos[idxInPos] + view.wxTimeCtrl.SetValue(idxRec) + idxTrigger=np.argmin(abs(doc.idxTrigger-idxRec)) + view.wxTrigCtrl.SetValue(idxTrigger) + view.DispJitter(doc.idxInPos[idxInPos],doc.idxTrigger[idxTrigger]) + doc.Update(view, 0, idxRec) @staticmethod - def OnSetIdxTrigger(usrData, value, msg): + def OnSetIdxTrigger(usrData, idxTrigger, msg): 'called when the idxTrigger slider has been changed' #print('OnSetIdxTrigger', usrData, value, msg) view = usrData.slider.Parent doc = view.doc - value=doc.idxTrigger[value] - view.wxTimeCtrl.SetValue(value) - idx=np.argmin(abs(doc.idxInPos-value)) - view.wxPosCtrl.SetValue(idx) - doc.Update(view, 0, value) + idxRec=doc.idxTrigger[idxTrigger] + view.wxTimeCtrl.SetValue(idxRec) + idxInPos=np.argmin(abs(doc.idxInPos-idxRec)) + view.wxPosCtrl.SetValue(idxInPos) + view.DispJitter(doc.idxInPos[idxInPos],doc.idxTrigger[idxTrigger]) + doc.Update(view, 0, idxRec) if __name__ == '__main__': diff --git a/python/shapepath.py b/python/shapepath.py index f755f4f..774d8d7 100755 --- a/python/shapepath.py +++ b/python/shapepath.py @@ -184,9 +184,14 @@ class ShapePath(MotionBase): 1. generates program and saves to fnPrg the type of generated program is defined by 2. runs the program on the deltatau + + mode=-1 jog a 10mm square + mode=0 linear motion + mode=1 pvt motion + mode=2 spline motion kwargs: - acq_per : acquire period: acquire data all acq_per servo loops (default=1) pt2pt_time : time to move from one point to the next point + sync_frq : synchronization mark all n points ''' prg=['close all buffers','open prog %d'%(prgId)] comm=self.comm @@ -229,6 +234,10 @@ class ShapePath(MotionBase): cnt=kwargs['cnt'] #move path multiple times except KeyError: cnt=1 + try: + sync_frq=kwargs['sync_frq'] #synchronization mark all n points + except KeyError: + sync_frq=10 try: pt=self.ptsCorr except AttributeError: @@ -259,7 +268,10 @@ class ShapePath(MotionBase): prg.append('N100:') prg.append(' pvt%g abs'%pt2pt_time) #100ms to next position for idx in range(1,pv.shape[0]): + if sync_frq is not None and idx%sync_frq==0: + prg.append('Coord[1].Q[0]=%d'%(idx)) prg.append('X%g:%g Y%g:%g'%tuple(pv[idx,(0,2,1,3)])) + prg.append('Coord[1].Q[0]=%d' % (idx)) prg.append('X%g Y%g' % tuple(pv[-1, (0,1)])) if cnt>1: prg.append('dwell 10') @@ -273,6 +285,8 @@ class ShapePath(MotionBase): prg.append('}') else: prg.append('dwell 1000') + if sync_frq is not None: + prg.append('Coord[1].Q[0]=-1') prg.append('Gather.Enable=0') elif mode==2: #### spline motion try: @@ -437,28 +451,30 @@ class ShapePath(MotionBase): idxTrigger = np.where(np.diff(idxTrigger) == 1)[0] + 1 idxInPos = [] # first point at idx 0 try: # find approximate distance of 2 points - rng = idxTrigger[2] - idxTrigger[1] + rng = idxTrigger[2]-idxTrigger[1] except IndexError: - rng = int(lenRec / lenPts) - idx = rng / 2 + rng = int(lenRec/lenPts) + idx = rng/2 for i in range(lenPts): - l = rec[idx:idx + rng, (3, 2)] - pts[i, :] - l2 = l[:, 0] ** 2 + l[:, 1] ** 2 - ofs = l2.argmin() - print(l2[ofs]) - idx += ofs + l=rec[idx:idx+rng,(3,2)]-pts[i,:] + l2=l[:,0]**2+l[:,1]**2 + ofs=l2.argmin() + #print(l2[ofs]) + idx+=ofs idxInPos.append(idx) - idx += rng / 2 + idx+= rng/2 idxInPos = np.array(idxInPos) - jitter=idxInPos-idxTrigger[:idxInPos.shape[0]] + jitter=idxTrigger[:idxInPos.shape[0]]-idxInPos fig = plt.figure('trigger jitter') ax = fig.add_subplot(1, 1, 1) hl = [] hl += ax.plot(jitter*ts, 'b-',label='jitter') ax.xaxis.set_label_text('position idx') - ax.yaxis.set_label_text('jitter (ms)') + ax.yaxis.set_label_text('jitter motion (ms)') + print('scaling of DesTimeBase: %f'%(float(idxInPos[-1])/idxTrigger[idxInPos.shape[0]-1])) + plt.show() @@ -623,8 +639,8 @@ if __name__=='__main__': sp.setup_gather(acq_per=2) #setup_sync(self, crdId=1, prgId=2, plcId=2, mode=0, **kwargs): #sp.setup_sync() #no sync at all - #sp.setup_sync(mode=1) #sync with timing system (PLC) - sp.setup_sync(mode=2) #sync with timing system (PROG) + #sp.setup_sync(mode=1) #sync with timing system (PROG) + sp.setup_sync(mode=2) #sync with timing system and PLC to sync speed (PROG) #sp.gen_grid_points(w=2,h=2,pitch=50,rnd=.2);sp.sort_points(xy);sp.setup_motion(fnPrg=fn+'.prg',mode=1,pt2pt_time=10,acq_per=1) #sp.gen_swissmx_points(width=1000,ofs=(-500,0));sp.setup_motion(fnPrg=fn+'.prg',mode=1,pt2pt_time=40,acq_per=1) @@ -647,7 +663,7 @@ if __name__=='__main__': sp.run() trigger(0.5) sp.gather_upload(fnRec=fn+'.npz') - sp.plot_gather() + sp.plot_gather(mode=8) # cfg={"points": [[100,523],[635,632],[756,213]],"sequencer":[sort_points,move_trj+')']}