#!/usr/bin/env python # *-----------------------------------------------------------------------* # | | # | Copyright (c) 2017 by Paul Scherrer Institute (http://www.psi.ch) | # | | # | Author Thierry Zamofing (thierry.zamofing@psi.ch) | # *-----------------------------------------------------------------------* ''' tools to setup and execute a helical scan of a cristal #THIS IS JUST TESTING CODE TO SOLVE FINDING THE ROTATION CENTER ''' import os, sys, json import numpy as np import matplotlib as mpl import matplotlib.pyplot as plt from utilities import * class HelicalScan: def __init__(self,args): if args.cfg: fh=open(args.cfg,'r') s=fh.read() cfg=json.loads(s, object_hook=ConvUtf8) s=json.dumps(cfg, indent=2, separators=(',', ': '));print(s) else: fn='/tmp/shapepath4' #fn='/home/zamofing_t/Documents/prj/SwissFEL/epics_ioc_modules/ESB_MX/python/data/'+time.strftime('%y-%m-%d-%H_%M_%S') #cfg = {"sequencer": ['gen_grid_points(w=5,h=5,pitch=100,rnd=0.4)', 'sort_points()','gen_prog(file="'+fn+'.prg",host="SAR-CPPM-EXPMX1",mode=1,pt2pt_time=10,cnt=1)', 'plot_gather("'+fn+'.npz")']} #cfg = {"sequencer": ['test_find_rot_ctr()']} #cfg = {"sequencer": ['test_find_rot_ctr(n=5. ,per=1.,bias=2.31,ampl=4.12,phi=24.6)']} cfg = {"sequencer": ['test_coord_trf()']} self.cfg=dotdict(cfg) self.args=args def run(self): print('args='+str(self.args)) print('cfg='+str(self.cfg)) #try: # self.points=np.array(self.cfg.points) #except AttributeError: # pass try: sequencer= self.cfg.pop('sequencer') except KeyError: print('no command sequence to execute') else: dryrun=self.args.dryrun for cmd in sequencer: print('>'*5+' '+cmd+' '+'<'*5) if not dryrun: eval('self.' + cmd) def test_coord_trf(self): n = 3.; per = 1.; t = np.arange(n) p=((2.3,2.31,4.12,24.6),(6.2,2.74,32.1,3.28)) #(y, bias, ampl, phi) self.param=param=np.ndarray((len(p),5)) z=4.5 # fix z position for i in range(2): (y, bias, ampl, phi) =p[i] x= ampl * np.cos(2 * np.pi * (per / n * t + phi / 360.)) + bias print('yMeas_%d='%i+str(y)+' xMeas_%d='%i+str(x)) #param[i]=(z_i, y_i, x_i, r_i,phi_i) param[i][0] =z param[i][1] =y param[i][2:]=HelicalScan.meas_rot_ctr(x) #(bias,ampl,phase) pass print param #self.fwd_transform(param[0][1],0.,param[0][2],param[0][1]) # y_0 ,120deg ,x_0 ,z_0) self.fwd_transform(param[0][1],2*np.pi/3.,param[0][2],param[0][0]) #self.fwd_transform(param[1][1],0.,param[1][2],param[1][3]) def fwd_transform(self,y,w,cx,cz): #cx,cy: coarse stage #TODO: NOT WORKING AT ALL NOW... param=self.param # param[i]=(z_i, y_i, x_i, r_i,phi_i) p=np.ndarray((param.shape[0], 3)) for i in range(2): #p[i][0]=param[i][2]+param[i][3]*np.cos(param[i][4]+w) # x= x_i+r_i*cos(phi_i*w)+cx p[i][0]=cx+param[i][3]*np.cos(param[i][4]+w) # x= x_i+r_i*cos(phi_i*w)+cx p[i][1]=param[i][1] # y= y_i #p[i][2]=param[i][2]+param[i][3]*np.sin(param[i][4]+w) # z= z_i+r_i*sin(phi_i*w) p[i][2] =cz + param[i][3] * np.sin(param[i][4] + w) # z= z_i+r_i*sin(phi_i*w) print p v=p[1]-p[0] v=v/np.sqrt(v.dot(v)) # v/|v| v=v*(y-param[0][1])/(param[1][1]-param[0][1]) # v(y)=v*(v-y_0)/(y_1-y_0) v=p[0]+v #v=v/abs(v) print v # # # #x,y,z #returns y,w,dx,dz pass def inv_transform(y,phi,dx=0,dz=0): #dx,dy: deviation from cristal center line #ps= #x,y,z #returns y,phi,cx,cz pass @staticmethod def meas_rot_ctr(y,per=1): # find the amplitude bias and phase of an equidistant sampled sinus # it needs at least 3 measurements e.g. at 0,120 240 deg or 0 90 180 270 deg # per is the number of persiods, default is 1 period =360 deg n=len(y) f = np.fft.fft(y) idx=int(per) bias=np.absolute(f[0]/n) phase=np.angle(f[idx]) ampl=np.absolute(f[idx])*2/n return (bias,ampl,phase) @staticmethod def test_find_rot_ctr(n=3.,per=1.,bias=4.1,ampl=2.4,phi=37): # find the rotation center, amplitude out of n (niminum 3) measurements # n number of equidistant measurements # per number of periods (full rotation of all measurements nut be a interger value for precise measurements) # phi phase # bias bias value # ampl amplitude t = np.arange(n) y=ampl*np.cos(2*np.pi*(per/n*t+phi/360.))+bias plt.figure(1) plt.subplot(311) plt.plot(t,y,'b.-') plt.subplot(312) f = np.fft.fft(y) plt.step(t, f.real,'b.-', t, f.imag,'r.-', where='mid') (bias,ampl,phase)=HelicalScan.meas_rot_ctr(y, per) print('bias: '+str(bias)) print('amplitude: '+str(ampl)) print('phase: '+str(phase*360./2/np.pi)) plt.subplot(313) t2 = np.linspace(0,2*np.pi,64) y2=ampl*np.cos(t2+phase)+bias plt.plot(t2,y2,'g-') plt.stem(t/n*2*np.pi*per,y,'b-') plt.show() pass if __name__=='__main__': from optparse import OptionParser, IndentedHelpFormatter class MyFormatter(IndentedHelpFormatter): 'helper class for formating the OptionParser' def __init__(self): IndentedHelpFormatter.__init__(self) def format_epilog(self, epilog): if epilog: return epilog else: return "" def parse_args(): 'main command line interpreter function' #usage: gpasciiCommunicator.py --host=PPMACZT84 myPowerBRICK.cfg (h, t)=os.path.split(sys.argv[0]);cmd='\n '+(t if len(h)>3 else sys.argv[0])+' ' exampleCmd=('-n', '-v15' ) epilog=__doc__+''' Examples:'''+''.join(map(lambda s:cmd+s, exampleCmd))+'\n ' fmt=MyFormatter() parser=OptionParser(epilog=epilog, formatter=fmt) parser.add_option('-v', '--verbose', type="int", dest='verbose', help='verbosity bits (see below)', default=0) parser.add_option('-n', '--dryrun', action='store_true', help='dryrun to stdout') parser.add_option('--xy', action='store_true', help='sort x,y instead y,x') parser.add_option('--cfg', help='config file containing json configuration structure') (args, other)=parser.parse_args() args.other=other sp=HelicalScan(args) sp.run() #------------------ Main Code ---------------------------------- #ssh_test() ret=parse_args() exit(ret)