Coverage for /Users/Newville/Codes/xraylarch/larch/io/mda.py: 5%
1184 statements
« prev ^ index » next coverage.py v7.3.2, created at 2023-11-09 10:08 -0600
« prev ^ index » next coverage.py v7.3.2, created at 2023-11-09 10:08 -0600
1#!/usr/bin/env python
2# adopted with few changes from Tim Mooney's mda.py
4# version 2.1 Tim Mooney 2/15/2012
5# merge of mda.py and mda_f.py
6# - supports reading, writing, and arithmetic operations for
7# up to 4-dimensional MDA files.
9__version__ = '2.1'
10import sys
11import os
12import string
13import numpy
14from xdrlib import Packer, Unpacker
16import copy
17from larch import Group
20################################################################################
21# classes
22# scanDim holds all of the data associated with a single execution of a single sscan record.
23class scanDim:
24 def __init__(self):
25 self.rank = 0 # [1..n] 1 means this is the "innermost" or only scan dimension
26 self.dim = 0 # dimensionality of data (numerically same as rank)
27 self.npts = 0 # number of data points planned
28 self.curr_pt = 0 # number of data points actually acquired
29 self.plower_scans = 0 # file offsets of next lower rank scans
30 self.name = "" # name of sscan record that acquired the data
31 self.time = "" # time at which scan (dimension) started
32 self.np = 0 # number of positioners
33 self.p = [] # list of scanPositioner instances
34 self.nd = 0 # number of detectors
35 self.d = [] # list of scanDetector instances
36 self.nt = 0 # number of detector triggers
37 self.t = [] # list of scanTrigger instances
39 def __str__(self):
40 if self.name != '':
41 s = "%dD data from \"%s\" acquired on %s:\n%d/%d pts; %d positioners, %d detectors" % (
42 self.dim, self.name, self.time, self.curr_pt, self.npts, self.np, self.nd)
43 else:
44 s = "%dD data (not read in)" % (self.dim)
46 return s
48# scanPositioner holds all the information associated with a single positioner, and
49# all the data written and acquired by that positioner during an entire (possibly
50# multidimensional) scan.
51class scanPositioner:
52 def __init__(self):
53 self.number = 0 # positioner number in sscan record
54 self.fieldName = "" # name of sscanRecord PV
55 self.name = "" # name of EPICS PV this positioner wrote to
56 self.desc = "" # description of 'name' PV
57 self.step_mode = "" # 'LINEAR', 'TABLE', or 'FLY'
58 self.unit = "" # units of 'name' PV
59 self.readback_name = "" # name of EPICS PV this positioner read from, if any
60 self.readback_desc = "" # description of 'readback_name' PV
61 self.readback_unit = "" # units of 'readback_name' PV
62 self.data = [] # list of values written to 'name' PV. If rank==2, lists of lists, etc.
64 def __str__(self):
65 data = self.data
67 n = data.ndim
68 if n==1:
69 dimString = '(' + str(data.shape[0]) + ')'
70 else:
71 dimString = str(data.shape)
73 s = "positioner <scanRecord>.%s\nPV name '%s'\nPV desc. '%s'\nPV units '%s'\nstep mode: %s\nRB name '%s'\nRB desc. '%s'\nRB units '%s'\ndata: %dD array %s\n" % (self.fieldName,
74 self.name, self.desc, self.unit, self.step_mode, self.name, self.desc, self.unit, n, dimString)
75 return s
77# scanDetector holds all the information associated with a single detector, and
78# all the data acquired by that detector during an entire (possibly multidimensional) scan.
79class scanDetector:
80 def __init__(self):
81 self.number = 0 # detector number in sscan record
82 self.fieldName = "" # name of sscanRecord PV
83 self.name = "" # name of EPICS PV this detector read from
84 self.desc = "" # description of 'name' PV
85 self.unit = "" # units of 'name' PV
86 self.data = [] # list of values read from 'name' PV. If rank==2, lists of lists, etc.
88 def __str__(self):
89 data = self.data
90 n = data.ndim
91 if n==1:
92 dimString = '(' + str(data.shape[0]) + ')'
93 else:
94 dimString = str(data.shape)
95 s = "detector <scanRecord>.%s\nPV name '%s'\nPV desc. '%s'\nPV units '%s'\ndata: %dD array %s\n" % (self.fieldName,
96 self.name, self.desc, self.unit, n, dimString)
97 return s
99# scanTrigger holds all the information associated with a single detector trigger.
100class scanTrigger:
101 def __init__(self):
102 self.number = 0 # detector-trigger number in sscan record
103 self.name = "" # name of sscanRecord PV
104 self.command = 0.0 # value written to 'name' PV
106 def __str__(self):
107 s = "trigger %d (%s), command=%f\n" % (self.number,
108 self.name, self.command)
109 return s
111# scanBuf is a private data structure used to assemble data that will be written to an MDA file.
112class scanBuf:
113 def __init__(self):
114 self.npts = 0
115 self.offset = 0
116 self.bufLen = 0
117 self.preamble = None
118 self.pLowerScans = []
119 self.pLowerScansBuf = ""
120 self.postamble = None
121 self.data = None
122 self.inner = [] # inner scans, if any
124# mdaBuf is a private data structure used to assemble data that will be written to an MDA file.
125class mdaBuf:
126 def __init__(self):
127 self.header = None
128 self.pExtra = None # file offset to extraPV section
129 self.scan = None
130 self.extraPV = None # extraPV section
132################################################################################
133# read MDA file
135# Given a detector number, return the name of the associated sscanRecord PV, 'D01'-'D99'.
136# (Currently, only 70 detectors are ever used.)
137def detName(i):
138 if i < 100:
139 return "D%02d"%(i+1)
140 else:
141 return "?"
143# Given a detector number, return the name of the associated sscanRecord PV, for the
144# old sscanRecord, which had only 15 detectors 'D1'-'DF'.
145def oldDetName(i):
146 if i < 15:
147 return string.upper("D%s"%(hex(i+1)[2]))
148 elif i < 85:
149 return "D%02d"%(i-14)
150 else:
151 return "?"
153# Given a positioner number, , return the name of the associated sscanRecord PV, "P1'-'P4'
154def posName(i):
155 if i < 4:
156 return "P%d" % (i+1)
157 else:
158 return "?"
160def verboseData(data, out=sys.stdout):
161 if ((len(data)>0) and (type(data[0]) == type([]))):
162 for i in len(data):
163 verboseData(data[i], out)
164 else:
165 out.write("[")
166 for datum in data:
167 if (type(datum) == type(0)):
168 out.write(" %d" % datum)
169 else:
170 out.write(" %.5f" % datum)
171 out.write(" ]\n")
173def readScan(scanFile, verbose=0, out=sys.stdout, unpacker=None):
174 """usage: (scan,num) = readScan(scanFile, verbose=0, out=sys.stdout)"""
176 scan = scanDim() # data structure to hold scan info and data
177 buf = scanFile.read(10000) # enough to read scan header
178 if unpacker == None:
179 u = Unpacker(buf)
180 else:
181 u = unpacker
182 u.reset(buf)
184 scan.rank = u.unpack_int()
185 if (scan.rank > 20) or (scan.rank < 0):
186 print("* * * readScan('%s'): rank > 20. probably a corrupt file" % scanFile.name)
187 return None
189 scan.npts = u.unpack_int()
190 scan.curr_pt = u.unpack_int()
191 if verbose:
192 print("scan.rank = ", scan.rank)
193 print("scan.npts = ", scan.npts)
194 print("scan.curr_pt = ", scan.curr_pt)
196 if (scan.rank > 1):
197 # if curr_pt < npts, plower_scans will have garbage for pointers to
198 # scans that were planned for but not written
199 scan.plower_scans = u.unpack_farray(scan.npts, u.unpack_int)
200 if verbose:
201 print("scan.plower_scans = ", scan.plower_scans)
202 namelength = u.unpack_int()
203 scan.name = u.unpack_string()
204 if verbose:
205 print("scan.name = ", scan.name)
206 timelength = u.unpack_int()
207 scan.time = u.unpack_string()
208 if verbose:
209 print("scan.time = ", scan.time)
210 scan.np = u.unpack_int()
211 if verbose:
212 print("scan.np = ", scan.np)
213 scan.nd = u.unpack_int()
214 if verbose:
215 print("scan.nd = ", scan.nd)
216 scan.nt = u.unpack_int()
217 if verbose:
218 print("scan.nt = ", scan.nt)
219 for j in range(scan.np):
220 scan.p.append(scanPositioner())
221 scan.p[j].number = u.unpack_int()
222 scan.p[j].fieldName = posName(scan.p[j].number)
223 if verbose:
224 print("positioner ", j)
225 length = u.unpack_int() # length of name string
226 if length:
227 scan.p[j].name = u.unpack_string()
228 if verbose:
229 print("scan.p[%d].name = %s" % (j, scan.p[j].name))
230 length = u.unpack_int() # length of desc string
231 if length:
232 scan.p[j].desc = u.unpack_string()
233 if verbose:
234 print("scan.p[%d].desc = %s" % (j, scan.p[j].desc))
235 length = u.unpack_int() # length of step_mode string
236 if length:
237 scan.p[j].step_mode = u.unpack_string()
238 if verbose:
239 print("scan.p[%d].step_mode = %s" % (j, scan.p[j].step_mode))
240 length = u.unpack_int() # length of unit string
241 if length:
242 scan.p[j].unit = u.unpack_string()
243 if verbose:
244 print("scan.p[%d].unit = %s" % (j, scan.p[j].unit))
245 length = u.unpack_int() # length of readback_name string
246 if length:
247 scan.p[j].readback_name = u.unpack_string()
248 if verbose:
249 print("scan.p[%d].readback_name = %s" % (j, scan.p[j].readback_name))
250 length = u.unpack_int() # length of readback_desc string
251 if length:
252 scan.p[j].readback_desc = u.unpack_string()
253 if verbose:
254 print("scan.p[%d].readback_desc = %s" % (j, scan.p[j].readback_desc))
255 length = u.unpack_int() # length of readback_unit string
256 if length:
257 scan.p[j].readback_unit = u.unpack_string()
258 if verbose:
259 print("scan.p[%d].readback_unit = %s" % (j, scan.p[j].readback_unit))
261 file_loc_det = scanFile.tell() - (len(buf) - u.get_position())
263 for j in range(scan.nd):
264 scan.d.append(scanDetector())
265 scan.d[j].number = u.unpack_int()
266 scan.d[j].fieldName = detName(scan.d[j].number)
267 if verbose:
268 print("detector ", j)
269 length = u.unpack_int() # length of name string
270 if length:
271 scan.d[j].name = u.unpack_string()
272 if verbose:
273 print("scan.d[%d].name = %s" % (j, scan.d[j].name))
274 length = u.unpack_int() # length of desc string
275 if length:
276 scan.d[j].desc = u.unpack_string()
277 if verbose:
278 print("scan.d[%d].desc = %s" % (j, scan.d[j].desc))
279 length = u.unpack_int() # length of unit string
280 if length:
281 scan.d[j].unit = u.unpack_string()
282 if verbose:
283 print("scan.d[%d].unit = %s" % (j, scan.d[j].unit))
285 for j in range(scan.nt):
286 scan.t.append(scanTrigger())
287 scan.t[j].number = u.unpack_int()
288 if verbose:
289 print("trigger ", j)
290 length = u.unpack_int() # length of name string
291 if length:
292 scan.t[j].name = u.unpack_string()
293 if verbose:
294 print("scan.t[%d].name = %s" % (j, scan.t[j].name))
295 scan.t[j].command = u.unpack_float()
296 if verbose:
297 print("scan.t[%d].command = %s" % (j, scan.t[j].command))
299 ### read data
300 # positioners
301 file_loc_data = scanFile.tell() - (len(buf) - u.get_position())
302 scanFile.seek(file_loc_data)
303 buf = scanFile.read(scan.npts * (scan.np * 8 + scan.nd *4))
304 u.reset(buf)
306 data = u.unpack_farray(scan.npts*scan.np, u.unpack_double)
307 start = 0
308 end = scan.npts
309 for j in range(scan.np):
310 start = j*scan.npts
311 scan.p[j].data = data[start:end]
312 start = end
313 end += scan.npts
315 # detectors
316 data = u.unpack_farray(scan.npts*scan.nd, u.unpack_float)
317 start = 0
318 end = scan.npts
319 for j in range(scan.nd):
320 scan.d[j].data = data[start:end]
321 start = end
322 end += scan.npts
324 return (scan, (file_loc_data-file_loc_det))
326useDetToDatOffset = 1
327def readScanQuick(scanFile, unpacker=None, detToDat_offset=None):
328 """usage: readScanQuick(scanFile, unpacker=None)"""
330 scan = scanDim() # data structure to hold scan info and data
331 buf = scanFile.read(10000) # enough to read scan header
332 if unpacker is None:
333 u = Unpacker(buf)
334 else:
335 u = unpacker
336 u.reset(buf)
338 scan.rank = u.unpack_int()
339 if (scan.rank > 20) or (scan.rank < 0):
340 print("* * * readScanQuick('%s'): rank > 20. probably a corrupt file" % scanFile.name)
341 return None
343 scan.npts = u.unpack_int()
344 scan.curr_pt = u.unpack_int()
346 if (scan.rank > 1):
347 scan.plower_scans = u.unpack_farray(scan.npts, u.unpack_int)
349 namelength = u.unpack_int()
350 scan.name = u.unpack_string()
351 timelength = u.unpack_int()
352 scan.time = u.unpack_string()
354 scan.np = u.unpack_int()
355 scan.nd = u.unpack_int()
356 scan.nt = u.unpack_int()
358 for j in range(scan.np):
359 scan.p.append(scanPositioner())
360 scan.p[j].number = u.unpack_int()
361 n = u.unpack_int() # length of name string
362 if n: u.set_position(u.get_position()+4+(n+3)//4*4)
363 n = u.unpack_int() # length of desc string
364 if n: u.set_position(u.get_position()+4+(n+3)//4*4)
365 n = u.unpack_int() # length of step_mode string
366 if n: u.set_position(u.get_position()+4+(n+3)//4*4)
367 n = u.unpack_int() # length of unit string
368 if n: u.set_position(u.get_position()+4+(n+3)//4*4)
369 n = u.unpack_int() # length of readback_name string
370 if n: u.set_position(u.get_position()+4+(n+3)//4*4)
371 n = u.unpack_int() # length of readback_desc string
372 if n: u.set_position(u.get_position()+4+(n+3)//4*4)
373 n = u.unpack_int() # length of readback_unit string
374 if n: u.set_position(u.get_position()+4+(n+3)//4*4)
376 file_loc_det = scanFile.tell() - (len(buf) - u.get_position())
378 if (detToDat_offset == None) or (not useDetToDatOffset):
379 for j in range(scan.nd):
380 scan.d.append(scanDetector())
381 scan.d[j].number = u.unpack_int()
382 scan.d[j].fieldName = detName(scan.d[j].number)
383 n = u.unpack_int() # length of name string
384 if n: u.set_position(u.get_position()+4+(n+3)//4*4)
385 n = u.unpack_int() # length of desc string
386 if n: u.set_position(u.get_position()+4+(n+3)//4*4)
387 n = u.unpack_int() # length of unit string
388 if n: u.set_position(u.get_position()+4+(n+3)//4*4)
390 for j in range(scan.nt):
391 scan.t.append(scanTrigger())
392 scan.t[j].number = u.unpack_int()
393 n = u.unpack_int() # length of name string
394 if n: u.set_position(u.get_position()+4+(n+3)//4*4)
395 scan.t[j].command = u.unpack_float()
397 ### read data
398 # positioners
400 file_loc = scanFile.tell() - (len(buf) - u.get_position())
401 diff = file_loc - (file_loc_det + detToDat_offset)
402 if diff != 0:
403 print("oldSeek, newSeek, o-n=", file_loc, file_loc_det + detToDat_offset, diff)
404 scanFile.seek(file_loc)
405 else:
406 for j in range(scan.nd):
407 scan.d.append(scanDetector())
408 scanFile.seek(file_loc_det + detToDat_offset)
410 buf = scanFile.read(scan.npts * (scan.np * 8 + scan.nd *4))
411 u.reset(buf)
413 data = u.unpack_farray(scan.npts*scan.np, u.unpack_double)
414 for j in range(scan.np):
415 start = j*scan.npts
416 scan.p[j].data = data[j*scan.npts : (j+1)*scan.npts]
418 # detectors
419 data = u.unpack_farray(scan.npts*scan.nd, u.unpack_float)
420 for j in range(scan.nd):
421 scan.d[j].data = data[j*scan.npts : (j+1)*scan.npts]
423 return scan
425EPICS_types_dict = {
4260: "DBR_STRING",
4271: "DBR_SHORT",
4282: "DBR_FLOAT",
4293: "DBR_ENUM",
4304: "DBR_CHAR",
4315: "DBR_LONG",
4326: "DBR_DOUBLE",
4337: "DBR_STS_STRING",
4348: "DBR_STS_SHORT",
4359: "DBR_STS_FLOAT",
43610: "DBR_STS_ENUM",
43711: "DBR_STS_CHAR",
43812: "DBR_STS_LONG",
43913: "DBR_STS_DOUBLE",
44014: "DBR_TIME_STRING",
44115: "DBR_TIME_SHORT",
44216: "DBR_TIME_FLOAT",
44317: "DBR_TIME_ENUM",
44418: "DBR_TIME_CHAR",
44519: "DBR_TIME_LONG",
44620: "DBR_TIME_DOUBLE",
44721: "DBR_GR_STRING",
44822: "DBR_GR_SHORT",
44923: "DBR_GR_FLOAT",
45024: "DBR_GR_ENUM",
45125: "DBR_GR_CHAR",
45226: "DBR_GR_LONG",
45327: "DBR_GR_DOUBLE",
45428: "DBR_CTRL_STRING",
45529: "DBR_CTRL_SHORT",
45630: "DBR_CTRL_FLOAT",
45731: "DBR_CTRL_ENUM",
45832: "DBR_CTRL_CHAR",
45933: "DBR_CTRL_LONG",
46034: "DBR_CTRL_DOUBLE"
461}
463def EPICS_types(n):
464 if EPICS_types_dict.has_key(n):
465 return EPICS_types_dict[n]
466 else:
467 return ("Unexpected type %d" % n)
469def readMDA(fname=None, maxdim=4, verbose=0, showHelp=0, outFile=None,
470 useNumpy=None, readQuick=False):
471 """usage readMDA(fname=None, maxdim=4, verbose=0, showHelp=0,
472 outFile=None, readQuick=False)"""
473 dim = []
474 if fname is None:
475 print("No file specified, and no file dialog could be opened")
476 return None
477 if (not os.path.isfile(fname)):
478 if (not fname.endswith('.mda')):
479 fname = fname + '.mda'
480 if (not os.path.isfile(fname)):
481 print(fname, "not found")
482 return None
484 if (outFile == None):
485 out = sys.stdout
486 else:
487 out = open(outFile, 'w')
489 scanFile = open(fname, 'rb')
490 if verbose: out.write("verbose=%d output for MDA file '%s'\n" % (verbose, fname))
491 buf = scanFile.read(100) # to read header for scan of up to 5 dimensions
492 u = Unpacker(buf)
494 # read file header
495 version = u.unpack_float()
496 if verbose: out.write("MDA version = %.3f\n" % version)
497 if abs(version - 1.3) > .01:
498 print("I can't read MDA version %f. Is this really an MDA file?" % version)
499 return None
501 scan_number = u.unpack_int()
502 if verbose: out.write("scan_number = %d\n" % scan_number)
503 rank = u.unpack_int()
504 if verbose: out.write("rank = %d\n" % rank)
505 dimensions = u.unpack_farray(rank, u.unpack_int)
506 if verbose:
507 out.write("dimensions = ")
508 verboseData(dimensions, out)
509 isRegular = u.unpack_int()
510 if verbose: out.write("isRegular = %d\n" % isRegular)
511 pExtra = u.unpack_int()
512 if verbose: out.write("pExtra = %d (0x%x)\n" % (pExtra, pExtra))
513 pmain_scan = scanFile.tell() - (len(buf) - u.get_position())
515 # collect 1D data
516 scanFile.seek(pmain_scan)
517 (s,n) = readScan(scanFile, max(0,verbose-1), out, unpacker=u)
518 dim.append(s)
519 dim[0].dim = 1
521 for p in dim[0].p:
522 p.data = numpy.array(p.data)
523 for d in dim[0].d:
524 d.data = numpy.array(d.data)
526 if ((rank > 1) and (maxdim > 1)):
527 # collect 2D data
528 for i in range(dim[0].curr_pt):
529 scanFile.seek(dim[0].plower_scans[i])
530 if (i==0):
531 (s,detToDat) = readScan(scanFile, max(0,verbose-1), out, unpacker=u)
532 dim.append(s)
533 dim[1].dim = 2
534 # replace data arrays [1,2,3] with [[1,2,3]]
535 for j in range(dim[1].np):
536 dim[1].p[j].data = [dim[1].p[j].data]
537 for j in range(dim[1].nd):
538 dim[1].d[j].data = [dim[1].d[j].data]
539 else:
540 if readQuick:
541 s = readScanQuick(scanFile, unpacker=u, detToDat_offset=detToDat)
542 else:
543 (s,junk) = readScan(scanFile, max(0,verbose-1), out, unpacker=u)
544 # append data arrays
545 # [ [1,2,3], [2,3,4] ] -> [ [1,2,3], [2,3,4], [3,4,5] ]
546 numP = min(s.np, len(dim[1].p))
547 if (s.np > numP):
548 print("First scan had %d positioners; This one only has %d." % (s.np, numP))
549 for j in range(numP):
550 dim[1].p[j].data.append(s.p[j].data)
551 numD = min(s.nd, len(dim[1].d))
552 if (s.nd > numD):
553 print("First scan had %d detectors; This one only has %d." % (s.nd, numD))
554 for j in range(numD): dim[1].d[j].data.append(s.d[j].data)
556 for p in dim[1].p:
557 p.data = numpy.array(p.data)
558 for d in dim[1].d:
559 d.data = numpy.array(d.data)
561 if ((rank > 2) and (maxdim > 2)):
562 # collect 3D data
563 #print("dim[0].curr_pt=",dim[0].curr_pt)
564 for i in range(dim[0].curr_pt):
565 #print("i=%d of %d points" % (i, dim[0].curr_pt))
566 scanFile.seek(dim[0].plower_scans[i])
567 (s1,detToDat) = readScan(scanFile, max(0,verbose-1), out, unpacker=u)
568 #print("s1.curr_pt=", s1.curr_pt)
569 for j in range(s1.curr_pt):
570 #print("j=%d of %d points" % (j, s1.curr_pt))
571 scanFile.seek(s1.plower_scans[j])
572 if (j==0) or not readQuick:
573 (s, detToDat) = readScan(scanFile, max(0,verbose-1), out, unpacker=u)
574 else:
575 s = readScanQuick(scanFile, unpacker=u, detToDat_offset=detToDat)
576 if ((i == 0) and (j == 0)):
577 dim.append(s)
578 dim[2].dim = 3
579 # replace data arrays [1,2,3] with [[[1,2,3]]]
580 for k in range(dim[2].np):
581 dim[2].p[k].data = [[dim[2].p[k].data]]
582 for k in range(dim[2].nd):
583 dim[2].d[k].data = [[dim[2].d[k].data]]
584 else:
585 # append data arrays
586 numP = min(s.np, len(dim[2].p))
587 if (s.np > numP):
588 print("First scan had %d positioners; This one only has %d." % (s.np, numP))
589 for k in range(numP):
590 if j==0: dim[2].p[k].data.append([])
591 dim[2].p[k].data[i].append(s.p[k].data)
592 numD = min(s.nd, len(dim[2].d))
593 if (s.nd > numD):
594 print("First scan had %d detectors; This one only has %d." % (s.nd, numD))
595 for k in range(numD):
596 if j==0: dim[2].d[k].data.append([])
597 dim[2].d[k].data[i].append(s.d[k].data)
599 for p in dim[2].p:
600 p.data = numpy.array(p.data)
601 for d in dim[2].d:
602 d.data = numpy.array(d.data)
604 if ((rank > 3) and (maxdim > 3)):
605 # collect 4D data
606 for i in range(dim[0].curr_pt):
607 scanFile.seek(dim[0].plower_scans[i])
608 (s1, detToDat) = readScan(scanFile, max(0,verbose-1), out, unpacker=u)
609 for j in range(s1.curr_pt):
610 scanFile.seek(s1.plower_scans[j])
611 (s2, detToDat) = readScan(scanFile, max(0,verbose-1), out, unpacker=u)
612 for k in range(s2.curr_pt):
613 scanFile.seek(s2.plower_scans[k])
614 if (k==0) or not readQuick:
615 (s, detToDat) = readScan(scanFile, max(0,verbose-1), out, unpacker=u)
616 else:
617 s = readScanQuick(scanFile, unpacker=u, detToDat_offset=detToDat)
618 if ((i == 0) and (j == 0) and (k == 0)):
619 dim.append(s)
620 dim[3].dim = 4
621 for m in range(dim[3].np):
622 dim[3].p[m].data = [[[dim[3].p[m].data]]]
623 for m in range(dim[3].nd):
624 dim[3].d[m].data = [[[dim[3].d[m].data]]]
625 else:
626 # append data arrays
627 if j==0 and k==0:
628 for m in range(dim[3].np):
629 dim[3].p[m].data.append([[]])
630 dim[3].p[m].data[i][0].append(s.p[m].data)
631 for m in range(dim[3].nd):
632 dim[3].d[m].data.append([[]])
633 dim[3].d[m].data[i][0].append(s.d[m].data)
634 else:
635 for m in range(dim[3].np):
636 if k==0: dim[3].p[m].data[i].append([])
637 dim[3].p[m].data[i][j].append(s.p[m].data)
638 for m in range(dim[3].nd):
639 if k==0: dim[3].d[m].data[i].append([])
640 dim[3].d[m].data[i][j].append(s.d[m].data)
642 for p in dim[3].p:
643 p.data = numpy.array(p.data)
644 for d in dim[3].d:
645 d.data = numpy.array(d.data)
649 # Collect scan-environment variables into a dictionary
650 dict = {}
651 dict['sampleEntry'] = ("description", "unit string", "value", "EPICS_type", "count")
652 dict['filename'] = fname
653 dict['version'] = version
654 dict['scan_number'] = scan_number
655 dict['rank'] = rank
656 dict['dimensions'] = dimensions
657 acq_dimensions = []
658 for d in dim:
659 acq_dimensions.append(d.curr_pt)
660 dict['acquired_dimensions'] = acq_dimensions
661 dict['isRegular'] = isRegular
662 dict['ourKeys'] = ['sampleEntry', 'filename', 'version', 'scan_number', 'rank', 'dimensions', 'acquired_dimensions', 'isRegular', 'ourKeys']
663 if pExtra:
664 scanFile.seek(pExtra)
665 buf = scanFile.read() # Read all scan-environment data
666 u.reset(buf)
667 numExtra = u.unpack_int()
668 if verbose: out.write("\nnumber of 'Extra' PV's = %d\n" % numExtra)
669 for i in range(numExtra):
670 if verbose: out.write("env PV #%d -------\n" % (i))
671 name = ''
672 n = u.unpack_int() # length of name string
673 if n: name = u.unpack_string()
674 if verbose: out.write("\tname = '%s'\n" % name)
675 desc = ''
676 n = u.unpack_int() # length of desc string
677 if n: desc = u.unpack_string()
678 if verbose: out.write("\tdesc = '%s'\n" % desc)
679 EPICS_type = u.unpack_int()
680 if verbose: out.write("\tEPICS_type = %d (%s)\n" % (EPICS_type, EPICS_types(EPICS_type)))
682 unit = ''
683 value = ''
684 count = 0
685 if EPICS_type != 0: # not DBR_STRING; array is permitted
686 count = u.unpack_int() #
687 if verbose: out.write("\tcount = %d\n" % count)
688 n = u.unpack_int() # length of unit string
689 if n: unit = u.unpack_string()
690 if verbose: out.write("\tunit = '%s'\n" % unit)
692 if EPICS_type == 0: # DBR_STRING
693 n = u.unpack_int() # length of value string
694 if n: value = u.unpack_string()
695 elif EPICS_type == 32: # DBR_CTRL_CHAR
696 #value = u.unpack_fstring(count)
697 vect = u.unpack_farray(count, u.unpack_int)
698 value = ""
699 for i in range(len(vect)):
700 # treat the byte array as a null-terminated string
701 if vect[i] == 0: break
702 value = value + chr(vect[i])
703 elif EPICS_type == 29: # DBR_CTRL_SHORT
704 value = u.unpack_farray(count, u.unpack_int)
705 elif EPICS_type == 33: # DBR_CTRL_LONG
706 value = u.unpack_farray(count, u.unpack_int)
707 elif EPICS_type == 30: # DBR_CTRL_FLOAT
708 value = u.unpack_farray(count, u.unpack_float)
709 elif EPICS_type == 34: # DBR_CTRL_DOUBLE
710 value = u.unpack_farray(count, u.unpack_double)
711 if verbose:
712 if (EPICS_type == 0):
713 out.write("\tvalue = '%s'\n" % (value))
714 else:
715 out.write("\tvalue = ")
716 verboseData(value, out)
718 dict[name] = (desc, unit, value, EPICS_type, count)
719 scanFile.close()
721 dim.reverse()
722 dim.append(dict)
723 dim.reverse()
724 if verbose or showHelp:
725 print("\n%s is a %d-D file; %d dimensions read in." % (fname, dim[0]['rank'], len(dim)-1))
726 print("dim[0] = dictionary of %d scan-environment PVs" % (len(dim[0])))
727 print(" usage: dim[0]['sampleEntry'] ->", dim[0]['sampleEntry'])
728 for i in range(1,len(dim)):
729 print("dim[%d] = %s" % (i, str(dim[i])))
730 print(" usage: dim[1].p[2].data -> 1D array of positioner 2 data")
731 print(" usage: dim[2].d[7].data -> 2D array of detector 7 data")
733 if showHelp:
734 print(" ")
735 print(" each scan dimension (i.e., dim[1], dim[2], ...) has the following fields: ")
736 print(" time - date & time at which scan was started: %s" % (dim[1].time))
737 print(" name - name of scan record that acquired this dimension: '%s'" % (dim[1].name))
738 print(" curr_pt - number of data points actually acquired: %d" % (dim[1].curr_pt))
739 print(" npts - number of data points requested: %d" % (dim[1].npts))
740 print(" nd - number of detectors for this scan dimension: %d" % (dim[1].nd))
741 print(" d[] - list of detector-data structures")
742 print(" np - number of positioners for this scan dimension: %d" % (dim[1].np))
743 print(" p[] - list of positioner-data structures")
744 print(" nt - number of detector triggers for this scan dimension: %d" % (dim[1].nt))
745 print(" t[] - list of trigger-info structures")
747 print(" ")
748 print(" each detector-data structure (e.g., dim[1].d[0]) has the following fields: ")
749 print(" desc - description of this detector")
750 print(" data - data list")
751 print(" unit - engineering units associated with this detector")
752 print(" fieldName - scan-record field (e.g., 'D01')")
754 print(" ")
755 print(" each positioner-data structure (e.g., dim[1].p[0]) has the following fields: ")
756 print(" desc - description of this positioner")
757 print(" data - data list")
758 print(" step_mode - scan mode (e.g., Linear, Table, On-The-Fly)")
759 print(" unit - engineering units associated with this positioner")
760 print(" fieldName - scan-record field (e.g., 'P1')")
761 print(" name - name of EPICS PV (e.g., 'xxx:m1.VAL')")
762 print(" readback_desc - description of this positioner")
763 print(" readback_unit - engineering units associated with this positioner")
764 print(" readback_name - name of EPICS PV (e.g., 'xxx:m1.VAL')")
766 if (outFile):
767 out.close()
768 return dim
770################################################################################
771# skim MDA file to get dimensions (planned and actually acquired), and other info
772def skimScan(dataFile):
773 """usage: skimScan(dataFile)"""
774 scan = scanDim() # data structure to hold scan info and data
775 buf = dataFile.read(10000) # enough to read scan header
776 u = Unpacker(buf)
777 scan.rank = u.unpack_int()
778 if (scan.rank > 20) or (scan.rank < 0):
779 print("* * * skimScan('%s'): rank > 20. probably a corrupt file" % dataFile.name)
780 return None
781 scan.npts = u.unpack_int()
782 scan.curr_pt = u.unpack_int()
783 if (scan.curr_pt == 0):
784 #print("mda:skimScan: curr_pt = 0")
785 return None
786 if (scan.rank > 1):
787 scan.plower_scans = u.unpack_farray(scan.npts, u.unpack_int)
788 namelength = u.unpack_int()
789 scan.name = u.unpack_string()
790 timelength = u.unpack_int()
791 scan.time = u.unpack_string()
792 scan.np = u.unpack_int()
793 scan.nd = u.unpack_int()
794 scan.nt = u.unpack_int()
795 return scan
797def skimMDA(fname=None, verbose=False):
798 """usage skimMDA(fname=None)"""
799 #print("skimMDA: filename=", fname)
800 dim = []
801 if (fname == None):
802 print("No file specified")
803 return None
804 if (not os.path.isfile(fname)):
805 if (not fname.endswith('.mda')):
806 fname = fname + '.mda'
807 if (not os.path.isfile(fname)):
808 print(fname, "not found")
809 return None
811 try:
812 dataFile = open(fname, 'rb')
813 except:
814 print("mda_f:skimMDA: failed to open file '%s'" % fname)
815 return None
817 buf = dataFile.read(100) # to read header for scan of up to 5 dimensions
818 u = Unpacker(buf)
820 # read file header
821 version = u.unpack_float()
822# if version < 1.299 or version > 1.301:
823# print(fname, " has file version", version)
824# return None
825 scan_number = u.unpack_int()
826 rank = u.unpack_int()
827 dimensions = u.unpack_farray(rank, u.unpack_int)
828 isRegular = u.unpack_int()
829 pExtra = u.unpack_int()
830 pmain_scan = dataFile.tell() - (len(buf) - u.get_position())
832 # collect 1D data
833 dataFile.seek(pmain_scan)
834 scan = skimScan(dataFile)
835 if (scan == None):
836 if verbose: print(fname, "contains no data")
837 return None
839 dim.append(scan)
840 dim[0].dim = 1
842 if (rank > 1):
843 dataFile.seek(dim[0].plower_scans[0])
844 dim.append(skimScan(dataFile))
845 if (dim[1]):
846 dim[1].dim = 2
847 else:
848 if verbose: print("had a problem reading 2d from ", fname)
849 return None
851 if (rank > 2):
852 dataFile.seek(dim[1].plower_scans[0])
853 dim.append(skimScan(dataFile))
854 if (dim[2]):
855 dim[2].dim = 3
856 else:
857 if verbose: print("had a problem reading 3d from ", fname)
858 return None
860 if (rank > 3):
861 dataFile.seek(dim[2].plower_scans[0])
862 dim.append(skimScan(dataFile))
863 if (dim[3]):
864 dim[3].dim = 4
865 else:
866 if verbose: print("had a problem reading 4d from ", fname)
867 return None
869 dataFile.close()
870 dict = {}
871 dict['filename'] = fname
872 dict['version'] = version
873 dict['scan_number'] = scan_number
874 dict['rank'] = rank
875 dict['dimensions'] = dimensions
876 dimensions = []
877 for d in dim:
878 dimensions.append(d.curr_pt)
879 dict['acquired_dimensions'] = dimensions
880 dict['isRegular'] = isRegular
881 dim.reverse()
882 dim.append(dict)
883 dim.reverse()
884 return dim
886################################################################################
887# Write MDA file
888def packScanHead(scan):
889 s = scanBuf()
890 s.npts = scan.npts
892 # preamble
893 p = Packer()
894 p.pack_int(scan.rank)
895 p.pack_int(scan.npts)
896 p.pack_int(scan.curr_pt)
897 s.preamble = p.get_buffer()
899 # file offsets to lower level scans (if any)
900 p.reset()
901 if (scan.rank > 1):
902 # Pack zeros for now, so we'll know how much
903 # space the real offsets will use.
904 for j in range(scan.npts):
905 p.pack_int(0)
906 s.pLowerScansBuf = p.get_buffer()
908 # postamble
909 p.reset()
910 n = len(scan.name); p.pack_int(n)
911 if (n): p.pack_string(scan.name)
912 n = len(scan.time); p.pack_int(n)
913 if (n): p.pack_string(scan.time)
914 p.pack_int(scan.np)
915 p.pack_int(scan.nd)
916 p.pack_int(scan.nt)
918 for j in range(scan.np):
919 p.pack_int(scan.p[j].number)
921 n = len(scan.p[j].name); p.pack_int(n)
922 if (n): p.pack_string(scan.p[j].name)
924 n = len(scan.p[j].desc); p.pack_int(n)
925 if (n): p.pack_string(scan.p[j].desc)
927 n = len(scan.p[j].step_mode); p.pack_int(n)
928 if (n): p.pack_string(scan.p[j].step_mode)
930 n = len(scan.p[j].unit); p.pack_int(n)
931 if (n): p.pack_string(scan.p[j].unit)
933 n = len(scan.p[j].readback_name); p.pack_int(n)
934 if (n): p.pack_string(scan.p[j].readback_name)
936 n = len(scan.p[j].readback_desc); p.pack_int(n)
937 if (n): p.pack_string(scan.p[j].readback_desc)
939 n = len(scan.p[j].readback_unit); p.pack_int(n)
940 if (n): p.pack_string(scan.p[j].readback_unit)
942 for j in range(scan.nd):
943 p.pack_int(scan.d[j].number)
944 n = len(scan.d[j].name); p.pack_int(n)
945 if (n): p.pack_string(scan.d[j].name)
946 n = len(scan.d[j].desc); p.pack_int(n)
947 if (n): p.pack_string(scan.d[j].desc)
948 n = len(scan.d[j].unit); p.pack_int(n)
949 if (n): p.pack_string(scan.d[j].unit)
951 for j in range(scan.nt):
952 p.pack_int(scan.t[j].number)
953 n = len(scan.t[j].name); p.pack_int(n)
954 if (n): p.pack_string(scan.t[j].name)
955 p.pack_float(scan.t[j].command)
957 s.postamble = p.get_buffer()
958 s.bufLen = len(s.preamble) + len(s.pLowerScansBuf) + len(s.postamble)
959 return s
961def packScanData(scan, cpt):
962 p = Packer()
963 if (len(cpt) == 0): # 1D array
964 for i in range(scan.np):
965 p.pack_farray(scan.npts, scan.p[i].data, p.pack_double)
966 for i in range(scan.nd):
967 p.pack_farray(scan.npts, scan.d[i].data, p.pack_float)
969 elif (len(cpt) == 1): # 2D array
970 j = cpt[0]
971 for i in range(scan.np):
972 p.pack_farray(scan.npts, scan.p[i].data[j], p.pack_double)
973 for i in range(scan.nd):
974 p.pack_farray(scan.npts, scan.d[i].data[j], p.pack_float)
976 elif (len(cpt) == 2): # 3D array
977 j = cpt[0]
978 k = cpt[1]
979 for i in range(scan.np):
980 p.pack_farray(scan.npts, scan.p[i].data[j][k], p.pack_double)
981 for i in range(scan.nd):
982 p.pack_farray(scan.npts, scan.d[i].data[j][k], p.pack_float)
984 return(p.get_buffer())
986def writeMDA(dim, fname=None):
987 m = mdaBuf()
988 p = Packer()
990 p.reset()
991 if (type(dim) != type([])): print("writeMDA: first arg must be a scan")
992 if ((fname != None) and (type(fname) != type(""))):
993 print("writeMDA: second arg must be a filename or None")
994 rank = dim[0]['rank'] # rank of scan as a whole
995 # write file header
996 p.pack_float(dim[0]['version'])
997 p.pack_int(dim[0]['scan_number'])
998 p.pack_int(dim[0]['rank'])
999 p.pack_farray(rank, dim[0]['dimensions'], p.pack_int)
1000 p.pack_int(dim[0]['isRegular'])
1001 m.header = p.get_buffer()
1003 p.reset()
1004 p.pack_int(0) # pExtra
1005 m.pExtra = p.get_buffer()
1007 m.scan = packScanHead(dim[1])
1008 m.scan.offset = len(m.header) + len(m.pExtra)
1009 m.scan.data = packScanData(dim[1], [])
1010 m.scan.bufLen = m.scan.bufLen + len(m.scan.data)
1011 prevScan = m.scan
1012 #print("\n m.scan=", m.scan)
1013 #print("\n type(m.scan.pLowerScans)=", type(m.scan.pLowerScans))
1015 if (rank > 1):
1016 for i in range(m.scan.npts):
1017 m.scan.inner.append(packScanHead(dim[2]))
1018 thisScan = m.scan.inner[i]
1019 thisScan.offset = prevScan.offset + prevScan.bufLen
1020 m.scan.pLowerScans.append(thisScan.offset)
1021 thisScan.data = packScanData(dim[2], [i])
1022 thisScan.bufLen = thisScan.bufLen + len(thisScan.data)
1023 prevScan = thisScan
1025 if (rank > 2):
1026 for j in range(m.scan.inner[i].npts):
1027 m.scan.inner[i].inner.append(packScanHead(dim[3]))
1028 thisScan = m.scan.inner[i].inner[j]
1029 thisScan.offset = prevScan.offset + prevScan.bufLen
1030 m.scan.inner[i].pLowerScans.append(thisScan.offset)
1031 thisScan.data = packScanData(dim[3], [i,j])
1032 thisScan.bufLen = thisScan.bufLen + len(thisScan.data)
1033 prevScan = thisScan
1035 if (rank > 3):
1036 for k in range(m.scan.inner[i].inner[j].npts):
1037 m.scan.inner[i].inner[j].append(packScanHead(dim[4]))
1038 thisScan = m.scan.inner[i].inner[j].inner[k]
1039 thisScan.offset = prevScan.offset + prevScan.bufLen
1040 m.scan.inner[i].inner[j].pLowerScans.append(thisScan.offset)
1041 thisScan.data = packScanData(dim[4], [i,j,k])
1042 thisScan.bufLen = thisScan.bufLen + len(thisScan.data)
1043 prevScan = thisScan
1045 # Now we know where the extraPV section must go.
1046 p.reset()
1047 p.pack_int(prevScan.offset + prevScan.bufLen) # pExtra
1048 m.pExtra = p.get_buffer()
1050 # pack scan-environment variables from dictionary
1051 p.reset()
1053 numKeys = 0
1054 for name in dim[0].keys():
1055 if not (name in dim[0]['ourKeys']):
1056 numKeys = numKeys + 1
1057 p.pack_int(numKeys)
1059 for name in dim[0].keys():
1060 # Note we don't want to write the dict entries we made for our own
1061 # use in the scanDim object.
1062 if not (name in dim[0]['ourKeys']):
1063 desc = dim[0][name][0]
1064 unit = dim[0][name][1]
1065 value = dim[0][name][2]
1066 EPICS_type = dim[0][name][3]
1067 count = dim[0][name][4]
1068 n = len(name); p.pack_int(n)
1069 if (n): p.pack_string(name)
1070 n = len(desc); p.pack_int(n)
1071 if (n): p.pack_string(desc)
1072 p.pack_int(EPICS_type)
1073 if EPICS_type != 0: # not DBR_STRING, so pack count and units
1074 p.pack_int(count)
1075 n = len(unit); p.pack_int(n)
1076 if (n): p.pack_string(unit)
1077 if EPICS_type == 0: # DBR_STRING
1078 n = len(value); p.pack_int(n)
1079 if (n): p.pack_string(value)
1080 elif EPICS_type == 32: # DBR_CTRL_CHAR
1081 # write null-terminated string
1082 v = []
1083 for i in range(len(value)): v.append(ord(value[i:i+1]))
1084 v.append(0)
1085 p.pack_farray(count, v, p.pack_int)
1086 elif EPICS_type == 29: # DBR_CTRL_SHORT
1087 p.pack_farray(count, value, p.pack_int)
1088 elif EPICS_type == 33: # DBR_CTRL_LONG
1089 p.pack_farray(count, value, p.pack_int)
1090 elif EPICS_type == 30: # DBR_CTRL_FLOAT
1091 p.pack_farray(count, value, p.pack_float)
1092 elif EPICS_type == 34: # DBR_CTRL_DOUBLE
1093 p.pack_farray(count, value, p.pack_double)
1095 m.extraPV = p.get_buffer()
1097 # Now we have to repack all the scan offsets
1098 if (rank > 1): # 2D scan
1099 #print("m.scan.pLowerScans", m.scan.pLowerScans)
1100 p.reset()
1101 p.pack_farray(m.scan.npts, m.scan.pLowerScans, p.pack_int)
1102 m.scan.pLowerScansBuf = p.get_buffer()
1103 if (rank > 2): # 3D scan
1104 for i in range(m.scan.npts):
1105 p.reset()
1106 p.pack_farray(m.scan.inner[i].npts, m.scan.inner[i].pLowerScans, p.pack_int)
1107 m.scan.inner[i].pLowerScansBuf = p.get_buffer()
1108 if (rank > 3): # 4D scan
1109 for j in range(m.scan.inner[i].npts):
1110 p.reset()
1111 p.pack_farray(m.scan.inner[i].inner[j].npts, m.scan.inner[i].inner[j].pLowerScans, p.pack_int)
1112 m.scan.inner[i].inner[j].pLowerScansBuf = p.get_buffer()
1114 # Write
1115 if (fname == None): fname = tkFileDialog.SaveAs().show()
1116 f = open(fname, 'wb')
1118 f.write(m.header)
1119 f.write(m.pExtra)
1120 s0 = m.scan
1121 f.write(s0.preamble)
1122 if len(s0.pLowerScansBuf): f.write(s0.pLowerScansBuf)
1123 f.write(s0.postamble)
1124 f.write(s0.data)
1125 for s1 in s0.inner:
1126 f.write(s1.preamble)
1127 if len(s1.pLowerScansBuf): f.write(s1.pLowerScansBuf)
1128 f.write(s1.postamble)
1129 f.write(s1.data)
1130 for s2 in s1.inner:
1131 f.write(s2.preamble)
1132 if len(s2.pLowerScansBuf): f.write(s2.pLowerScansBuf)
1133 f.write(s2.postamble)
1134 f.write(s2.data)
1135 for s3 in s2.inner:
1136 f.write(s3.preamble)
1137 if len(s3.pLowerScansBuf): f.write(s3.pLowerScansBuf)
1138 f.write(s3.postamble)
1139 f.write(s3.data)
1140 f.write(m.extraPV)
1141 f.close()
1142 return
1144################################################################################
1145# write Ascii file
1146def getFormat(d, rank):
1147 # number of positioners, detectors
1148 np = d[rank].np
1149 nd = d[rank].nd
1151 min_column_width = 15
1152 # make sure there's room for the names, etc.
1153 phead_fmt = []
1154 dhead_fmt = []
1155 pdata_fmt = []
1156 ddata_fmt = []
1157 columns = 1
1158 for i in range(np):
1159 cw = max(min_column_width, len(d[rank].p[i].name)+1)
1160 cw = max(cw, len(d[rank].p[i].desc)+1)
1161 cw = max(cw, len(d[rank].p[i].fieldName)+1)
1162 phead_fmt.append("%%-%2ds" % cw)
1163 pdata_fmt.append("%%- %2d.8f" % cw)
1164 columns = columns + cw
1165 for i in range(nd):
1166 cw = max(min_column_width, len(d[rank].d[i].name)+1)
1167 cw = max(cw, len(d[rank].d[i].desc)+1)
1168 cw = max(cw, len(d[rank].d[i].fieldName)+1)
1169 dhead_fmt.append("%%-%2ds" % cw)
1170 ddata_fmt.append("%%- %2d.8f" % cw)
1171 columns = columns + cw
1172 return (phead_fmt, dhead_fmt, pdata_fmt, ddata_fmt, columns)
1174def writeAscii(d, fname=None):
1175 if (type(d) != type([])):
1176 print("writeMDA: first arg must be a scan")
1177 return
1179 if (fname == None):
1180 f = sys.stdout
1181 else:
1182 f = open(fname, 'wb')
1184 (phead_fmt, dhead_fmt, pdata_fmt, ddata_fmt, columns) = getFormat(d, 1)
1185 # header
1186 f.write("### %s is a %d-dimensional file\n" % (d[0]['filename'], d[0]['rank']))
1187 f.write("### Number of data points = [")
1188 for i in range(d[0]['rank'],1,-1): f.write("%-d," % d[i].curr_pt)
1189 f.write("%-d]\n" % d[1].curr_pt)
1191 f.write("### Number of detector signals = [")
1192 for i in range(d[0]['rank'],1,-1): f.write("%-d," % d[i].nd)
1193 f.write("%-d]\n" % d[1].nd)
1195 # scan-environment PV values
1196 f.write("#\n# Scan-environment PV values:\n")
1197 ourKeys = d[0]['ourKeys']
1198 maxKeyLen = 0
1199 for i in d[0].keys():
1200 if (i not in ourKeys):
1201 if len(i) > maxKeyLen: maxKeyLen = len(i)
1202 for i in d[0].keys():
1203 if (i not in ourKeys):
1204 f.write("#%s%s%s\n" % (i, (maxKeyLen-len(i))*' ', d[0][i]))
1206 f.write("\n#%s\n" % str(d[1]))
1207 f.write("# scan date, time: %s\n" % d[1].time)
1208 sep = "#"*columns + "\n"
1209 f.write(sep)
1211 # 1D data table head
1212 f.write("#")
1213 for j in range(d[1].np):
1214 f.write(phead_fmt[j] % (d[1].p[j].fieldName))
1215 for j in range(d[1].nd):
1216 f.write(dhead_fmt[j] % (d[1].d[j].fieldName))
1217 f.write("\n")
1219 f.write("#")
1220 for j in range(d[1].np):
1221 f.write(phead_fmt[j] % (d[1].p[j].name))
1222 for j in range(d[1].nd):
1223 f.write(dhead_fmt[j] % (d[1].d[j].name))
1224 f.write("\n")
1226 f.write("#")
1227 for j in range(d[1].np):
1228 f.write(phead_fmt[j] % (d[1].p[j].desc))
1229 for j in range(d[1].nd):
1230 f.write(dhead_fmt[j] % (d[1].d[j].desc))
1231 f.write("\n")
1233 f.write(sep)
1235 # 1D data
1236 for i in range(d[1].curr_pt):
1237 f.write("")
1238 for j in range(d[1].np):
1239 f.write(pdata_fmt[j] % (d[1].p[j].data[i]))
1240 for j in range(d[1].nd):
1241 f.write(ddata_fmt[j] % (d[1].d[j].data[i]))
1242 f.write("\n")
1244 # 2D data
1245 if (len(d) > 2):
1246 f.write("\n# 2D data\n")
1247 for i in range(d[2].np):
1248 f.write("\n# Positioner %d (.%s) PV:'%s' desc:'%s'\n" % (i, d[2].p[i].fieldName, d[2].p[i].name, d[2].p[i].desc))
1249 for j in range(d[1].curr_pt):
1250 for k in range(d[2].curr_pt):
1251 f.write("%f " % d[2].p[i].data[j][k])
1252 f.write("\n")
1254 for i in range(d[2].nd):
1255 f.write("\n# Detector %d (.%s) PV:'%s' desc:'%s'\n" % (i, d[2].d[i].fieldName, d[2].d[i].name, d[2].d[i].desc))
1256 for j in range(d[1].curr_pt):
1257 for k in range(d[2].curr_pt):
1258 f.write("%f " % d[2].d[i].data[j][k])
1259 f.write("\n")
1261 if (len(d) > 3):
1262 f.write("\n# Can't write 3D (or higher) data\n")
1264 if (fname != None):
1265 f.close()
1268################################################################################
1269# misc
1270def showEnv(dict, all=0):
1271 if type(dict) == type([]) and type(dict[0]) == type({}):
1272 dict = dict[0]
1273 fieldLen = 0
1274 for k in dict.keys():
1275 if len(k) > fieldLen:
1276 fieldLen = len(k)
1277 format = "%%-%-ds %%s" % fieldLen
1278 for k in dict.keys():
1279 if not (k in dict['ourKeys']):
1280 if type(dict[k]) == type((1,2,3)):
1281 value = dict[k][2]
1282 else:
1283 value = dict[k]
1284 if type(value) == type([]) and len(value) == 1:
1285 value = value[0]
1286 if all:
1287 print(format % (k,dict[k]))
1288 else:
1289 print(format % (k,value))
1290 return
1292def fixMDA(d):
1293 """usage: d=fixMDA(d), where d is a list returned by readMDA()"""
1294 dimensions = []
1295 for i in range(1,len(d)):
1296 npts = d[i].curr_pt
1297 d[i].npts = npts
1298 dimensions.append(npts)
1299 for j in range(d[i].np):
1300 if (len(d[i].p[j].data) > npts):
1301 d[i].p[j].data = d[i].p[j].data[0:npts]
1302 for j in range(d[i].nd):
1303 if (len(d[i].d[j].data) > npts):
1304 d[i].d[j].data = d[i].d[j].data[0:npts]
1305 dimensions.reverse()
1306 d[0]['dimensions'] = dimensions
1307 return(d)
1309# translate mca-ROI PV's to mca-ROI description PV's, scaler signal PV'ss to scaler signal description PV's
1310descDict = {'R1':'R1NM', 'R2':'R2NM', 'R3':'R3NM', 'R4':'R4NM', 'R5':'R5NM',
1311 'R6':'R6NM', 'R7':'R7NM', 'R8':'R8NM', 'R9':'R9NM', 'R10':'R10NM',
1312 'R11':'R11NM', 'R12':'R12NM', 'R13':'R13NM', 'R14':'R14NM', 'R15':'R15NM',
1313 'R16':'R16NM', 'R17':'R17NM', 'R18':'R18NM', 'R19':'R19NM', 'R20':'R20NM',
1314 'R21':'R21NM', 'R22':'R22NM', 'R23':'R23NM', 'R24':'R24NM', 'R25':'R25NM',
1315 'R26':'R26NM', 'R27':'R27NM', 'R28':'R28NM', 'R29':'R29NM', 'R30':'R30NM',
1316 'R31':'R31NM', 'R32':'R32NM',
1317 'S1':'NM1', 'S2':'NM2', 'S3':'NM3', 'S4':'NM4', 'S5':'NM5', 'S6':'NM6', 'S7':'NM7', 'S8':'NM8', 'S9':'NM9', 'S10':'NM10',
1318 'S11':'NM11', 'S12':'NM12', 'S13':'NM13', 'S14':'NM14', 'S15':'NM15', 'S16':'NM16', 'S17':'NM17', 'S18':'NM18', 'S19':'NM19', 'S20':'NM20',
1319 'S21':'NM21', 'S22':'NM22', 'S23':'NM23', 'S24':'NM24', 'S25':'NM25', 'S26':'NM26', 'S27':'NM27', 'S28':'NM28', 'S29':'NM29', 'S30':'NM30',
1320 'S31':'NM31', 'S32':'NM32', 'S33':'NM33', 'S34':'NM34', 'S35':'NM35', 'S36':'NM36', 'S37':'NM37', 'S38':'NM38', 'S39':'NM39', 'S40':'NM40',
1321 'S41':'NM41', 'S42':'NM42', 'S43':'NM43', 'S44':'NM44', 'S45':'NM45', 'S46':'NM46', 'S47':'NM47', 'S48':'NM48', 'S49':'NM49', 'S50':'NM50',
1322 'S51':'NM51', 'S52':'NM52', 'S53':'NM53', 'S54':'NM54', 'S55':'NM55', 'S56':'NM56', 'S57':'NM57', 'S58':'NM58', 'S59':'NM59', 'S60':'NM60',
1323 'S61':'NM61', 'S62':'NM62', 'S63':'NM63', 'S64':'NM64'}
1325def findDescInEnv(name, env):
1326 try:
1327 (record, field) = name.split('.')
1328 except:
1329 return ""
1330 try:
1331 descField = descDict[field]
1332 except:
1333 return ""
1334 try:
1335 desc = env[record+'.'+descField]
1336 except:
1337 return ""
1338 if desc[2] == "" or desc[2].isspace():
1339 return ""
1340 return "{%s}" % desc[2]
1342def getDescFromEnv(data):
1343 if (data):
1344 for d in data[1:]:
1345 for p in d.p:
1346 if not p.desc:
1347 p.desc = findDescInEnv(p.name, data[0])
1348 for d in d.d:
1349 if not d.desc:
1350 d.desc = findDescInEnv(d.name, data[0])
1352########################
1353# opMDA and related code
1354########################
1355def isScan(d):
1356 if type(d) != type([]): return(0)
1357 if type(d[0]) != type({}): return(0)
1358 if 'rank' not in d[0].keys(): return(0)
1359 if len(d) < 2: return(0)
1360 if type(d[1]) != type(scanDim()): return(0)
1361 return(1)
1363def isScalar(d):
1364 if (type(d) == type(1)) or (type(d) == type(1.0)): return(1)
1365 return(0)
1367def add(a,b): return(a+b)
1368def sub(a,b): return(a-b)
1369def mul(a,b): return(a*b)
1370def div(a,b): return(a/b)
1372def setOp(op):
1373 if (op == '+') or (op == 'add'): return(add)
1374 if (op == '-') or (op == 'sub'): return(sub)
1375 if (op == '*') or (op == 'x') or (op == 'mul'): return(mul)
1376 if (op == '/') or (op == 'div'): return(div)
1377 if (op == '>') or (op == 'max'): return(max)
1378 if (op == '<') or (op == 'min'): return(min)
1379 print("opMDA: unrecognized op = ", op)
1380 return None
1382def opMDA_usage():
1383 print("opMDA() usage:")
1384 print(" result = opMDA(op, scan1, scan2)")
1385 print(" OR")
1386 print(" result = opMDA(op, scan1, scalar_value)")
1387 print("\nwhere:")
1388 print(" op is one of '+', '-', '*', '/', '>', '<'")
1389 print(" scan1, scan2 are scans, i.e., structures returned by mda.readMDA()")
1390 print(" result is a copy of scan1, modified by the operation\n")
1391 print("\n examples:")
1392 print(" r = opMDA('+', scan1, scan2) -- adds all detector data from scan1 and scan2")
1393 print(" r = opMDA('-', scan1, 2.0) -- subtracts 2 from all detector data from scan1")
1394 print(" r = opMDA('>', r, 0) -- 'r' data or 0, whichever is greater")
1396def opMDA_scalar(op, d1, scalar):
1397 op = setOp(op)
1398 if (op == None):
1399 opMDA_usage()
1400 return None
1402 s = copy.deepcopy(d1)
1404 # 1D op
1405 for i in range(s[1].nd):
1406 for j in range(s[1].npts):
1407 s[1].d[i].data[j] = op(s[1].d[i].data[j], scalar)
1409 if (len(s) == 2): return s
1410 # 2D op
1411 for i in range(s[2].nd):
1412 for j in range(s[1].npts):
1413 for k in range(s[2].npts):
1414 s[2].d[i].data[j][k] = op(s[2].d[i].data[j][k], scalar)
1416 if (len(s) == 3): return s
1417 # 3D op
1418 for i in range(s[3].nd):
1419 for j in range(s[1].npts):
1420 for k in range(s[2].npts):
1421 for l in range(s[3].npts):
1422 s[3].d[i].data[j][k][l] = op(s[3].d[i].data[j][k][l], scalar)
1424 if (len(s) == 4): return s
1425 # 4D op
1426 for i in range(s[4].nd):
1427 for j in range(s[1].npts):
1428 for k in range(s[2].npts):
1429 for l in range(s[3].npts):
1430 for m in range(s[4].npts):
1431 s[4].d[i].data[j][k][l][m] = op(s[4].d[i].data[j][k][l][m], scalar)
1433 if (len(s) > 4):
1434 print("opMDA supports up to 4D scans")
1435 return s
1437def opMDA(op, d1, d2):
1438 """opMDA() is a function for performing arithmetic operations on MDA files,
1439 or on an MDA file and a scalar value.
1441 For examples, type 'opMDA_usage()'.
1442 """
1443 if isScan(d1) and isScalar(d2): return(opMDA_scalar(op,d1,d2))
1444 if (not isScan(d1)) :
1445 print("opMDA: first operand is not a scan")
1446 opMDA_usage()
1447 return None
1449 if (not isScan(d2)):
1450 print("opMDA: second operand is neither a scan nor a scalar")
1451 opMDA_usage()
1452 return None
1454 if len(d1) != len(d2):
1455 print("scans do not have same dimension")
1456 return None
1458 op = setOp(op)
1459 if (op == None):
1460 opMDA_usage()
1461 return None
1463 s = copy.deepcopy(d1)
1465 # 1D op
1466 if s[1].nd != d2[1].nd:
1467 print("scans do not have same number of 1D detectors")
1468 return None
1469 if s[1].npts != d2[1].npts:
1470 print("scans do not have same number of data points")
1471 return None
1472 for i in range(s[1].nd):
1473 s[1].d[i].data = map(op, s[1].d[i].data, d2[1].d[i].data)
1475 if (len(s) == 2): return s
1476 # 2D op
1477 if s[2].nd != d2[2].nd:
1478 print("scans do not have same number of 2D detectors")
1479 return None
1480 if s[2].npts != d2[2].npts:
1481 print("scans do not have same number of data points")
1482 return None
1483 for i in range(s[2].nd):
1484 for j in range(s[1].npts):
1485 s[2].d[i].data[j] = map(op, s[2].d[i].data[j], d2[2].d[i].data[j])
1487 if (len(s) == 3): return s
1488 # 3D op
1489 if s[3].nd != d2[3].nd:
1490 print("scans do not have same number of 3D detectors")
1491 return None
1492 if s[3].npts != d2[3].npts:
1493 print("scans do not have same number of data points")
1494 return None
1495 for i in range(s[3].nd):
1496 for j in range(s[1].npts):
1497 for k in range(s[2].npts):
1498 s[3].d[i].data[j][k] = map(op, s[3].d[i].data[j][k], d2[3].d[i].data[j][k])
1500 if (len(s) == 4): return s
1501 # 3D op
1502 if s[4].nd != d2[4].nd:
1503 print("scans do not have same number of 4D detectors")
1504 return None
1505 if s[4].npts != d2[4].npts:
1506 print("scans do not have same number of data points")
1507 return None
1508 for i in range(s[4].nd):
1509 for j in range(s[1].npts):
1510 for k in range(s[2].npts):
1511 for l in range(s[3].npts):
1512 s[4].d[i].data[j][k][l] = map(op, s[4].d[i].data[j][k][l], d2[4].d[i].data[j][k][l])
1514 if (len(s) > 5):
1515 print("opMDA supports up to 4D scans")
1516 return s
1519def read_mda(fname, maxdim=3, verbose=False):
1520 """read an MDA file from the Epics Scan Record
1522 Arguments
1523 ---------
1524 filname (str) name of file
1525 maxdim (integer) max number of dimensions [default=3]
1527 Returns
1528 -------
1529 group containing `scan1` and possibly `scan2`, etc objects
1531 Notes
1532 -----
1533 not very well tested for scans of dimension > 2
1535 """
1536 print("WARNING: support for MDA files will expire in 2024")
1537 out = readMDA(fname, maxdim=maxdim, verbose=verbose)
1538 group = Group(name=f'MDA_file {fname}')
1539 group.extra_pvs = out[0]
1540 group.scan1 = out[1]
1541 group.dimension = dim = len(out) - 1
1543 if dim > 1:
1544 group.scan2 = out[2]
1545 if dim > 2:
1546 group.scan3 = out[3]
1547 if dim > 3:
1548 group.scan4 = out[4]
1550 if dim == 1:
1551 data, array_labels, field_names, pv_names = [], [], [], []
1552 for x in group.scan1.p + group.scan1.d:
1553 data.append(x.data)
1554 pv_names.append(x.name)
1555 field_names.append(x.fieldName)
1556 label = x.desc
1557 if label in (None, 'None', ''):
1558 label = x.fieldName
1559 array_labels.append(label)
1561 group.data = numpy.array(data)
1562 group.pv_names = pv_names
1563 group.field_names = field_names
1564 group.array_labels = array_labels
1565 return group