From ad149fb7503539095a31a08b52fe5a4a60a331a9 Mon Sep 17 00:00:00 2001 From: Thierry Zamofing Date: Fri, 16 Jun 2023 16:23:17 +0200 Subject: [PATCH] enhance shapepath for compact code in grid scan --- .idea/vcs.xml | 4 + python/MXMotion.py | 2 +- python/shapepath.py | 265 +++++++++++++++++++++++++++++++------------- 3 files changed, 194 insertions(+), 77 deletions(-) diff --git a/.idea/vcs.xml b/.idea/vcs.xml index 94a25f7..897833d 100644 --- a/.idea/vcs.xml +++ b/.idea/vcs.xml @@ -2,5 +2,9 @@ + + + + \ No newline at end of file diff --git a/python/MXMotion.py b/python/MXMotion.py index 26679db..e5ab414 100644 --- a/python/MXMotion.py +++ b/python/MXMotion.py @@ -92,7 +92,7 @@ class MotionBase: if sync_mode==1: prg = f'\n{flag0}=0\n' if sync_flag&1 else '' prg += f''' -Coord[{crdId}].Q[0]=-2 motion program started and armed +Coord[{crdId}].Q[0]=-2 // motion program started and armed Coord[{crdId}].TimeBaseSlew=1 //1E-4 is default Coord[{crdId}].DesTimeBase=0 while({flag0}==0){{}} diff --git a/python/shapepath.py b/python/shapepath.py index 22e40dc..de47b07 100755 --- a/python/shapepath.py +++ b/python/shapepath.py @@ -44,6 +44,16 @@ Mot 4: Stage X Stada Stepper 670mA 200 poles 1 rev = 100*2 Mot 5: Stage Z Stada Stepper 670mA 200 poles 1 rev = 100*2048 phase_step (2 stepper motor) s.a. https://docs.google.com/document/d/1soSuCZYyfGf_ntcgG_Y1_WeGuo_687OuFn0s4sMj1uY/ + +tunneling: +PPMAC=SAR-CPPM-EXPMX1 +ssh -L 10001:localhost:22 root@$PPMAC 'uname -a' +ssh -L 10002:localhost:2332 root@$PPMAC 'uname -a' + +debug code: +ssh root@$PPMAC +sendgetsends -1 + ''' from __future__ import print_function @@ -454,6 +464,21 @@ class DebugPlot: class ShapePath(MotionBase): + ''' + member variables: + meta: defined in base class MotionBase + MotionBase uses keys: + srv_per + pt2pt_time + sync_flag + sync_mode + additional keys from function setup_gather(): + acq_per + address + + points: numpy array of raw points (motor values not X,Y-cooridnate values) + + ''' def __init__(self,comm, gather, verbose,**kwargs): MotionBase.__init__(self,comm, gather, verbose, **kwargs) @@ -677,35 +702,42 @@ class ShapePath(MotionBase): gpascii.send_block(prg) def setup_motion(self,prgId=2,fnPrg=None,mode=0,**kwargs): - ''' - 1. generates program and saves to fnPrg - the type of generated program is defined by - 2. runs the program on the deltatau + ''' + generates program and saves to fnPrg + the type of generated program is defined by - mode=1 pvt motion - kwargs: - scale : scaling velocity (default=1. value=0 would stop at the point - cnt : move path multiple times (default=1) - dwell : dwell time at end (default=100ms) + mode:0 unused + mode:1 pvt motion + kwargs: + scale : scaling velocity (default=1. value=0 would stop at the point + cnt : move path multiple times (default=1) + dwell : dwell time at end (default=100ms) + mode:2 unused + mode:3 pvt motion using inverse fft velocity + kwargs : same as pvt motion plus: + numPad : number of padding points to reduce aliasing (default=16) + mode:4 pvt motion short code using grid parameters + kwargs : same as pvt motion plus: + grid: grid parameters: {orig:(0,0),pitch(10,10),cnt:(10,10),mode:0} + mode=0 ->X,Y 'snake' scan + mode=0 ->Y,X 'snake' scan - mode=3 pvt motion using inverse fft velocity - kwargs: same as pvt motion - numPad : number of padding points to reduce aliasing (default=16) - ''' - prg=['close all buffers','open prog %d'%(prgId)] + ''' + prg=f'close all buffers\nopen prog {prgId}\n' - verb=self.verbose - comm=self.comm - if comm is not None: - gpascii=comm.gpascii - # this uses Coord[1].Tm and limits with MaxSpeed - if mode in (1,3): #### pvt motion - pt2pt_time=self.meta['pt2pt_time'] - ts=self.meta['srv_per'] - scale=kwargs.get('scale', 1.) - cnt=kwargs.get('cnt', 1) # move path multiple times - dwell=kwargs.get('dwell', 100) # wait time at end of motion - CoordFeedTime=1000. #Defaut deltatau value + verb=self.verbose + comm=self.comm + if comm is not None: + gpascii=comm.gpascii + # this uses Coord[1].Tm and limits with MaxSpeed + if mode in (1,3,4): #### pvt motion + pt2pt_time=self.meta['pt2pt_time'] + ts=self.meta['srv_per'] + scale=kwargs.get('scale', 1.) + cnt=kwargs.get('cnt', 1) # move path multiple times + dwell=kwargs.get('dwell', 100) # wait time at end of motion + CoordFeedTime=1000. #Defaut deltatau value + if mode in (1, 3): #### pvt motion, using points try: pt=self.ptsCorr except AttributeError: @@ -720,7 +752,7 @@ class ShapePath(MotionBase): if mode==1: # set velocity to average from prev to next point dist=pv[2:,(0,1)] - pv[:-2,(0,1)] pv[ 1:-1,(2,3)] = dist/(2.*pt2pt_time)*scale #um/ms - else: #mode=3: set velocity to the reconstructed inverse fourier transformation + else: # mode==3: # set velocity to the reconstructed inverse fourier transformation numPad=kwargs.get('numPad', 16) p=np.hstack((pt.T,pt[-1,:].repeat(numPad).reshape(2,-1))) k=p.shape[1] @@ -743,53 +775,121 @@ class ShapePath(MotionBase): plt.show(block=False) pv[1:-1, (2, 3)]*=CoordFeedTime #scaling for Deltatau - prg.append('linear abs') - #prg.append('X%g Y%g' % tuple(pv[0, (0,1)]-(120,120))) - prg.append('X%g Y%g' % tuple(pv[0, (0,1)])) - prg.append('dwell 10') - try: prg.extend(self.sync_prg.split('\n')) - except AttributeError: - #print('no sync code available') - prg.append('Gather.Enable=2') - if cnt>1: - prg.append('P100=%d'%cnt) - prg.append('N100:') - prg.append(' pvt%g abs'%pt2pt_time) #100ms to next position + prg+=' linear abs\n X%g Y%g\n' % tuple(pv[0, (0,1)]) + else: # mode==4: #### pvt motion, short code using grid parameters + g=kwargs['grid'] + ox,oy=g['pos'] + px,py=g['pitch'] + nx,ny=g['count'] + + scan=0x2 + xx, yy=np.meshgrid(range(nx), range(ny)) + if scan&0x01: #modify x scaning forward backward each line + for i in range(1,ny,2): + xx[i]=xx[i][::-1] + if scan&0x02: # modify y scaning forward backward each line + xx=xx.T + yy=yy.T + for i in range(1, nx, 2): + yy[i]=yy[i][::-1] + pts=np.array([xx.reshape(-1), yy.reshape(-1)], dtype=np.float).transpose() #*pitch + self.points=pts*g['pitch']+g['pos'] + + prg+=f' linear abs\n X{ox:g} Y{oy:g}\n' + prg+=' dwell 10\n' + try: prg+=self.sync_prg + except AttributeError: + #print('no sync code available') + prg+=' Gather.Enable=2\n' + if cnt>1: + prg+=' P100=%d\n'%cnt + prg+='N100:\n' + prg+=' pvt%g abs\n'%pt2pt_time #100ms to next position + if mode in (1,3): for idx in range(1,pv.shape[0]): - prg.append(f'N{idx} ' + 'X%g:%g Y%g:%g'%tuple(pv[idx,(0,2,1,3)])) - #if idx%256==4: #sync data all 256 points - # prg.append('Coord[1].Q[12]=Sys.ServoCount') - # #prg.append(f'Coord[1].Q[13]={idx}') - # prg.append(f'Coord[1].Q[13]=Motor[1].DesPos') - prg.append('X%g Y%g' % tuple(pv[-1, (0,1)])) - if cnt>1: - prg.append('dwell 10') - prg.append('P100=P100-1') - prg.append('if(P100>0)') - prg.append('{') - prg.append(' linear abs') - prg.append('X%g Y%g' % tuple(pv[0, (0,1)])) - prg.append('dwell %d' % dwell) - prg.append('goto 100') - prg.append('}') - else: - prg.append('dwell %d'%dwell) - prg.append('Gather.Enable=0') + prg+=f'N{idx} ' + 'X%g:%g Y%g:%g\n'%tuple(pv[idx,(0,2,1,3)]) + prg+=f'X{pv[-1, 0]} Y{pv[-1, 1]}\n' + else: # mode=4 + #mode=0 # X fast Y slow + mode=1 # X fast Y slow + if mode==0: + pass + else: # mode=1 + vx=px/(pt2pt_time)*scale*CoordFeedTime #scaling for Deltatau + vy=py/(pt2pt_time)*scale*CoordFeedTime #scaling for Deltatau - prg.append('close') - #prg.append('&1\nb%dr\n'%prgId) - if verb&0x04: - for ln in prg: - print(ln) + prg+=f'''\ +//grid pvt motion +L1=0 //slow loop x +L0=0 //fast loop y +while(L1<{nx}) +{{ + //send 1"A:move X%f:%f Y%f:%f",{ox}+L1*{px},0,{oy}+L0*{py},{vy/2:g} + X({ox}+L1*{px}):0 Y({oy}+L0*{py}):{vy/2:g} + L0+=1 + while(L0<{ny}-1) + {{ + //send 1"B:move X%f:%f Y%f:%f",{ox}+L1*{px},0,{oy}+L0*{py},{vy:g} + X({ox}+L1*{px}):0 Y({oy}+L0*{py}):{vy:g} + L0+=1 + }} + if(L1>={nx}-1) + {{ + break + }} + //send 1"C:move X%f:%f Y%f:%f",{ox}+L1*{px},{vx/2:g},{oy}+L0*{py},{vy/2:g} + X({ox}+L1*{px}):{vx/2:g} Y({oy}+L0*{py}):{vy/2:g} + L1+=1 + //send 1"D:move X%f:%f Y%f:%f",{ox}+L1*{px},{vx/2:g},{oy}+L0*{py},{-vy/2:g} + X({ox}+L1*{px}):{vx/2:g} Y({oy}+L0*{py}):{-vy/2:g} + L0-=1 + while(L0>=1) + {{ + //send 1"E:move X%f:%f Y%f:%f",{ox}+L1*{px},0,{oy}+L0*{py},{-vy:g} + X({ox}+L1*{px}):0 Y({oy}+L0*{py}):{-vy:g} + L0-=1 + }} + if(L1>={nx}-1) + {{ + break + }} + //send 1"F:move X%f:%f Y%f:%f",{ox}+L1*{px},{vx/2:g},{oy}+L0*{py},{-vy/2:g} + X({ox}+L1*{px}):{vx/2:g} Y({oy}+L0*{py}):{-vy/2:g} + L1+=1 + //send 1"G:move X%f:%f Y%f:%f",{ox}+L1*{px},{vx/2:g},{oy}+L0*{py},{vy/2:g} + X({ox}+L1*{px}):{vx/2:g} Y({oy}+L0*{py}):{vy/2:g} + L0+=1 +}} +//send 1"H:move X%f:%f Y%f:%f",{ox}+L1*{px},{0:g},{oy}+L0*{py},{0:g} +X({ox}+L1*{px}):{0:g} Y({oy}+L0*{py}):{0:g} - if fnPrg is not None: - fh=open(fnPrg,'w') - fh.write('\n'.join(prg)) - fh.close() - if comm is not None: - gpascii.send_block(prg,verb&0x08) +''' + #common code to repeat the motion multiple times + if cnt>1: + prg+=f'''\ +dwell 10 +P100=P100-1 +f(P100>0) +{{ + linear abs + X{pv[0,0]:g} Y{pv[0,1]:g} + dwell {dwell} + goto 100 +}}\n''' + else: + prg+=f' dwell {dwell}\n Gather.Enable=0\nclose\n' + #prg+='&1\nb%dr\n'%prgId) + if verb&0x04: + print(prg) - self.prg=prg + if fnPrg is not None: + fh=open(fnPrg,'w') + fh.write(prg) + fh.close() + if comm is not None: + gpascii.send_block(prg,verb&0x08) + + self.prg=prg def gather_upload(self,fnRec=None): gt=self.gather @@ -844,13 +944,13 @@ if __name__=='__main__': param['fast_gather_port']=int(hpp[2]) print(' -> ssh-tunneling PPComm({host}:{port} {host}:{fast_gather_port})'.format(**param)) comm=PPComm(**param) - gather = Gather(comm) + gather=Gather(comm) #real start and frame trigger with sync #sp = ShapePath(comm, gather, args.verbose) # direct start - #sp = ShapePath(comm, gather, args.verbose,sync_mode=0,pt2pt_time=10) + sp = ShapePath(comm, gather, args.verbose,sync_mode=0,pt2pt_time=10) #simulated start and frame trigger no sync #sp = ShapePath(comm, gather, args.verbose,sync_mode=1,sync_flag=3) @@ -862,7 +962,7 @@ if __name__=='__main__': #sp = ShapePath(comm, gather, args.verbose,sync_mode=1,sync_flag=1) #simulated start real frame trigger with sync - sp = ShapePath(comm, gather, args.verbose,sync_mode=2,sync_flag=1) + #sp = ShapePath(comm, gather, args.verbose,sync_mode=2,sync_flag=1) fn='/tmp/shapepath' #fn =unique_filename('ShapePathAnalyser/records/19_01_24/spiral') @@ -899,15 +999,26 @@ if __name__=='__main__': #sp.gen_grid_points(w=50,h=50,pitch=120,rnd=0,ofs=(0,+2000));sp.sort_points(False,50); sp.meta['pt2pt_time']=10 #sp.gen_grid_points(w=16,h=16,pitch=120,rnd=0,ofs=(0,+2000));sp.sort_points(False,16); sp.meta['pt2pt_time']=10 #12.5x12.5 - #sp.gen_grid_points(w=78,h=78,pitch=120,rnd=0,ofs=(-10000,-12000));sp.sort_points(False,78); sp.meta['pt2pt_time']=10 + sp.gen_grid_points(w=78,h=78,pitch=120,rnd=0,ofs=(-10000,-12000));sp.sort_points(False,78); sp.meta['pt2pt_time']=10 #23.0x23.0 -> only 3 data points from shot to shot: 6,9,12,14,17,20... - sp.gen_grid_points(w=162,h=162,pitch=120,rnd=0,ofs=(-10000,-12000));sp.sort_points(False,162); sp.meta['pt2pt_time']=10 + #sp.gen_grid_points(w=162,h=162,pitch=120,rnd=0,ofs=(-10000,-12000));sp.sort_points(False,162); sp.meta['pt2pt_time']=10 #sp.gen_grid_points(w=1,h=10,pitch=100,rnd=0,ofs=(0,0)) #sp.gen_spiral_points(rStart=100,rInc=20,numSeg=8,numCir=32, ofs=(0, 0)) #sp.gen_spiral_points(rStart=100,rInc=10,numSeg=2,numCir=32, phase=45, ofs=(0, 0)) #sp.gen_spiral_points(rStart=100,rInc=10,numSeg=4,numCir=32, ofs=(0, 0)) #sp.gen_closed_shifted() + #sp.setup_motion(fnPrg=fn+'.prg', mode=3, scale=1.,dwell=10) + sp.gen_grid_points(w=10,h=11,pitch=120,rnd=0,ofs=(-1000,-1200));sp.sort_points(False,11); sp.meta['pt2pt_time']=10 + #sp.setup_motion(fnPrg=fn+'.prg', mode=1, scale=1.,dwell=10) + #grid={'pos':(-1000, -1200),'pitch':(120,120),'count':(10,11)} + #sp.setup_motion(fnPrg=fn+'.prg', mode=4, scale=1.,dwell=10,grid=grid) + + sp.gen_grid_points(w= 40,h=45,pitch=120,rnd=0,ofs=(-1000,-1200));sp.sort_points(False,45); sp.meta['pt2pt_time']=10 + sp.setup_motion(fnPrg=fn+'.prg', mode=1, scale=1.,dwell=10) + grid={'pos':(-500, -600),'pitch':(120,120),'count':(40,45)} + sp.setup_motion(fnPrg=fn+'.prg', mode=4, scale=1.,dwell=10,grid=grid) + gtMaxLn=int(sp.comm.gpascii.get_variable('Gather.MaxLines')) # 116508 if gtMaxLn==0: @@ -927,7 +1038,9 @@ Filename: {fn}.[prg|npz] sp.setup_sync(verbose=args.verbose&0x40,timeOfs=0.03,timeCor=0.0005) sp.setup_coord_trf() # reset to shape path system #sp.meta['pt2pt_time']=10 #put between setup_sync and setup_motion to have more motion points than FEL syncs - sp.setup_motion(fnPrg=fn+'.prg', mode=3, scale=1.,dwell=10) + #sp.setup_motion(fnPrg=fn+'.prg', mode=3, scale=1.,dwell=10) + + #NEW sp.setup_motion(fnPrg=fn+'.prg', mode=2, scale=1.,dwell=10) #sp.setup_motion(fnPrg=fn + '.prg', mode=1, scale=1,dwell=10) #sp.setup_motion(fnPrg=fn + '.prg', mode=1, scale=0,dwell=10) sp.homing() #homing if needed