#!/usr/bin/env python # *-----------------------------------------------------------------------* # | | # | Copyright (c) 2016 by Paul Scherrer Institute (http://www.psi.ch) | # | | # | Author Thierry Zamofing (thierry.zamofing@psi.ch) | # *-----------------------------------------------------------------------* ''' shape an optimal path with given points verbose bits: 0x01 basic info 0x02 plot sorting steps 0x04 list program 0x08 upload progress 0x10 plot gather path 0x20 plot pvt trajectory (before motion) 0x40 print sync details if mode&1: self.plot_trajectory() if mode&2: self.plot_pos_error() if mode&4: self.plot_bode(xy=(3,1),mode=31,db=True) # FX self.plot_bode(xy=(2,0),mode=31,db=True) # FY if mode&8: self.plot_trigger_jitter() Gather motor order idx 0 1 2 3 4 NEW Motor[1].ActPos Motor[2].ActPos Motor[1].DesPos Motor[2].DesPos Gate3[1].Chan[1].UserFlag NEW y.ActPos x.ActPos y.DesPos x.DesPos Gate3[1].Chan[1].UserFlag Mot 1: Stage Y Parker MX80L D11 25mm one pole cycle = 13mm = 2048 phase_step Mot 2: Stage X Parker MX80L D11 25mm one pole cycle = 13mm = 2048 phase_step Mot 3: Rotation stage LS Mecapion MDM-DC06DNC0H 32 poles = 1 rev = 16*2048=32768 phase_step Mot 4: Stage X Stada Stepper 670mA 200 poles 1 rev = 100*2048 phase_step (2 stepper motor) 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 ''' import logging _log=logging.getLogger(__name__) import os, sys, time, re import numpy as np import matplotlib as mpl import matplotlib.pyplot as plt import subprocess as sprc import socket hostname=socket.gethostname() if hostname=='ganymede': sys.path.insert(0, os.path.expanduser('~/Documents/prj/SwissFEL/PBTools/')) else: sys.path.insert(0, os.path.expanduser('/sf/cristallina/applications/SwissMX/PBTools/')) from pbtools.misc.pp_comm import PPComm from pbtools.misc.gather import Gather from MXMotion import MotionBase def gen_pvt(p,v, p2pt, ts): '''generates a pvt motion p: position array v: velocity array t: time array ts: servo cycle time !!! it is assumed, that the time intervals are constant !!! ''' n=int(p2pt/ts) pvt=np.ndarray((p.shape[0]-1)*n) tt=np.arange(0,p2pt,ts)[:n] for i in range(p.shape[0]-1): d=p[i] c=v[i] a=(-2*(p[i+1]-p[i]-v[i]*p2pt)+p2pt*(v[i+1]-v[i]))/p2pt**3 b=(3*p2pt*(p[i+1]-p[i]-v[i]*p2pt)-p2pt**2*(v[i+1]-v[i]))/p2pt**3 pvt[i*n:(i+1)*n]=a*tt**3+b*tt**2+c*tt+d return pvt class DebugPlot: def __init__(self,obj=None): if obj is None: self.load_npz() elif type(obj)==str: self.load_npz(obj) else: self.set_data(obj) def plot_gen_pvt(self,pv): # pv is an array of posx posy velx vely #pv=pv[5:10,:] #pv=pv[5:-4,:] p2pt=self.meta['pt2pt_time'] # ms step between samples ts=self.meta['srv_per'] # sampling time in ms n=int(p2pt/ts) # servo cycle between samples k=pv.shape[0] # number of unique samples t=np.arange(0, p2pt*k, p2pt) # time array of trajectory ppx=gen_pvt(pv[:,0], pv[:,2], p2pt, ts) ppy=gen_pvt(pv[:,1], pv[:,3], p2pt, ts) tt=np.arange(0, n*(k-1))*ts # time array of trajectory fig=plt.figure() ax1=fig.add_subplot(2, 1, 1) ax2=fig.add_subplot(2, 1, 2) #ax.xaxis.set_ticks(t) ax1.stem(t, pv[:,0], '-r',use_line_collection=True) ax2.stem(t, pv[:,1], '-g',use_line_collection=True) ax1.plot(tt, ppx, '-r', label='x') ax2.plot(tt, ppy, '-g', label='y') #ax.legend(loc='best') ax=plt.figure().add_subplot(1, 1, 1) ax.plot(pv[:,0], pv[:,1], '.r', label='pft') ax.plot(ppx, ppy, '-c', label='pft') ax.invert_xaxis() ax.invert_yaxis() plt.axis('equal') ax.legend(loc='best') plt.show(block=False) # ### frequency plots ### # fig=plt.figure() # ax=fig.add_subplot(1,1,1)#ax=plt.gca() # # #remove linear slope # sx=ppx-(pv[-1,0]-pv[0,0])*np.arange(ppx.shape[0]) # sy=ppy-(pv[-1,1]-pv[0,1])*np.arange(ppy.shape[0]) # # #normalize with l -> value of k means amplitude of k at a given frequency # ppxf=np.fft.rfft(sx)/(2*n) # ppyf=np.fft.rfft(sy)/(2*n) # # f=np.fft.rfftfreq(ppx.shape[0], d=ts*1E-3) # f=f[1:] #remove dc value frequency # # mag=abs(ppxf[1:])#; mag=20*np.log10(abs(mag)) # ax.semilogx(f,mag,'-b',label='ppx') # Bode magnitude plot # mag=abs(ppyf[1:])#; mag=20*np.log10(abs(mag)) # ax.semilogx(f,mag,'-g',label='ppy') # Bode magnitude plot # #ax.yaxis.set_label_text('dB ampl') # ax.yaxis.set_label_text('ampl') # ax.xaxis.set_label_text('frequency [Hz]') # plt.grid(True) # # ax.legend(loc='best') # plt.show(block=False) return (tt,ppx,ppy) #@staticmethod #def onclick(event): # print('button=%s, x=%d, y=%d, xdata=%f, ydata=%f'%( # event.button, event.x, event.y, event.xdata, event.ydata)) # obj=event.canvas.figure.obj @staticmethod def plot_points(pts): fig=plt.figure() ax = fig.add_subplot(1,1,1) ax.invert_xaxis();ax.invert_yaxis() #hl=ax[0].plot(x, y, color=col) hl=ax.plot(pts[:,0],pts[:,1],'r.') hl=ax.plot(pts[:,0],pts[:,1],'y--') #cid = fig.canvas.mpl_connect('button_press_event', self.onclick) #fig.obj=self plt.axis('equal') #self.ax=ax #self.hl=hl def analyze_trigger(self): if hasattr(self,'idxTrigger'): return pts=self.pts # X,Y array rec=self.rec # yA,xA,yD,xD,trig lenRec=rec.shape[0] lenPts=pts.shape[0] idxTrigger=rec[:,4] idxTrigger=np.where(np.diff(idxTrigger)==1)[0]+1 idxInPos=[] #first point at idx 0 idx=0 for i in range(lenPts): l=rec[idx:,(3,2)]-pts[i,:] l2=l[:,0]**2+l[:,1]**2 try: ofs=l2.argmin() except ValueError as e: raise e #should never happen break idx+=ofs idxInPos.append(idx) idxInPos = np.array(idxInPos) #select only triggers on a target point i=max(0,np.abs(idxTrigger-idxInPos[1]).argmin()-1) j=i+idxInPos.shape[0] idxTrigger=idxTrigger[i:j] self.idxInPos=idxInPos self.idxTrigger=idxTrigger def plot_trigger_jitter(self): self.analyze_trigger() ts=self.meta['srv_per']*self.meta['acq_per'] idxTrigger=self.idxTrigger idxInPos=self.idxInPos n=min(idxTrigger.shape[0],idxInPos.shape[0])-1 jitter = idxTrigger[1:n]-idxInPos[1:n] pts=self.pts # X,Y array rec=self.rec # yA,xA,yD,xD,trig 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 motion (ms)') rec = self.rec # yA,xA,yD,xD,trig ts=self.meta['srv_per']*self.meta['acq_per'] fig = plt.figure('shot position error') ax = fig.add_subplot(1, 1, 1) #errx = rec[idxTrigger, 1] - rec[idxInPos, 3] #erry = rec[idxTrigger, 0] - rec[idxInPos, 2] n=idxTrigger.shape[0] errx = rec[idxTrigger, 1] - pts[:n, 0] erry = rec[idxTrigger, 0] - pts[:n, 1] err = np.sqrt(errx ** 2 + erry ** 2) hl = [] hl += ax.plot(errx, 'b-', label='x-error') hl += ax.plot(erry, 'g-', label='y-error') hl += ax.plot(err, 'r-', label='error') ax.xaxis.set_label_text('target point index') ax.yaxis.set_label_text('pos-error um') legend = ax.legend(loc='best', shadow=True) print('shot average error x %g um, y %g um, %g um' % (np.abs(errx).mean(), np.abs(erry).mean(), err.mean())) plt.show(block=False) def plot_pos_error(self): rec = self.rec # yA,xA,yD,xD,trig ts=self.meta['srv_per']*self.meta['acq_per'] fig = plt.figure('position error') ax = fig.add_subplot(1, 1, 1) t=np.arange(rec.shape[0],dtype=np.uint32) errx = rec[:, 1] - rec[:, 3] erry = rec[:, 0] - rec[:, 2] err = np.sqrt(errx ** 2 + erry ** 2) hl = [] hl += ax.plot(t, errx, 'b-', label='x-error') hl += ax.plot(t, erry, 'g-', label='y-error') hl += ax.plot(t, err, 'r-', label='error') ax.xaxis.set_label_text('ms (timebase: %g ms per data point)' % ts) ax.yaxis.set_label_text('pos-error um') legend = ax.legend(loc='best', shadow=True) print('motion average error x %g um, y %g um, %g um' % (np.abs(errx).mean(), np.abs(erry).mean(), err.mean())) plt.show(block=False) def plot_trajectory(self): pts = self.pts # X,Y array rec = self.rec # yA,xA,yD,xD,trig fig = plt.figure('trajectory') ax = fig.add_subplot(1, 1, 1) ax.invert_xaxis() ax.invert_yaxis() # hl=ax[0].plot(x, y, color=col) hl = ax.plot(pts[:, 0], pts[:, 1], 'r.', label='points') hl += ax.plot(pts[:, 0], pts[:, 1], 'y--', label='direct') hl += ax.plot(rec[:, 3], rec[:, 2], 'b-', label='DesPos') # desired path hl += ax.plot(rec[:, 1], rec[:, 0], 'g.', label='ActPos') # actual path try: pvt = self.pvt except AttributeError: pass else: hl = ax.plot(pvt[1], pvt[2], 'c--', label='SimPos') # simulated path fig2 = plt.figure('time line') ax2 = fig2.add_subplot(1, 1, 1) hl2 = ax2.plot(rec[:, 2], 'r-', label='desPos Mot1') hl2 += ax2.plot(rec[:, 3], 'g-', label='desPos Mot2') idxTrigger = rec[:, 4] idxTrigger = np.where(np.diff(idxTrigger) == 1)[0] + 1 if idxTrigger.shape[0] > 0: hl += ax.plot(rec[idxTrigger, 1], rec[idxTrigger, 0], 'xr', label='trig') # actual path hl2 += ax2.plot(rec[:, 4]*10, 'b-', label='trigger') ax.xaxis.set_label_text('x-pos um') ax.yaxis.set_label_text('y-pos um') ax.axis('equal') ax.legend(loc='best') # cid = fig.canvas.mpl_connect('button_press_event', self.onclick) # fig.obj=self ax2.legend(loc='best') plt.show(block=False) def plot_dbg(self): rec = self.rec # yA,xA,yD,xD,trig ts=self.meta['srv_per']*self.meta['acq_per'] fig = plt.figure('debug plot') ax = fig.add_subplot(1, 1, 1) t=np.arange(rec.shape[0],dtype=np.uint32) #address=("Motor[1].ActPos", "Motor[2].ActPos", "Motor[1].DesPos", "Motor[2].DesPos", "Gate3[1].Chan[1].UserFlag","EncTable[20].DeltaPos") hl = [] rec[0, 5]=rec[0, 6]=0 hl += ax.plot(t, rec[:, 6], 'rx', label='EncTable[20].DeltaPos') hl += ax.plot(t, rec[:, 5], 'b-', label='Coord[1].TimeBase') hl += ax.plot(t, rec[:, 4], 'g.', label='Gate3[1].Chan[1].UserFlag') ax.xaxis.set_label_text('ms (timebase: %g ms per data point)' % ts) ax.legend(loc='best') plt.show(block=False) def plot_gather(self,mode=255): try: meta=self.meta pts=self.pts # X,Y array rec = self.rec # yA,xA,yD,xD,trig except AttributeError as e: _log.warning('plot_gather(): '+str(e)+': no data acquired yet') return if mode&1: self.plot_trajectory() if mode&2: self.plot_pos_error() if mode&4: self.plot_bode(xy=(3,1),mode=31,db=True) # FX self.plot_bode(xy=(2,0),mode=31,db=True) # FY if mode&8: self.plot_trigger_jitter() #self.plot_dbg() plt.show(block=False) def plot_bode(self,xy=(0,1),mode=25,db=True): '''displays a bode plot of the data Y(s)=G(s)*X(s) Y= output signal X= input signal xy are the row indexes of input and output signal meta= meta information (dictionary) of data mode bits: (+mean default) +1: display time signal 2: display bode of X(s) signal 4: display bode of Y(s) signal +8: display bode of G(s) signal +16: clip frequencies out of minFrq,maxFrq ''' meta=self.meta pts=self.pts # X,Y array rec = self.rec # yA,xA,yD,xD,trig strMot=('FY.act','FX.act','FY.des','FX.des') ts=self.meta['srv_per']*self.meta['acq_per']*1E-3 #0.2ms num=rec.shape[0] #rngMin=int(.01/ts);rngMax=int(num-1.001/ts) #0.01s from start 1.01 sec before end rngMin=int(.01/ts);rngMax=rngMin+int((pts.shape[0]-2)*.01/ts) num=rngMax-rngMin minFrq=1/(num*ts)#minimal frq to show bode maxFrq=1/(2*ts) #maximal frq to show bode xIdx,yIdx=xy #remove DC value x=rec[rngMin:rngMax,xIdx]-rec[rngMin,xIdx] y=rec[rngMin:rngMax,yIdx]-rec[rngMin,yIdx] #make last value same as first (nice periodicity) x=x-(x[-1]*np.arange(num)/(num-1.)) y=y-(y[-1]*np.arange(num)/(num-1.)) if mode&1: t = ts*np.arange(num) fig=plt.figure('raw {}->{}'.format(strMot[xIdx],strMot[yIdx])) ax=fig.gca() ax.plot(t,x,'b') ax.plot(t,y,'g') fig=plt.figure('bode {}->{}'.format(strMot[xIdx],strMot[yIdx])) ax1=fig.add_subplot(2,1,1) ax1.grid(True) ax1.yaxis.set_label_text('Amplitude'+ (' [dB]' if db else '')) ax1.axvline(minFrq,c='k');ax1.axvline(maxFrq,c='k') ax2=fig.add_subplot(2,1,2, sharex = ax1) ax2.grid(True) ax2.xaxis.set_label_text('Frequency [Hz]') ax2.yaxis.set_label_text('Phase [degree]') ftX=np.fft.rfft(x) ftY=np.fft.rfft(y) fMax=.5/ts #fs=1/ts, fMax=1/2fs n=ftX.shape[0] f=np.arange(n)*fMax/(n-1) if mode&16: i=int(minFrq*num*ts); j=int(maxFrq*num*ts); #print(w[i],w[j]) f=f[i:j+1] ftX=ftX[i:j+1] ftY=ftY[i:j+1] ftLst=[] if mode&2: ftLst.append((ftX,'b','input')) if mode&4: ftLst.append((ftY,'g','output')) if mode&8: ftLst.append((ftY/ftX,'r','out/inp')) for ft,c,s in ftLst: phase=np.angle(ft) phase=np.degrees(np.unwrap(phase)) mag=np.abs(ft) #ftY)/np.abs(ftX) if db: magDb=20*np.log10(mag) #in decibel (20=10*2: factor 2 because rfft only half) ax1.semilogx(f,magDb,c,label=s) # Bode magnitude plot else: ax1.semilogx(f, mag, c,label=s) # Bode magnitude plot ax2.semilogx(f,phase,c,label=s) # Bode phase plot ax2.set_ylim(-360,360) ax2.legend(loc='best') plt.show(block=False) def load_npz(self,fn='/tmp/shapepath.npz'): fh=np.load(fn,allow_pickle=True) for k,v in fh.items(): setattr(self,k,v) self.meta=self.meta.item() def set_data(self,spObj): self.meta=spObj.meta self.pts=spObj.mot_pts try: self.rec=spObj.rec except AttributeError: pass try: self.pvt=spObj.pvt except AttributeError: pass class GenPath(): def __init__(self, verbose=0xff): self.verbose=verbose def swissmx(self,flipx=False,flipy=False,ofs=(0,0),width=1000): 'generathe a path that writes swissfel' #string from inkscape path of the drawing d="m 524.7061,637.31536 3.54883,0 3.54882,0 3.54883,0 0,-4.20801 0,-4.20801 0,-4.208 0,-4.20801 4.22949,0 4.22949,0 4.2295,0 4.22949,0 0,-3.55957 0,-3.55957 0,-3.55957 0,-3.55957 -4.22949,0 -4.2295,0 -4.22949,0 -4.22949,0 0,-4.22949 0,-4.2295 0,-4.22949 0,-4.22949 -3.54883,0 -3.54882,0 -3.54883,0 -3.54883,0 0,4.22949 0,4.22949 0,4.2295 0,4.22949 -4.20752,0 -4.20752,0 -4.20752,0 -4.20752,0 0,3.55957 0,3.55957 0,3.55957 0,3.55957 4.20752,0 4.20752,0 4.20752,0 4.20752,0 0,4.20801 0,4.208 0,4.20801 0,4.20801 -11.87126,0.36152 -12.12171,-0.13934 -2.52941,3.93977 -2.57238,3.94369 -2.50854,3.88614 -2.50731,3.91767 -2.49035,3.88268 -2.50987,3.91244 -2.50453,3.88732 -2.51897,3.9189 -6.39782,5.72802 -6.63782,6.70894 -3.21517,5.11464 -3.3404,5.32333 -3.08995,5.11464 -3.17343,5.15637 -16.69223,0.0698 5.55908,0 5.55909,0 5.55908,0 3.18604,-5.17432 3.18603,-5.17431 3.18604,-5.17432 3.18603,-5.17431 3.17481,5.17431 3.1748,5.17432 3.17481,5.17431 3.1748,5.17432 5.59229,0 5.59228,0 5.59229,0 5.59228,0 -2.74121,-4.15283 -2.74121,-4.15283 -2.74121,-4.15283 -2.74121,-4.15284 -2.74122,-4.15283 -2.74121,-4.15283 -2.74121,-4.15283 -2.74121,-4.15283 2.50488,-3.90015 2.50489,-3.90015 2.50488,-3.90014 2.50488,-3.90015 2.50488,-3.90015 2.50489,-3.90015 2.50488,-3.90014 2.50488,-3.90015 -5.42724,0 -5.42725,0 -5.42724,0 -5.42725,0 -2.76855,4.95508 -2.76856,4.95508 -2.76855,4.95508 -2.76856,4.95508 -2.85644,-4.95508 -2.85645,-4.95508 -2.85644,-4.95508 -2.85645,-4.95508 -5.48193,0 -5.48194,0 -5.48194,0 -5.48193,0 2.52686,3.8562 2.52685,3.8562 2.52686,3.8562 2.52686,3.85621 2.52685,3.8562 2.52686,3.8562 2.52685,3.8562 2.52686,3.8562 -2.77954,4.19678 -2.77954,4.19678 -2.77954,4.19677 -2.77955,4.19678 -2.77954,4.19678 -2.77954,4.19678 -2.77954,4.19677 -2.77954,4.19678 -4.91638,0 -4.91638,0 -4.91639,0 -4.91638,0 -4.91638,0 -4.91638,0 -4.91638,0 -4.91638,0 -4.91639,0 -4.91638,0 -4.91638,0 -4.91638,0 -4.91638,0 -4.91639,0 -4.91638,0 -4.91638,0 4.07568,0 4.07568,0 4.07569,0 4.07568,0 0,-6.14136 0,-6.14135 0,-6.14136 0,-6.14136 0,-6.14136 0,-6.14135 0,-6.14136 0,-6.14136 1.57105,6.14136 1.57104,6.14136 1.57104,6.14135 1.57105,6.14136 1.57105,6.14136 1.57104,6.14136 1.57104,6.14135 1.57105,6.14136 3.68066,0 3.68067,0 3.68067,0 3.68066,0 1.57642,-6.14136 1.57641,-6.14135 1.57642,-6.14136 1.57641,-6.14136 1.57642,-6.14136 1.57642,-6.14135 1.57641,-6.14136 1.57642,-6.14136 0,6.14136 0,6.14136 0,6.14135 0,6.14136 0,6.14136 0,6.14136 0,6.14135 0,6.14136 4.06494,0 4.06494,0 4.06494,0 4.06494,0 0,-8.05298 0,-8.05298 0,-8.05298 0,-8.05297 0,-8.05298 0,-8.05298 0,-8.05298 0,-8.05298 -6.52588,0 -6.52588,0 -6.52587,0 -6.52588,0 -1.25781,4.8999 -1.25782,4.89991 -1.25781,4.8999 -1.25781,4.8999 -1.25781,4.8999 -1.25782,4.89991 -1.25781,4.8999 -1.25781,4.8999 -1.26343,-4.8999 -1.26343,-4.8999 -1.26343,-4.89991 -1.26343,-4.8999 -1.26342,-4.8999 -1.26343,-4.8999 -1.26343,-4.89991 -1.26343,-4.8999 -6.54785,0 -6.54785,0 -6.54785,0 -6.54785,0 0,8.05298 0,8.05298 0,8.05298 0,8.05298 0,8.05297 0,8.05298 0,8.05298 -4.25755,8.13646 -8.40743,0.19687 -8.40743,0.19687 -8.40743,0.19687 -8.40743,0.19687 5.93521,0.22812 8.09742,-0.56079 6.18579,-1.6814 4.55883,-2.66919 3.13062,-3.43823 1.84571,-3.87866 0.61523,-3.98853 -0.58179,-3.83373 -1.74634,-3.50416 -2.802,-2.95581 -3.83472,-2.18676 -5.49316,-1.60401 -7.77832,-1.20849 -7.64649,-1.58204 -1.75781,-2.59179 1.36328,-2.59375 4.4375,-1.09766 5.09766,1.40625 2.19727,3.29492 4.24072,-0.41748 4.24073,-0.41748 4.24072,-0.41748 4.24072,-0.41748 -1.98804,-4.09741 -2.44946,-3.15259 -2.97778,-2.3291 -3.65894,-1.62598 -5.05371,-0.95629 -7.25098,-0.3191 -7.10766,0.41748 -5.50367,1.25244 -4.19677,2.05494 -3.18604,2.91186 -2.01099,3.65796 -0.67065,4.29517 0.61523,3.98852 1.84571,3.5271 2.78002,2.823 3.32935,1.87817 5.06421,1.42822 7.89868,1.56006 7.69141,1.84571 2.02148,2.98828 -1.53906,2.85742 -5.58008,1.53711 -5.27344,-1.36133 -3.07617,-4.52734 -4.43847,0.41748 -4.43848,0.41748 -4.43848,0.41748 -4.43847,0.41748 2.50488,5.95459 4.43848,4.4165 3.18313,1.59592 4.10031,1.14017 -3.65979,0.0939 -5.9713,6e-5 -5.97131,5e-5 -5.9713,6e-5 -5.9713,6e-5 -5.9713,5e-5 -5.97131,6e-5 -5.9713,5e-5 -5.9713,6e-5 5.34491,0.81842 8.09742,-0.56079 6.18579,-1.6814 4.55883,-2.66919 3.13062,-3.43823 1.84571,-3.87866 0.61523,-3.98853 -0.58179,-3.83373 -1.74634,-3.50416 -2.802,-2.95581 -3.83472,-2.18676 -5.49316,-1.60401 -7.77832,-1.20849 -7.64649,-1.58204 -1.75781,-2.59179 1.36328,-2.59375 4.4375,-1.09766 5.09766,1.40625 2.19727,3.29492 4.24072,-0.41748 4.24073,-0.41748 4.24072,-0.41748 4.24072,-0.41748 -1.98804,-4.09741 -2.44946,-3.15259 -2.97778,-2.3291 -3.65894,-1.62598 -5.05371,-0.95629 -7.25098,-0.3191 -7.10766,0.41748 -5.50367,1.25244 -4.19677,2.05494 -3.18604,2.91186 -2.01099,3.65796 -0.67065,4.29517 0.61523,3.98852 1.84571,3.5271 2.78002,2.823 3.32935,1.87817 5.06421,1.42822 7.89868,1.56006 7.69141,1.84571 2.02148,2.98828 -1.53906,2.85742 -5.58008,1.53711 -5.27344,-1.36133 -3.07617,-4.52734 -4.43847,0.41748 -4.43848,0.41748 -4.43848,0.41748 -4.43847,0.41748 2.50488,5.95459 4.43848,4.4165 3.18313,1.59592 4.10031,1.14017 -3.06953,-0.0416 -3.06952,-0.0416 -8.58102,-0.0261 -10.12782,-0.0261 -7.03422,-0.0261 -8.58102,-0.0261 4.47168,0 6.6151,0 2.32826,0 4.47168,0 0,-5.83374 0,-5.83374 0,-5.83374 0,-5.83374 0,-5.83374 0,-5.83374 0,-5.83374 0,-5.83374 -4.47168,0 -4.47168,0 -4.47168,0 0,-5.5796 4.47168,0 4.47168,0 4.47168,0 0,-6.08691 0,-6.08692 -4.47168,0 -4.47168,0 -4.47168,0 -4.47168,0 0,6.08692 0,6.08691 0,5.5796 0,5.83374 0,5.83374 0,5.83374 0,5.83374 0,5.83374 0,5.83374 0,5.83374 -3.67318,5.83374 -8.7308,0 -10.73079,0 -6.7308,0 -9.10563,0 -2.25201,0.007 -8.72971,0.0266 -7.53755,-0.0442 -9.68477,0.0107 -6.3443,0 3.99902,0 3.99902,0 3.99903,0 3.99902,0 2.28516,-7.02002 2.28516,-7.02002 2.28516,-7.02002 2.28516,-7.02002 2.36181,7.02002 2.36182,7.02002 2.36181,7.02002 2.36182,7.02002 3.97705,0 3.97705,0 3.97705,0 3.97705,0 2.14795,-5.83374 2.14795,-5.83374 2.14795,-5.83374 2.14795,-5.83374 2.14795,-5.83374 2.14795,-5.83374 2.14795,-5.83374 2.14795,-5.83374 -4.2959,0 -4.2959,0 -4.2959,0 -4.2959,0 -0.93921,3.67505 -0.93921,3.67505 -0.93921,3.67505 -0.93921,3.67505 -0.9392,3.67504 -0.93921,3.67505 -0.93921,3.67505 -0.93921,3.67505 -1.23047,-3.67505 -1.23047,-3.67505 -1.23047,-3.67505 -1.23047,-3.67504 -1.23046,-3.67505 -1.23047,-3.67505 -1.23047,-3.67505 -1.23047,-3.67505 -4.03223,0 -4.03222,0 -4.03223,0 -4.03223,0 -1.18652,3.67505 -1.18653,3.67505 -1.18652,3.67505 -1.18653,3.67505 -1.18652,3.67504 -1.18652,3.67505 -1.18653,3.67505 -1.18652,3.67505 -0.93921,-3.67505 -0.93921,-3.67505 -0.93921,-3.67505 -0.93921,-3.67504 -0.9392,-3.67505 -0.93921,-3.67505 -0.93921,-3.67505 -0.93921,-3.67505 -4.32862,0 -4.32861,0 -4.32862,0 -4.32861,0 2.16431,5.83374 2.1643,5.83374 2.16431,5.83374 2.16431,5.83374 2.16431,5.83374 2.1643,5.83374 2.16431,5.83374 -3.84635,5.83374 -5.60781,0.003 -5.6078,0.003 -5.60781,0.003 -5.6078,0.003 -5.4839,-1.59358 0,0 5.47119,-3.35034 4.10888,-4.60278 2.5708,-5.4712 0.85694,-5.95459 -0.64868,-5.02123 -1.94507,-4.51587 -3.32837,-3.91114 -4.88843,-3.20801 -7.482173,-2.87842 -5.1337,-1.42273 -6.06186,-1.41174 -6.67969,-2.37304 -1.44922,-2.76758 1.75782,-3.56055 5.22851,-1.49414 6.5918,1.97852 1.99951,2.5708 1.16455,3.75732 4.69141,-0.2749 4.691403,-0.2749 4.6914,-0.27491 4.69141,-0.2749 -0.94483,-4.66918 -1.604,-3.98804 -2.26318,-3.30688 -2.92236,-2.62574 -3.59802,-2.01858 -4.334103,-1.44162 -5.0702,-0.86484 -5.80627,-0.28824 -4.76547,0.1593 -4.23282,0.47791 -6.86695,1.91162 -5.04223,2.98828 -3.61401,3.95507 -2.14283,4.53687 -0.7146,4.82251 1.40625,6.88892 4.21875,5.54858 3.26035,2.31812 4.19986,2.07641 5.13919,1.83472 6.07834,1.59302 6.54785,1.81226 3.64746,1.92211 2.19727,4.48242 -2.33008,4.65821 -6.54688,1.97851 -5.05371,-0.97827 -3.73535,-2.93384 -1.57153,-2.9663 -0.93433,-4.06495 -4.73486,0.29688 -4.73487,0.29687 -4.73486,0.29688 -4.73486,0.29687 0.76065,4.6637 1.44711,4.23523 2.13376,3.80676 2.82059,3.3783 3.79577,2.76855 5.0592,1.97754 6.32264,1.18652 7.58606,0.39551 9.481626,-0.95145 -7.224723,-0.043 -7.224724,-0.043 -7.224723,-0.043 -7.224723,-0.043 -7.224723,-0.043 -7.224723,-0.043 -7.224724,-0.043 -7.224723,-0.043" d=d.split() pts=np.ndarray((len(d)-1,2),dtype=np.float64) for i in range(pts.shape[0]): pts[i,:]=tuple(map(float,d[i+1].split(','))) pts[0,:]=(0,0) pts=pts.cumsum(0) pts=pts[::-1,:] if flipx: pts[:,0]=-pts[:,0] if not flipy: pts[:,1]=-pts[:,1] pts=pts-pts.min(0) pts*=width/pts[:,0].max() pts+=ofs self.points=pts def swissfel(self,flipx=False,flipy=False,ofs=(0,0),width=1000): 'generathe a path that writes swissfel' #string from inkscape path of the drawing d="m 15.801613,951.54022 -1.655274,-0.17578 -1.809082,-0.52002 0,-1.52344 1.765137,0.76172 1.699219,0.25635 1.955566,-0.49805 0.688477,-1.4209 -0.498047,-1.25976 -1.618652,-0.68115 -0.900879,-0.17578 -1.426392,-0.42298 -0.968628,-0.60974 -0.739746,-1.95557 0.254516,-1.29638 0.76355,-0.98877 1.217652,-0.62622 1.602173,-0.20874 1.567383,0.13916 1.6333,0.41748 0,1.44287 -1.589355,-0.60059 -1.442871,-0.19775 -1.071167,0.11719 -0.796509,0.35156 -0.651856,1.33301 0.432129,1.09863 1.655274,0.59326 0.893555,0.18311 1.437377,0.43579 1.001587,0.67749 0.593262,0.92651 0.197754,1.19751 -0.267334,1.3971 -0.802002,1.01257 -1.311035,0.61523 2.995605,-0.004 2.995606,-0.004 -0.536499,-2.05078 -0.536499,-2.05078 -0.536499,-2.05079 -0.536499,-2.05078 1.347656,0 0.421142,1.60034 0.421143,1.60034 0.421143,1.60035 0.421142,1.60034 0.419312,-1.60034 0.419311,-1.60035 0.419312,-1.60034 0.419311,-1.60034 1.589356,0 0.421142,1.60034 0.421143,1.60034 0.421143,1.60035 0.421142,1.60034 0.419312,-1.60034 0.419311,-1.60035 0.419312,-1.60034 0.419311,-1.60034 1.347657,0 -0.536499,2.05078 -0.5365,2.05079 -0.536499,2.05078 -0.536499,2.05078 -1.589355,0 -0.441284,-1.68091 -0.441285,-1.68091 -0.441284,-1.6809 -0.441284,-1.68091 -0.443115,1.68091 -0.443116,1.6809 -0.443115,1.68091 -0.443115,1.68091 2.330933,-8e-5 2.330933,-8e-5 2.330932,-8e-5 2.330933,-8e-5 0,-2.05078 0,-2.05078 0,-2.05079 0,-2.05078 0.673828,-1.48681 0.673828,0 0,-0.85327 0,-0.85327 -0.673828,0 -0.673828,0 0,0.85327 0,0.85327 0.673828,1.48681 0.673828,0 0,2.05078 0,2.05079 0,2.05078 0,2.05078 2.545166,0.1062 2.545166,0.1062 -1.376953,-0.13183 -1.501465,-0.38086 0,-1.3916 1.472168,0.58593 1.435547,0.19043 1.464844,-0.32226 0.512695,-0.92285 -0.373535,-0.84229 -1.038789,-0.41935 -1.048614,-0.25448 -1.078491,-0.33325 -0.730591,-0.47241 -0.55664,-1.50147 0.205078,-1.02539 0.615234,-0.76172 0.99243,-0.47241 1.336672,-0.15747 1.40625,0.10986 1.21582,0.32959 0,1.27442 -1.186523,-0.43946 -1.274414,-0.14648 -1.508789,0.30762 -0.498047,0.92285 0.358886,0.73975 0.763094,0.30189 0.76857,0.22893 0.785513,0.20842 0.813927,0.24037 0.809326,0.49988 0.455932,0.66833 0.151978,0.89173 -0.227051,1.02355 -0.681152,0.78553 -1.071167,0.49988 3.205262,0.0833 3.205261,0.0833 -1.376953,-0.13183 -1.501465,-0.38086 0,-1.3916 1.472168,0.58593 1.435547,0.19043 1.464843,-0.32226 0.512696,-0.92285 -0.373536,-0.84229 -1.038789,-0.41935 -1.048613,-0.25448 -1.078491,-0.33325 -0.730591,-0.47241 -0.556641,-1.50147 0.205078,-1.02539 0.615235,-0.76172 0.99243,-0.47241 1.336672,-0.15747 1.40625,0.10986 1.21582,0.32959 0,1.27442 -1.186524,-0.43946 -1.274414,-0.14648 -1.508789,0.30762 -0.498047,0.92285 0.358887,0.73975 0.805073,0.3095 0.76935,0.22988 0.764915,0.20509 0.791765,0.23514 0.809327,0.49988 0.455932,0.66833 0.151978,0.89173 -0.227051,1.02355 -0.681152,0.78553 -1.071167,0.49988 2.095642,-0.0229 2.095642,-0.0229 0,-1.36688 0,-1.36688 0,-1.36689 0,-1.36688 0,-1.36688 0,-1.36688 0,-1.36689 0,-1.36688 1.571045,0 1.571045,0 1.571045,0 1.571045,0 0,1.24512 -1.201172,0 -1.201172,0 -1.201172,0 -1.201172,0 0,0.80566 0,0.80567 0,0.80566 0,0.80566 1.083984,0 1.083985,0 1.083984,0 1.083985,0 0,1.24512 -1.083985,0 -1.083984,0 -1.083985,0 -1.083984,0 0,1.30554 0,1.30555 0,1.30554 0,1.30554 3.581543,0 3.581543,0 0,-1.36688 0,-1.36688 0,-1.36689 0,-1.36688 0,-1.36688 0,-1.36688 0,-1.36689 0,-1.36688 1.728516,0 1.728516,0 1.728515,0 1.728516,0 0,1.24512 -1.358643,0 -1.358642,0 -1.358643,0 -1.358643,0 0,0.80932 0,0.80933 0,0.80933 0,0.80932 1.30188,0 1.30188,0 1.30188,0 1.30188,0 0,1.24512 -1.30188,0 -1.30188,0 -1.30188,0 -1.30188,0 0,0.9906 0,0.9906 0,0.9906 0,0.9906 1.391602,0 1.391601,0 1.391602,0 1.391602,0 0,1.24512 -3.587581,3.8e-4 -1.964972,0 3.702844,0 4.295998,0 0,-1.36733 0,-1.36688 0,-1.36689 0,-1.36688 0,-1.36688 0,-1.36689 0,-1.36688 0,-1.36688 1.479492,0 0,1.21124 0,1.21124 0,1.21125 0,1.21124 0,1.21124 0,1.21124 0,1.21125 0,1.21124 1.331177,0 1.331177,0 1.331176,0 1.331177,0 0,1.24512 -1.70105,0 -1.701049,0 -1.70105,0" d=d.split() pts=np.ndarray((len(d)-1,2),dtype=np.float64) for i in range(pts.shape[0]): pts[i,:]=tuple(map(float,d[i+1].split(','))) pts[0,:]=(0,0) pts=pts.cumsum(0) pts=pts[::-1,:] if flipx: pts[:,0]=-pts[:,0] if not flipy: pts[:,1]=-pts[:,1] pts=pts-pts.min(0) pts*=width/pts[:,0].max() pts+=ofs self.points=pts def rand(self,n=107,scale=1000,ofs=(0,0)): 'generate random distributed points' np.random.seed(0) #data=np.random.randint(0,1000,(30,2)) pts=np.random.rand(n,2)*scale pts+=ofs self.points=pts def grid(self,w=10,h=10,pitch=100,rnd=.2,ofs=(0,0)): 'generates points in a grid with a given pitch and a bit randomness' np.random.seed(0) xx,yy=np.meshgrid(range(w), range(h)) pts=np.array([xx.reshape(-1),yy.reshape(-1)],dtype=np.float64).transpose()*pitch if rnd != 0: pts+=(np.random.rand(pts.shape[0],2)*(rnd*pitch)) pts+=ofs self.points=pts def spiral(self,rStart=1.,rInc=.2,numSeg=4,numCir=6, phase=0, ofs=(0, 0)): #rInc radius increment per circle r=rStart+np.arange(numSeg*numCir)*(float(rInc)/numSeg) ang=2.*np.pi/numSeg*np.arange(numSeg*numCir)+phase*np.pi/180 pts=np.vstack((np.sin(ang)*r,np.cos(ang)*r)).T pts+=ofs self.points=pts def closed_shifted(self,pitch=100,shift=5,mult=3): 'from the given points, close the path, and runs 9 times with small pitch' pts=self.points pts = np.vstack((pts, pts[-1]+(0,-50)))#add a new point outside of the grid mn=pts.min(0) mx=pts.max(0) d=pts[0,:]-pts[-1,:] l=np.sum(d) bk=[] if abs(d[1])>pitch: #make a vertical back move s=np.sign(d[1]) n=int(np.ceil(np.abs(d[1])/pitch)) p=np.ndarray((n,2)) p[:,0]=pts[-1, 0] p[:,1]=pts[-1, 1]+np.arange(1,n+1)*s*pitch pts=np.vstack((pts,p)) if abs(d[0])>pitch: #make a horizonlat back move s=np.sign(d[0]) n=int(np.ceil(np.abs(d[0])/pitch)) p=np.ndarray((n,2)) p[:,0]=pts[-1, 0]+np.arange(1,n+1)*s*pitch p[:,1]=pts[-1, 1] pts[-1, :] pts=np.vstack((pts,p)) stack=[] for y in np.arange(mult)*shift: for x in np.arange(mult)*shift: stack.append(pts+(x,y)) pts=np.vstack(stack) if self.verbose&0x02: DebugPlot.plot_points(pts) plt.show(block=False) self.points=pts def sort(self,mode=1,grp_sz=None): ''' sorts self.points in forward backward scanning groups :param mode: 0:X fast Y slow 1:Y fast X slow :param grp_sz: size of group to scan in one direction ''' pts=self.points cnt=pts.shape[0] idx=np.ndarray(cnt,dtype=np.int32) if grp_sz is None: grp_cnt=int(np.sqrt(cnt)) grp_sz=int(np.ceil(float(cnt)/grp_cnt)) else: grp_sz=int(grp_sz) grp_cnt=int(np.ceil(float(cnt)/grp_sz)) if mode==0: idxA=1;idxB=0 else: idxA=0;idxB=1 #sort points along idxA pts=pts[pts[:,idxA].argsort()] #group sorting along idxB for i in range(grp_cnt): a=i*grp_sz #print a,a+grp_sz if i%2: idx[a:a+grp_sz]=a+pts[a:a+grp_sz,idxB].argsort()[::-1] else: idx[a:a+grp_sz]=a+pts[a:a+grp_sz,idxB].argsort() #print(idx) pts=pts[idx] self.points=pts 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 mot_pts: numpy array of raw motor points (motor values not X,Y-cooridnate values) ''' def __init__(self,comm, gather, verbose,**kwargs): MotionBase.__init__(self,comm, gather, verbose, **kwargs) def setup_gather(self,acq_per=None,numPts=None): ''' setup the channels to gather kwargs: acq_per : acquire period: acquire data all acq_per servo loops (default=None->calc best, =1-> fastest) ''' comm=self.comm if comm is None: _log.info('simulated');return gt=self.gather gt.set_phasemode(False) meta=self.meta if meta['sync_flag']&2: address=("Motor[1].ActPos","Motor[2].ActPos","Motor[1].DesPos","Motor[2].DesPos","Coord[1].Q[11]") else: address=("Motor[1].ActPos","Motor[2].ActPos","Motor[1].DesPos","Motor[2].DesPos","Gate3[1].Chan[1].UserFlag") #address=("Motor[1].ActPos","Motor[2].ActPos","Motor[1].DesPos","Motor[2].DesPos","Gate3[1].Chan[1].UserFlag","EncTable[20].PrevEnc") #address=("Motor[1].ActPos", "Motor[2].ActPos", "Motor[1].DesPos", "Motor[2].DesPos", "Gate3[1].Chan[1].UserFlag","Coord[1].TimeBase","EncTable[20].DeltaPos") gtMaxLn=gt.set_address(*address) if acq_per is None: if numPts is None: numPts=self.mot_pts.shape[0] ovhdTime=100 acq_per=int(np.ceil((meta['pt2pt_time']*numPts+ovhdTime)/(gtMaxLn*meta['srv_per']))) gt.set_property(MaxSamples=1000000, Period=acq_per) #gt.set_property(Period=acq_per) meta.update({'acq_per':acq_per,'address':address}) def setup_coord_trf(self,fx='X',fy='Y',cz='0'): # FY:1, FX:2, ROT_Y:3, CX:4, CZ:5, #0X 0Y 0Z are not allowed in the coordinates !!! ptrn='[+-][0]*[XYZ]' fx=re.sub(ptrn,'',fx) fy=re.sub(ptrn,'',fy) cz=re.sub(ptrn,'',cz) _log.info(f'\n fx->{fx}\n fy->{fy}\n cz->{cz}') comm = self.comm if comm is None: _log.info('simulated');return prg=f'''&1a &1#3->0 &1#4->0 &1#6->0 &1#7->0 &0#3->0 &0#4->0 &0#6->0 &0#7->0 &1 #1->{fy} #2->{fx} #5->{cz} #1,2,5j/ ''' comm.gpascii.send_block(prg) def setup_motion(self,prgId=2,fnPrg=None,mode=0,**kwargs): ''' generates program and saves to fnPrg the type of generated program is defined by $ -> the list af all points that will be moved at, is in 'mot_pts' common kwargs: scale : scaling velocity (default=1. value=0 would stop at each point cnt : move path multiple times (default=1) dwell : dwell time at end (default=100ms) mode:0 unused mode:1 pvt motion common kwargs plus: points : point list trf : optional transformation that will be done on 'points', mot_pts=trf*points mode:2 unused mode:3 pvt motion using inverse fft velocity common kwargs plus: points : point list trf : optional transformation that will be done on 'points', mot_pts=trf*points numPad : number of padding points to reduce aliasing (default=16) mode:4 pvt motion short code using grid parameters common kwargs plus: trf : transformation that will be done on 'grid points' grid: grid parameters: {orig:(0,0),pitch(10,10),cnt:(10,10),mode:0} mode:5 pvt motion short code using grid parameters. Instead of continous motion it moves and waits as give in the parameterstime common kwargs plus: trf : transformation that will be done on 'grid points' grid: grid parameters: {orig:(0,0),pitch(10,10),cnt:(10,10),mode:0} tmove: time to move in ms (move start on FEL-trigger twait: time to wait in ms (tmove+twait will be rounded to a multiple of fel_per) ''' #scan=0 # snake motion X fast, Y slow scan=1 # snake motion Y fast, X slow (default) prg=f'close all buffers\nopen prog {prgId}\n Coord[1].DesTimeBase=Sys.ServoPeriod\n' verb=self.verbose comm=self.comm meta=self.meta try: self.sync_prg except AttributeError: _log.warning('missing motion sync code!') # this uses Coord[1].Tm and limits with MaxSpeed if mode in (1,3,4,5): #### pvt motion if mode!=5: pt2pt_time=meta['pt2pt_time']=meta['fel_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 pt=kwargs['points'] try: trf=kwargs['trf'] except KeyError as e: self.mot_pts=pt else: self.mot_pts=(np.hstack((pt, np.ones((pt.shape[0], 1))))*np.asmatrix(trf)).A # pt*trf #pv is an array of posx posy velx vely pv=np.ndarray(shape=(pt.shape[0]+2,4),dtype=pt.dtype) pv[:]=np.NaN pv[ 0,(0,1)]=pt[0,:] pv[ 1:-1,(0,1)]=pt pv[ -1,(0,1)]=pt[-1,:] pv[(0,0,-1,-1),(2,3,2,3)]=0 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 numPad=kwargs.get('numPad', 16) p=np.hstack((pt.T,pt[-1,:].repeat(numPad).reshape(2,-1))) k=p.shape[1] stp=((p[:,-1]-p[:,0])/(k-1)) #calculate steepness point to point #stp*=0 p[0,:]-=stp[0]*np.arange(k) p[1,:]-=stp[1]*np.arange(k) f=np.fft.fftfreq(k, d=1.) pf=np.fft.fft(p) pfd=pf*f*1j # differentiate in fourier pd=np.fft.ifft(pfd) v=pd.real.T/pt2pt_time*np.pi*2+stp/pt2pt_time if numPad==0: n=None else: n=-numPad pv[ 1:-1,(2,3)] = v[:n]*scale if verb&0x20: if 'trf' in kwargs: _log.warning('correct ploting of pvt only works without transformation !') else: dp=DebugPlot(self);self.pvt=dp.plot_gen_pvt(pv) plt.show(block=False) pv[1:-1, (2, 3)]*=CoordFeedTime #scaling for Deltatau prg+=' linear abs\n X%g Y%g\n' % tuple(pv[0, (0,1)]) else: # mode==(4,5): #### pvt motion, short code using grid parameters g=kwargs['grid'] nx, ny=g['count'] xx, yy=np.meshgrid(range(nx), range(ny)) if scan==0: # snake motion X fast, Y slow for i in range(1,ny,2): xx[i]=xx[i][::-1] else: #scan==1 snake motion Y fast, X slow xx=xx.T yy=yy.T for i in range(1, nx, 2): yy[i]=yy[i][::-1] pt=np.array([xx.reshape(-1), yy.reshape(-1)], dtype=np.float64).transpose() #*pitch try: trf=kwargs['trf'] except KeyError as e: ox, oy=g['pos'] px, py=g['pitch'] #self.mot_pts=pt self.mot_pts=pt*np.array(g['pitch'], np.float)+np.array(g['pos'], np.float) else: ox, oy=(0,0) px, py=(1,1) self.mot_pts=(np.hstack((pt, np.ones((pt.shape[0], 1))))*np.asmatrix(trf)).A # pt*trf #prg+=f' linear abs\n X{ox:g} Y{oy:g}\n' prg+=f' linear abs\n X{ox-px:g} Y{oy-py:g}\n' # start one position out of grid 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' if mode in (1,3): prg+=' pvt%g abs\n'%pt2pt_time # 100ms to next position for idx in range(1,pv.shape[0]): #prg+=f'N{idx} ' + 'X%g:%g Y%g:%g\n'%tuple(pv[idx,(0,2,1,3)]) prg+=f' X%g:%g Y%g:%g\n'%tuple(pv[idx, (0, 2, 1, 3)]) prg+=f' X{pv[-1, 0]:g} Y{pv[-1, 1]:g}\n' elif mode==4: if scan==0: raise Exception('scan=0 not supported') pass else: # scan=1 vx=px/(pt2pt_time)*scale*CoordFeedTime #scaling for Deltatau vy=py/(pt2pt_time)*scale*CoordFeedTime #scaling for Deltatau prg+=f'''\ pvt{pt2pt_time} abs //grid pvt motion L1=0 //slow loop x L0=0 //fast loop y while(L1<{nx}) {{ //send 1"A:move X%g:%g Y%g:%g",{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%g:%g Y%g:%g",{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%g:%g Y%g:%g",{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%g:%g Y%g:%g",{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%g:%g Y%g:%g",{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%g:%g Y%g:%g",{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%g:%g Y%g:%g",{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%g:%g Y%g:%g",{ox}+L1*{px},{0:g},{oy}+L0*{py},{0:g} X({ox}+L1*{px}):{0:g} Y({oy}+L0*{py}):{0:g} ''' elif mode==5: if scan==0: raise Exception('scan=0 not supported') pass else: # scan=1 if meta['sync_mode']==2: _log.error('sync_mode=2 not allowed for stop-and-go motion !') tmove=kwargs['tmove'] twait=kwargs['twait'] fel_per=meta['fel_per'] pt2tp_felpulse=round((tmove+twait)/fel_per) # number of fel-pulses for a whole cycle (tmove+twait) meta['pt2pt_time']=fel_per*pt2tp_felpulse twait_=round((tmove+twait)/fel_per)*fel_per-tmove if twait!=twait_: _log.warning(f'adjust twait({twait}) to {twait_} to match multiple of fel-cycles') syncPlc=f'''\ close all buffers disable plc 2 open plc 2 Sys.Udata[0]=0 Sys.Udata[1]=0 while(1) {{ while (Gate3[1].Chan[1].UserFlag==0){{}} Sys.Udata[0]=Sys.Udata[0]+1 if (Sys.Udata[0]>={pt2tp_felpulse}) {{ Sys.Udata[1]=Sys.Udata[1]+1 Sys.Udata[0]=0 }} while (Gate3[1].Chan[1].UserFlag==1){{}} }} close enable plc 2 ''' comm.gpascii.send_block(syncPlc, verb&0x08) # prg+=f'''\ # linear abs # Motor[1].JogTa=-1 // closer to 0 harder acceleration # Motor[2].JogTa=-1 # Motor[5].JogTa=-10 # Coord[1].Ta=00000.1 // 455 # Coord[1].Td=00000.1 # Coord[1].Ts=0 # jog1=-1080 # dwell 50 # jog1=-1200 # dwell 50 # jog1=-1080 # dwell 50 # jog1=-1200 # dwell 50 # //grid stop-and-go motion # L1=0 //slow loop x # L0=0 //fast loop y # while(L1<{nx}) # {{ # //send 1"A:move X%g Y%g",{ox}+L1*{px},{oy}+L0*{py} # X({ox}+L1*{px}) Y({oy}+L0*{py}) # while(Sys.Udata[2]==Sys.Udata[1]){{}};Sys.Udata[2]=Sys.Udata[1] // wait motion trigger # L0+=1 # while(L0<{ny}-1) # {{ # //send 1"B:move X%g Y%g",{ox}+L1*{px},{oy}+L0*{py} # X({ox}+L1*{px}) Y({oy}+L0*{py}) # while(Sys.Udata[2]==Sys.Udata[1]){{}};Sys.Udata[2]=Sys.Udata[1] // wait motion trigger # L0+=1 # }} # if(L1>={nx}-1) # {{ # break # }} # //send 1"C:move X%g Y%g",{ox}+L1*{px},{oy}+L0*{py} # X({ox}+L1*{px}) Y({oy}+L0*{py}) # while(Sys.Udata[2]==Sys.Udata[1]){{}};Sys.Udata[2]=Sys.Udata[1] // wait motion trigger # L1+=1 # //send 1"D:move X%g Y%g",{ox}+L1*{px},{oy}+L0*{py} # X({ox}+L1*{px}) Y({oy}+L0*{py}) # while(Sys.Udata[2]==Sys.Udata[1]){{}};Sys.Udata[2]=Sys.Udata[1] // wait motion trigger # L0-=1 # while(L0>=1) # {{ # //send 1"E:move X%g Y%g",{ox}+L1*{px},{oy}+L0*{py} # X({ox}+L1*{px}) Y({oy}+L0*{py}) # while(Sys.Udata[2]==Sys.Udata[1]){{}};Sys.Udata[2]=Sys.Udata[1] // wait motion trigger # L0-=1 # }} # if(L1>={nx}-1) # {{ # break # }} # //send 1"F:move X%g Y%g",{ox}+L1*{px},{oy}+L0*{py} # X({ox}+L1*{px}) Y({oy}+L0*{py}) # while(Sys.Udata[2]==Sys.Udata[1]){{}};Sys.Udata[2]=Sys.Udata[1] // wait motion trigger # L1+=1 # //send 1"G:move X%g Y%g",{ox}+L1*{px},{oy}+L0*{py} # X({ox}+L1*{px}) Y({oy}+L0*{py}) # while(Sys.Udata[2]==Sys.Udata[1]){{}};Sys.Udata[2]=Sys.Udata[1] // wait motion trigger # L0+=1 # }} # //send 1"H:move X%g Y%g",{ox}+L1*{px},{oy}+L0*{py} # X({ox}+L1*{px}) Y({oy}+L0*{py}) # #''' prg+=f'''\ pvt{tmove} abs //grid pvt motion L1=0 //slow loop x L0=0 //fast loop y while(L1<{nx}) {{ //send 1"A:move X%g:%g Y%g:%g",{ox}+L1*{px},0,{oy}+L0*{py},0 X({ox}+L1*{px}):0 Y({oy}+L0*{py}):0 while(Sys.Udata[2]==Sys.Udata[1]){{}};Sys.Udata[2]=Sys.Udata[1] // wait motion trigger L0+=1 while(L0<{ny}-1) {{ //send 1"B:move X%g:%g Y%g:%g",{ox}+L1*{px},0,{oy}+L0*{py},0 X({ox}+L1*{px}):0 Y({oy}+L0*{py}):0 while(Sys.Udata[2]==Sys.Udata[1]){{}};Sys.Udata[2]=Sys.Udata[1] // wait motion trigger L0+=1 }} if(L1>={nx}-1) {{ break }} //send 1"C:move X%g:%g Y%g:%g",{ox}+L1*{px},0,{oy}+L0*{py},0 X({ox}+L1*{px}):0 Y({oy}+L0*{py}):0 while(Sys.Udata[2]==Sys.Udata[1]){{}};Sys.Udata[2]=Sys.Udata[1] // wait motion trigger L1+=1 //send 1"D:move X%g:%g Y%g:%g",{ox}+L1*{px},0,{oy}+L0*{py},0 X({ox}+L1*{px}):0 Y({oy}+L0*{py}):0 while(Sys.Udata[2]==Sys.Udata[1]){{}};Sys.Udata[2]=Sys.Udata[1] // wait motion trigger L0-=1 while(L0>=1) {{ //send 1"E:move X%g:%g Y%g:%g",{ox}+L1*{px},0,{oy}+L0*{py},0 X({ox}+L1*{px}):0 Y({oy}+L0*{py}):0 while(Sys.Udata[2]==Sys.Udata[1]){{}};Sys.Udata[2]=Sys.Udata[1] // wait motion trigger L0-=1 }} if(L1>={nx}-1) {{ break }} //send 1"F:move X%g:%g Y%g:%g",{ox}+L1*{px},0,{oy}+L0*{py},0 X({ox}+L1*{px}):0 Y({oy}+L0*{py}):0 while(Sys.Udata[2]==Sys.Udata[1]){{}};Sys.Udata[2]=Sys.Udata[1] // wait motion trigger L1+=1 //send 1"G:move X%g:%g Y%g:%g",{ox}+L1*{px},0,{oy}+L0*{py},0 X({ox}+L1*{px}):0 Y({oy}+L0*{py}):0 while(Sys.Udata[2]==Sys.Udata[1]){{}};Sys.Udata[2]=Sys.Udata[1] // wait motion trigger L0+=1 }} //send 1"H:move X%g:%g Y%g:%g",{ox}+L1*{px},{0:g},{oy}+L0*{py},{0:g} X({ox}+L1*{px}):{0:g} Y({oy}+L0*{py}):{0:g} while(Sys.Udata[2]==Sys.Udata[1]){{}};Sys.Udata[2]=Sys.Udata[1] // wait motion trigger ''' else: raise ValueError('unsupported mode') #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&0x02: DebugPlot.plot_points(self.mot_pts) plt.show(block=False) if verb&0x04: print(prg) if fnPrg is not None: fh=open(fnPrg,'w') fh.write(prg) fh.close() if comm is None: _log.info('simulated') else: comm.gpascii.send_block(prg,verb&0x08) self.prg=prg def gather_upload(self,fnRec=None): gt=self.gather comm=self.comm gt.wait_stopped(verbose=True) self.rec=rec=gt.upload() try: syncShell=self.syncShell except AttributeError: pass else: print(syncShell.sync()) del self.syncShell pts=self.mot_pts ofsy=comm.gpascii.get_variable("Motor[1].HomePos", type_=float) ofsx=comm.gpascii.get_variable("Motor[2].HomePos", type_=float) rec[:,(0,2)]-=ofsy rec[:,(1,3)]-=ofsx if fnRec: np.savez_compressed(fnRec, rec=rec, pts=pts, meta=self.meta) if __name__=='__main__': logging.getLogger('pbtools.misc.pp_comm').setLevel(logging.INFO) logging.getLogger('paramiko').setLevel(logging.WARNING) logging.getLogger('matplotlib').setLevel(logging.INFO) #https://docs.python.org/3/library/logging.html#logging.Formatter add %(name)s to identify the logger logging.basicConfig(level=logging.DEBUG, format='%(levelname)s:%(module)s:%(lineno)d:%(funcName)s:%(message)s ') import argparse def unique_filename(fnBase): i = 0; while (True): fn=fnBase+('%0.3d'%i) i+=1 if not os.path.exists(fn+'.npz'): print('save to '+fn+'.*') break return fn def run_test(args): if args.host: hpp=args.host.split(':') param={'host':hpp[0]} if len(hpp)>1: param['port']=int(hpp[1]) if len(hpp)>2: param['fast_gather_port']=int(hpp[2]) _log.info(' -> ssh-tunneling PPComm({host}:{port} {host}:{fast_gather_port})'.format(**param)) comm=PPComm(**param) gather=Gather(comm) else: comm=gather=None gp=GenPath() #real start and frame trigger with sync #sp = ShapePath(comm, gather, args.verbose) # direct start #sp = ShapePath(comm, gather, args.verbose,fel_per=10,sync_mode=0) #simulated start and frame trigger no sync #sp = ShapePath(comm, gather, args.verbose,fel_per=10,sync_mode=1,sync_flag=3) #simulated start and frame trigger with sync #sp = ShapePath(comm, gather, args.verbose,fel_per=10,sync_mode=2,sync_flag=3) #simulated start real frame trigger no sync sp = ShapePath(comm, gather, args.verbose,fel_per=10,sync_mode=1,sync_flag=1) #simulated start real frame trigger with sync #sp = ShapePath(comm, gather, args.verbose,fel_per=10,sync_mode=2,sync_flag=1) fn='/tmp/shapepath' #fn =unique_filename('ShapePathAnalyser/records/19_01_24/spiral') # Gather.MaxLines=116508 # ts=0.2ms # max_num_points=(MaxLines*ts-1000ms)/(+acq_per*fel_per*ts) # fel_per acq_per maxpts # 40ms 1 555 # 40ms 2 1135 # 40ms 3 1715 # 40ms 4 2295 # 10ms 1 2220 # 10ms 2 4540 # 10ms 3 6860 # 10ms 4 9180 #gp.grid(w=6,h=6,pitch=100,rnd=0,ofs=(0,0));gp.sort() #gp.grid(w=100,h=100,pitch=10,rnd=.2) #gp.swissfel(width=1000,ofs=(-500,0));gp.sort() #gp.grid(w=10,h=10,pitch=50,rnd=.2) #gp.closed_shifted() #gp.grid(w=100,h=100,pitch=50,rnd=.2) #gp.swissmx(width=1000,ofs=(-500,0)) #gp.swissfel(width=1000,ofs=(-500,0)) #gp.rand(n=20, scale=100,ofs=(-950,+2780));gp.sort() #gp.rand(n=200, scale=100,ofs=(0,+2000));gp.sort() #gp.swissmx(width=1000, ofs=(-500, 0)) #gp.spiral(rStart=100,rInc=10,numSeg=4,numCir=60, ofs=(0, 0)) #gp.spiral(rStart=100,rInc=130,numSeg=4,numCir=2, ofs=(0, 0)) #gp.grid(w=20,h=20,pitch=100,rnd=0,ofs=(0,+2000));gp.sort() #gp.grid(w=5,h=10,pitch=100,rnd=0,ofs=(0,+2000));gp.sort(grp_sz=10) #gp.grid(w=50,h=50,pitch=120,rnd=0,ofs=(0,+2000));gp.sort(grp_sz=50) #gp.grid(w=16,h=16,pitch=120,rnd=0,ofs=(0,+2000));gp.sort(grp_sz=16) #12.5x12.5 #gp.grid(w=78,h=78,pitch=120,rnd=0,ofs=(-10000,-12000));gp.sort(grp_sz=78) #23.0x23.0 -> only 3 data points from shot to shot: 6,9,12,14,17,20... #gp.grid(w=162,h=162,pitch=120,rnd=0,ofs=(-10000,-12000));gp.sort(grp_sz=162) #gp.grid(w=1,h=10,pitch=100,rnd=0,ofs=(0,0)) #gp.spiral(rStart=100,rInc=20,numSeg=8,numCir=32, ofs=(0, 0)) #gp.spiral(rStart=100,rInc=10,numSeg=2,numCir=32, phase=45, ofs=(0, 0)) #gp.spiral(rStart=100,rInc=10,numSeg=4,numCir=32, ofs=(0, 0)) #gp.closed_shifted() #gp.grid(w=10,h=15,pitch=120,rnd=0,ofs=(-1000,-1200));gp.sort(mode=0,grp_sz=10) gp.grid(w=10,h=15,pitch=120,rnd=0,ofs=(-1000,-1200));gp.sort(grp_sz=15) # for motion mode 4,5 grid={'pos':(-1000, -1200), 'pitch':(120, 120), 'count':(10, 15)} # mode:0 unused # mode:1 pvt motion # mode:2 unused # mode:3 pvt motion using inverse fft velocity # mode:4 pvt motion short code using grid parameters # mode:5 pvt motion short code using grid parameters. Instead of continous motion it moves and waits as give in the parameter time #mode=3;scale=0 mode=5 if mode==5: tmove=20 # time to move in ms twait=130# time to move in ms try: num_pts=gp.points.shape[0] except AttributeError as e: num_pts=np.array(grid['count'], np.int32).prod() if sp.comm: sp.setup_sync(verbose=args.verbose&0x40,timeOfs=0.03,timeCor=0.0005) sp.setup_coord_trf() # reset to shape path system if mode==1: sp.setup_motion(fnPrg=fn+'.prg',scale=1.,cnt=1,dwell=100,mode=mode,points=gp.points) elif mode==3: sp.setup_motion(fnPrg=fn+'.prg',scale=1.,cnt=1,dwell=100,mode=mode,points=gp.points) elif mode==4: sp.setup_motion(fnPrg=fn+'.prg',scale=1.,cnt=1,dwell=100,mode=4,grid=grid) elif mode==5: sp.setup_motion(fnPrg=fn+'.prg',scale=1.,cnt=1,dwell=100,mode=5,tmove=tmove, twait=twait, grid=grid) if sp.comm: sp.setup_gather() sp.homing() #homing if needed sp.run() #start motion program _log.info('wait_armed') sp.wait_armed() # wait until motors are at first position _log.info('trigger') sp.trigger(0.5) #send a start trigger (if needed) ater given time _log.info('progress..') if not comm is None: while True: p=sp.progress() if p<0: break _log.info('progress %d/%d'%(p,sp.mot_pts.shape[0]));time.sleep(.1) sp.gather_upload(fnRec=fn+'.npz') dp=DebugPlot(sp);dp.plot_gather(mode=11) _log.info('done') plt.show(block=False) input('press return') def parse_args(): 'main command line interpreter function' (h, t)=os.path.split(sys.argv[0]);cmd='\n '+(t if len(h)>3 else sys.argv[0])+' ' exampleCmd=('-v15', '--host=SAR-CPPM-EXPMX1 -v 0x5d', '--host=localhost:10001:10002 -v 0x59' ) epilog=__doc__+'\nExamples:'+''.join(map(lambda s:cmd+s, exampleCmd))+'\n ' parser=argparse.ArgumentParser(epilog=epilog, formatter_class=argparse.RawDescriptionHelpFormatter) parser.add_argument("-m", "--mode", type=lambda x:int(x, 0), help="mode (see bitmasks) default=0x%(default)x", default=0xff) parser.add_argument('-v', '--verbose', type=lambda x:int(x, 0), dest='verbose', help='verbosity bits (see below) default=0x%(default)x', default=0x00) parser.add_argument('--host', help='hostname', default=None) args=parser.parse_args() _log.info('Arguments:{}'.format(args.__dict__)) run_test(args) #------------------ Main Code ---------------------------------- #dp=DebugPlot('/tmp/shapepath.npz');dp.plot_gather(mode=11);plt.show() #exit(0) #ssh_test() ret=parse_args() exit(ret)