From 0fe59e9ad69e5840e3a16822bf37728bee6d44e8 Mon Sep 17 00:00:00 2001 From: Thierry Zamofing Date: Wed, 11 Dec 2024 15:26:01 +0100 Subject: [PATCH] try to cleanup stuff(3) --- ModuleFixTarget.py | 51 +++++++++++---------- Readme.md | 111 +++++++++++++++++++++++++++++++++++++++++++++ pyqtUsrObj.py | 72 ++++++++++++++++++++--------- swissmx.py | 19 ++++---- 4 files changed, 195 insertions(+), 58 deletions(-) diff --git a/ModuleFixTarget.py b/ModuleFixTarget.py index 18999b0..3e706f5 100644 --- a/ModuleFixTarget.py +++ b/ModuleFixTarget.py @@ -16,7 +16,7 @@ This contains a Widget to handle FixTargetFrames and fiducials, calculate final import logging _log=logging.getLogger(__name__) -import json, base64, yaml +import os, json, base64, yaml import numpy as np import pyqtUsrObj as UsrGO import pyqtgraph as pg @@ -47,6 +47,22 @@ class MyJsonEncoder(json.JSONEncoder): return repr(obj) return json.JSONEncoder.default(self, obj) + def iterencode(self, o, _one_shot=False): + list_lvl = 0 + l=super().iterencode(o, _one_shot=_one_shot) + #l=tuple(l);print(''.join(l)) # helpful to debug + for s in l: + if s.startswith('['): + list_lvl += 1 + if list_lvl > 0: + s = s[0]+s[1:].replace('\n', '').strip() + s = s.replace('\n', '').rstrip() + #self.item_separator): + #self.key_separator + if s.endswith(']'): + list_lvl -= 1 + yield s + def MyJsonDecoder(dct): if isinstance(dct, dict): if '__class__' in dct: @@ -173,28 +189,10 @@ class WndFixTarget(QWidget): #bm_pos_eu=self._goBeamMarker._pos_eu #bm_size_eu=self._goBeamMarker._size_eu try: - #parse the parameters: 'key:value [,key:value]' - # as key value separator : and = are allowed - #examples: - #Fiducial: - # no param - #FixTarget(12.5x12.5), FixTarget(23.0x23.0), FixTarget(test): - # code_gen:2 - # code_gen=3 | tmove=10 | twait=30 - #Grid(): - # size:(30, 20) - # cnt:(30, 22) - # fiducialSize:0.1 - #SwissMX(): - # ofs:(.2, .2) - # width:10 - # fidScl:.02 - # fiducial:((.1, .1), (.1, 2.7), (10.3, .1), (10.3, 2.7)) - #SwissFEL(): - # ofs:(.2, .2) - # width:10 - # fidScl:.02 - # fiducial:((.1, .1), (.1, 2.2), (10.3, .1), (10.3, 2.2)) + #parse the parameters: as yaml string. + # allows : without space, allows () as [] + # no {} to define a dictionary + # e.g. 'a:ggf,b:5,c:[5,6.1],d(8,9,3)' param=param.replace(':', ': ') # allow gen:4 without space param=yaml.safe_load(param) # "ofs":[10, 5],"width":200,"fidScl":0.5,"fiducial":[[18,7],[25,16],[70, 20]] @@ -376,7 +374,10 @@ class WndFixTarget(QWidget): #df = pd.DataFrame(data) #df.to_csv(filename, float_format="%.6f") #import numpy as np - ext=filename.rsplit('.',1)[1].lower() + base,ext=os.path.splitext(filename) + if not ext.lower(): + ext='json' + filename=base+'.'+ext try: wnd=app._mainWnd except AttributeError: @@ -386,7 +387,7 @@ class WndFixTarget(QWidget): grp=wnd._goTracked data=grp.childItems() - if ext=='json': + if ext.lower()=='json': with open(filename, 'w') as f: json.dump(data, f,cls=MyJsonEncoder, indent=2)#separators=(',', ':') else: diff --git a/Readme.md b/Readme.md index 3fde5b0..aa1d428 100644 --- a/Readme.md +++ b/Readme.md @@ -102,3 +102,114 @@ python swissmx.py --sim 0xc0 ``` Document to start SwissMX in cristallina environment (maintained by John): https://docs.google.com/document/d/1yEmV_DbRBKQKVCoovjXriNgSjNEBaz50WA0l3yA5jtg/edit#heading=h.z9io692b8tow + +## code generation parameters +``` +copied from: PBSwissMX/python/shapepath.py: ShapePath.setup_motion + + 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' + + (m)= mandatory + (o)= optional + common kwargs: + scale : (o) scaling velocity (default=1. value=0 would stop at each point + cnt : (o) move path multiple times (default=1) + dwell : (o) dwell time at end (default=100ms) + + mode:0 unused + mode:1 pvt motion point list + common kwargs plus: + points : (m) point list + trf : (o) transformation that will be done on 'points', mot_pts=trf*points + mode:2 unused + mode:3 pvt motion point list using inverse fft velocity + common kwargs plus: + points : (m) point list + trf : (o) transformation that will be done on 'points', mot_pts=trf*points + numPad : (o) number of padding points to reduce aliasing (default=16) + mode:4 pvt motion short code using grid parameters + common kwargs plus: + trf : (o) transformation that will be done on 'grid points' + grid: (m) grid parameters: {orig:(0,0),pitch(10,10),cnt:(10,10),mode:0} + mode:5 pvt motion 'stop and go' short code using grid parameters. + Instead of continous motion it moves and waits as given in the parameters + common kwargs plus: + trf : (o) transformation that will be done on 'grid points' + grid: (m) grid parameters: {orig:(0,0),pitch(10,10),cnt:(10,10),mode:0} + tmove: (m) time to move in ms (move start on FEL-trigger + twait: (m) time to wait in ms + (tmove+twait will be rounded to a multiple of fel_per) + mode:6 pvt motion 'hit and return using grid parameters. continous motion on 2n ells to pump then same 2n wells to probe, then go 2 rows down + common kwargs plus: + trf : (o) transformation that will be done on 'grid points' + grid : (m) grid parameters: {orig:(0,0),pitch(10,10),cnt:(10,10),mode:0} + ssz : (m) section size (in wells) + smv : (o) time(in num of shots) to move to next section (horiz/vert) + default is (ssz[0]-1,ssz[1]) + sdelay: (o) shots count of delay. Default is ssz[0]*ssz[1] + +Examples: + mode:1 + mode:3 + mode:4 + mode:5,tmove:20,twait:30 + mode:6,ssz:(4,3) +``` +## graphical object parameters +``` + FixTarget: + 90*40+480*2=4560 + 60*30+360*2=2520 + 2520-240=2280 + 4560-240=4320 + size in mm, dscr.size in user units (um) + "size:(6,3.5), + dscr: { + size:(4560,2520), + fiducial:{type:0,pos:((240,240),(4320,240),(240,2280),(4320,2280))}, + grid:{pos:(480,360),pitch:(90,60),count:(40,30)} + }" + + grid: + size, fiducialSize in mm: + (60-1)*.120 -> 7.08mm + (45-1)*.120 -> 5.28mm + fiducialSize -> 0.1mm + "size:(7.08,5.28),cnt:(60,45),fiducialSize:.01" + + SwissMX(): + "ofs:[.2,.2],width:10,fidScl:.02,fiducial:[[.1,.1],[.1,2.7],[10.3,.1],[10.3, 2.7]]" + SwissFEL(): + "ofs:[.2,.2],width:10,fidScl:.02,fiducial:[[.1,.1],[.1,2.2],[10.3,.1],[10.3,2.2]]" +``` + +## fully parameter examples: +``` + "mode:4,size:(7.08,5.28), cnt:(60,45), fiducialSize:.01" -> add a grid + + "mode:6,ssz:(4,3), size:(7.08,5.28), cnt:(60,45), fiducialSize:.01" -> add a grid + "mode:6,ssz:(4,3), size:(3,1.5), cnt:(30,15), fiducialSize:.01" -> add a grid + + "mode:6,ssz:(6,8), + dscr: { + size:(4560,2520), + fiducial:{type:0,pos:((240,240),(4320,240),(240,2280),(4320,2280))}, + grid:{pos:(480,360),pitch:(90,60),count:(40,30)} + }" -> add a FixTarget + + +``` + + +## testing hit and return: +``` +-> add a FixTarget + "mode:6,ssz:(6,8), + dscr: { + size:(4560,2520), + fiducial:{type:0,pos:((240,240),(4320,240),(240,2280),(4320,2280))}, + grid:{pos:(480,360),pitch:(90,60),count:(40,30)} + }" -> add a FixTarget +``` diff --git a/pyqtUsrObj.py b/pyqtUsrObj.py index 0b53001..d99afd4 100644 --- a/pyqtUsrObj.py +++ b/pyqtUsrObj.py @@ -251,25 +251,55 @@ class Grid(UsrROI): def get_scan_param(self): 'returns scan parameters for scanning with deltatau. the format is as used for shapepath' - scan=1 # snake motion Y fast, X slow (default) - cnt=np.array(self._cnt,np.int32) - sz=np.array(self.size()) - pitch=sz/cnt - xx, yy=np.meshgrid(range(cnt[0]), range(cnt[1])) - - - if scan==0: # snake motion X fast, Y slow - for i in range(1,cnt[1],2): - xx[i]=xx[i][::-1] - else: # scan==1 # snake motion Y fast, X slow (default) - xx=xx.T - yy=yy.T - for i in range(1, cnt[0], 2): - yy[i]=yy[i][::-1] - pts=np.array([xx.reshape(-1), yy.reshape(-1)], dtype=np.float64).transpose()*pitch - param={'points':pts} + cnt=np.array(self._cnt, np.int32) + grid={'pos':tuple(self.pos()), 'pitch':tuple(np.array(self.size())/cnt), 'count':self._cnt} + use_trf=self._param.get('use_trf', True) # do not use coordinate transformation + mode=self._param.get('mode',1) + num_pts=np.array(self._cnt, np.int32).prod() + param={'num_pts':num_pts} param.update(self._param) - assert(param.get('code_gen',0)==0) # this provides fully x,y motor coordinates + + # TODO: simplify !!! + t=self.transform() #obj_info(t) + p=np.array(self.pos()) + s=1#self.size()/self._dscr['size'] + trf=np.array(((t.m11(),t.m12()),(t.m21(),t.m22()),(0,0))) + trf[2,:]=p # shift origin + trf[:2,:]=(trf[:2,:].T*s).T # same as np.asmatrix(np.diag(s))*trf[:2,:], trf[:2,:]*=s not working, scale before rot / shear + + pos=np.array((0,0)) #np.array(grid['pos']) # in um + pitch=np.array(grid['pitch']) # in um + trf2=np.asmatrix(np.identity(3)) + trf2[:, :2]=trf + trf2*=np.asmatrix(((1000, 0, 0), (0, 1000, 0), (0, 0, 1))) + trf3=np.asmatrix(((pitch[0], 0, 0), (0, pitch[1], 0), (pos[0], pos[1], 1))) + trf=(trf3*trf2)[:, :2] + + if mode in (1,3): # needs all points, not grid + scan=1 # snake motion Y fast, X slow (default) + xx, yy=np.meshgrid(range(cnt[0]), range(cnt[1])) + if scan==0: # snake motion X fast, Y slow + for i in range(1,cnt[1],2): + xx[i]=xx[i][::-1] + else: # scan==1 # snake motion Y fast, X slow (default) + xx=xx.T + yy=yy.T + for i in range(1, cnt[0], 2): + yy[i]=yy[i][::-1] + + pts=np.array([xx.reshape(-1), yy.reshape(-1)], dtype=np.float64).transpose() #*pitch + + if not use_trf: + pts=(np.hstack((pts, np.ones((pts.shape[0], 1))))*trf).A + param['trf']=trf + param['points']=pts + else: + if use_trf: + param.update({'grid':grid, 'trf':trf}) + else: + g=grid.copy() #has not be tested ! + g['pos']=tuple((np.array((0,0,1))*trf).A.reshape(-1).tolist()) + param.update({'grid':p, 'trf':trf}) return param @@ -371,11 +401,9 @@ class Path(UsrROI): s=self.size()/self.szOrig trf=np.array(((t.m11(),t.m12()),(t.m21(),t.m22()),(0,0))) trf[2,:]=p # shift origin - #trf[:2, 0]*=s[0];trf[:2, 1]*=s[1] #scaling (before rotation shear) - trf[:2,:]=(trf[:2,:].T*s).T # same as np.asmatrix(np.diag(s))*trf[:2,:], trf[:2,:]*=s not working, scale before rot / shear - + trf[:2,:]=(trf[:2,:].T*s).T # trf*'gridpos in um' -> motor pos in mm - param={'points':self._path,'trf':trf} + param={'num_pts':len(self._path),'trf':trf,'points':self._path} param.update(self._param) return param diff --git a/swissmx.py b/swissmx.py index e36baa4..370d362 100755 --- a/swissmx.py +++ b/swissmx.py @@ -1527,7 +1527,7 @@ class WndSwissMx(QMainWindow, Ui_MainWindow): if type(go)==UsrGO.Fiducial: continue t=type(go) - if t not in(UsrGO.FixTargetFrame,UsrGO.Path): + if t not in(UsrGO.FixTargetFrame,UsrGO.Path,UsrGO.Grid): _log.warning(f'{t} not supported for FixTargetFrame ->skipped:{go}') continue try: @@ -2025,7 +2025,7 @@ object settings: ofs:[.2,.2] width:10 fidScl:.02 - fiducial:[[.1,.1],[[.1,2.2],[10.3,.1],[10.3,2.2]] + fiducial:[[.1,.1],[.1,2.2],[10.3,.1],[10.3,2.2]] ''') mft._btnAdd.clicked.connect(self.module_fix_target_add_obj) @@ -2146,19 +2146,16 @@ object settings: go=UsrGO.Fiducial((fx-l/2,fy-l/2), (l, l),bz) go.sigRegionChangeFinished.connect(self.cb_fiducial_update_z) elif idx==1: - v=geo.pos2pix((12.5, 0)) - l=np.linalg.norm(v) - l=12.5 - #ctr=bm_pos+bm_sz/2 + #v=geo.pos2pix((12.5, 0));l=np.linalg.norm(v);l=12.5 + sz=param.pop('size',(12.5, 12.5)) go=UsrGO.FixTargetFrame((fx-l/2,fy-l/2), (l, l), tpl='12.5x12.5',**param) elif idx==2: - v=geo.pos2pix((23, 0)) - l=np.linalg.norm(v) - l=23 + #v=geo.pos2pix((23, 0));l=np.linalg.norm(v)#l=23 + sz=param.pop('size',(23, 23)) go=UsrGO.FixTargetFrame((fx-l/2,fy-l/2), (l, l), tpl='23.0x23.0',**param) elif idx==3: - w,h=(.120*12, .120*8) - go=UsrGO.FixTargetFrame((fx-w/2,fy-h/2), (w, h), tpl='test',**param) + sz=param.pop('size',(.120*12, .120*8)) + go=UsrGO.FixTargetFrame((fx-sz[0]/2,fy-sz[1]/2), sz, tpl='test',**param) elif idx==4: w,h=size=param.pop('size',(30, 20)) cnt=param.pop('cnt',(30, 22))