newportxps 0.3.0__py3-none-any.whl → 2025.1.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
newportxps/newportxps.py CHANGED
@@ -1,11 +1,11 @@
1
1
  #!/usr/bin/env python
2
2
 
3
- import os
4
3
  import posixpath
4
+ import atexit
5
5
  import sys
6
6
  import time
7
7
  import socket
8
- from io import StringIO
8
+ from copy import deepcopy
9
9
  from configparser import ConfigParser
10
10
  import numpy as np
11
11
 
@@ -75,11 +75,12 @@ class NewportXPS:
75
75
  err, uptime = self._xps.ElapsedTimeGet(self._sid)
76
76
  self.check_error(err, msg="Elapsed Time")
77
77
  boottime = time.time() - uptime
78
- out = ["# XPS host: %s (%s)" % (self.host, socket.getfqdn(self.host)),
79
- "# Firmware: %s" % self.firmware_version,
80
- "# Current Time: %s" % time.ctime(),
81
- "# Last Reboot: %s" % time.ctime(boottime),
82
- "# Trajectory Group: %s" % self.traj_group,
78
+ hostn = socket.getfqdn(self.host)
79
+ out = [f"# XPS host: {self.host} ({hostn})",
80
+ f"# Firmware: {self.firmware_version}",
81
+ f"# Current Time: {time.ctime()}",
82
+ f"# Last Reboot: {time.ctime(boottime)}",
83
+ f"# Trajectory Group: {self.traj_group}",
83
84
  ]
84
85
 
85
86
  out.append("# Groups and Stages")
@@ -88,30 +89,35 @@ class NewportXPS:
88
89
 
89
90
  for groupname, status in self.get_group_status().items():
90
91
  this = self.groups[groupname]
91
- out.append("%s (%s), Status: %s" %
92
- (groupname, this['category'], status))
92
+ out.append(f"{groupname} ({this['category']}), Status: {status}")
93
93
  for pos in this['positioners']:
94
- stagename = '%s.%s' % (groupname, pos)
94
+ stagename = f"{groupname}.{pos}"
95
95
  stage = self.stages[stagename]
96
- out.append(" %s (%s)" % (stagename, stage['stagetype']))
97
- out.append(" Hardware Status: %s" % (hstat[stagename]))
98
- out.append(" Positioner Errors: %s" % (perrs[stagename]))
96
+ out.extend([f"# {stagename} ({stage['stagetype']})",
97
+ f" Hardware Status: {hstat[stagename]}",
98
+ f" Positioner Errors: {perrs[stagename]}"])
99
99
  return "\n".join(out)
100
100
 
101
+ def disconnect(self):
102
+ self.ftpconn.close()
103
+ if self._sid is not None:
104
+ self._xps.TCP_CloseSocket(self._sid)
105
+ self._sid = None
101
106
 
102
107
  def connect(self):
103
108
  self._sid = self._xps.TCP_ConnectToServer(self.host,
104
109
  self.port, self.timeout)
110
+
111
+ atexit.register(self.disconnect)
105
112
  try:
106
113
  self._xps.Login(self._sid, self.username, self.password)
107
114
  except:
108
- raise XPSException('Login failed for %s' % self.host)
115
+ raise XPSException(f'Login failed for {self.host}')
109
116
 
110
117
  err, val = self._xps.FirmwareVersionGet(self._sid)
111
118
  self.firmware_version = val
112
119
  self.ftphome = ''
113
-
114
- if any([m in self.firmware_version for m in ['XPS-D', 'HXP-D']]):
120
+ if any([m in self.firmware_version for m in ['XPS-D', 'HXP-D', 'XPS-RL']]):
115
121
  err, val = self._xps.Send(self._sid, 'InstallerVersionGet(char *)')
116
122
  self.firmware_version = val
117
123
  self.ftpconn = SFTPWrapper(**self.ftpargs)
@@ -119,19 +125,21 @@ class NewportXPS:
119
125
  self.ftpconn = FTPWrapper(**self.ftpargs)
120
126
  if 'XPS-C' in self.firmware_version:
121
127
  self.ftphome = '/Admin'
122
- try:
123
- self.read_systemini()
124
- except:
125
- print("Could not read system.ini!!!")
128
+ self.read_systemini()
129
+
130
+ def clean_folders(self):
131
+ if 'xps-d' in self.firmware_version.lower():
132
+ self._xps.CleanTmpFolder(self._sid)
133
+ self._xps.CleanCoreDumpFolder(self._sid)
126
134
 
127
135
 
128
136
  def check_error(self, err, msg='', with_raise=True):
129
137
  if err != 0:
130
- err = "%d" % err
138
+ err = f"{err}"
131
139
  desc = self._xps.errorcodes.get(err, 'unknown error')
132
- print("XPSError: message= %s, error=%s, description=%s" % (msg, err, desc))
140
+ print(f"XPSError: message={msg}, error={err}, description={desc}")
133
141
  if with_raise:
134
- raise XPSException("%s %s [Error %s]" % (msg, desc, err))
142
+ raise XPSException(f"{msg} {desc} [Error {err}]")
135
143
 
136
144
  def save_systemini(self, fname='system.ini'):
137
145
  """
@@ -169,10 +177,10 @@ class NewportXPS:
169
177
  self.stages= {}
170
178
  self.groups = {}
171
179
  sconf = ConfigParser()
172
- sconf.readfp(StringIO(initext))
180
+ sconf.read_string(initext)
173
181
 
174
182
  # read and populate lists of groups first
175
- for gtype, glist in sconf.items('GROUPS'): # ].items():
183
+ for gtype, glist in sconf.items('GROUPS'):
176
184
  if len(glist) > 0:
177
185
  for gname in glist.split(','):
178
186
  gname = gname.strip()
@@ -195,21 +203,19 @@ class NewportXPS:
195
203
 
196
204
  if len(pvtgroups) == 1:
197
205
  self.set_trajectory_group(pvtgroups[0])
198
-
199
206
  for sname in self.stages:
200
207
  ret = self._xps.PositionerMaximumVelocityAndAccelerationGet(self._sid, sname)
201
208
  try:
202
209
  self.stages[sname]['max_velo'] = ret[1]
203
- self.stages[sname]['max_accel'] = ret[2]/3.0
210
+ self.stages[sname]['max_accel'] = 0.75*ret[2]
204
211
  except:
205
- print("could not set max velo/accel for %s" % sname)
212
+ print(f"could not read max velo/accel for {sname}")
206
213
  ret = self._xps.PositionerUserTravelLimitsGet(self._sid, sname)
207
214
  try:
208
215
  self.stages[sname]['low_limit'] = ret[1]
209
216
  self.stages[sname]['high_limit'] = ret[2]
210
217
  except:
211
- print("could not set limits for %s" % sname)
212
-
218
+ print(f"could not read limits for {sname}")
213
219
  return self.groups
214
220
 
215
221
  def download_trajectory(self, filename):
@@ -342,27 +348,38 @@ class NewportXPS:
342
348
  gkp, gki, gkd, kform, ffgain
343
349
  """
344
350
  if stage not in self.stages:
345
- print("Stage '%s' not found: " % stage)
351
+ print(f"Stage '{stage}' not found: ")
346
352
  return
347
353
  params = self._xps.PositionerCorrectorPIDFFVelocityGet(self._sid, stage)
348
354
  if params[0] != 0 or len(params) != 13:
349
- print("error getting tuning parameters for %s" % stage)
355
+ print(f"error getting tuning parameters for {stage}")
350
356
  return
351
357
 
352
358
  params = params[1:]
353
359
  params[0] = closedloopstatus
354
- if kp is not None: params[1] = kp
355
- if ki is not None: params[2] = ki
356
- if kd is not None: params[3] = kd
357
- if ks is not None: params[4] = ks
358
- if inttime is not None: params[5] = inttime
359
- if dfilter is not None: params[6] = dfilter
360
- if gkp is not None: params[7] = gkp
361
- if gki is not None: params[8] = gki
362
- if gkd is not None: params[9] = gkd
363
- if kform is not None: params[10] = kform
364
- if ffgain is not None: params[11] = ffgain
365
- ret = self._xps.PositionerCorrectorPIDFFVelocitySet(self._sid, stage, *params)
360
+ if kp is not None:
361
+ params[1] = kp
362
+ if ki is not None:
363
+ params[2] = ki
364
+ if kd is not None:
365
+ params[3] = kd
366
+ if ks is not None:
367
+ params[4] = ks
368
+ if inttime is not None:
369
+ params[5] = inttime
370
+ if dfilter is not None:
371
+ params[6] = dfilter
372
+ if gkp is not None:
373
+ params[7] = gkp
374
+ if gki is not None:
375
+ params[8] = gki
376
+ if gkd is not None:
377
+ params[9] = gkd
378
+ if kform is not None:
379
+ params[10] = kform
380
+ if ffgain is not None:
381
+ params[11] = ffgain
382
+ self._xps.PositionerCorrectorPIDFFVelocitySet(self._sid, stage, *params)
366
383
 
367
384
  @withConnectedXPS
368
385
  def get_tuning(self, stage):
@@ -371,11 +388,11 @@ class NewportXPS:
371
388
  gkp, gki, gkd, kform, ffgain
372
389
  """
373
390
  if stage not in self.stages:
374
- print("Stage '%s' not found: " % stage)
391
+ print(f"Stage '{stage}' not found: ")
375
392
  return
376
393
  params = self._xps.PositionerCorrectorPIDFFVelocityGet(self._sid, stage)
377
394
  if params[0] != 0 or len(params) != 13:
378
- print("error getting tuning parameters for %s" % stage)
395
+ print(f"error getting tuning parameters for {stage}")
379
396
  return
380
397
 
381
398
  params = params[1:]
@@ -400,8 +417,8 @@ class NewportXPS:
400
417
  if group['category'].lower().startswith('multiple'):
401
418
  pvtgroups.append(gname)
402
419
  pvtgroups = ', '.join(pvtgroups)
403
- msg = "'%s' cannot be a trajectory group, must be one of %s"
404
- raise XPSException(msg % (group, pvtgroups))
420
+ msg = f"'{group}' cannot be a trajectory group, must be one of {pvtgroups}"
421
+ raise XPSException(msg)
405
422
 
406
423
  self.traj_group = group
407
424
  self.traj_positioners = self.groups[group]['positioners']
@@ -416,11 +433,11 @@ class NewportXPS:
416
433
  try:
417
434
  self.enable_group(self.traj_group)
418
435
  except XPSException:
419
- print("Warning: could not enable trajectory group '%s'"% self.traj_group)
436
+ print(f"Warning: could not enable trajectory group '{self.traj_group}'")
420
437
  return
421
438
 
422
- for i in range(64):
423
- self._xps.EventExtendedRemove(self._sid, i)
439
+ #for i in range(64):
440
+ # self._xps.EventExtendedRemove(self._sid, i)
424
441
 
425
442
  # build template for linear trajectory file:
426
443
  trajline1 = ['%(ramptime)f']
@@ -448,14 +465,14 @@ class NewportXPS:
448
465
  if group is None:
449
466
  for group in self.groups:
450
467
  err, ret = method(self._sid, group)
451
- self.check_error(err, msg="%s group '%s'" % (action, group),
468
+ self.check_error(err, msg=f"{action} group '{group}'",
452
469
  with_raise=with_raise)
453
470
  elif group in self.groups:
454
471
  err, ret = method(self._sid, group)
455
- self.check_error(err, msg="%s group '%s'" % (action, group),
472
+ self.check_error(err, msg=f"%{action} group '{group}'",
456
473
  with_raise=with_raise)
457
474
  else:
458
- raise ValueError("Group '%s' not found" % group)
475
+ raise ValueError("Group '{group}' not found")
459
476
 
460
477
  def kill_group(self, group=None):
461
478
  """
@@ -478,8 +495,8 @@ class NewportXPS:
478
495
  self.initialize_group(group=g)
479
496
  except XPSException:
480
497
  print(f"Warning: could not initialize '{g}' (already initialized?)")
481
-
482
-
498
+
499
+
483
500
  def home_allgroups(self, with_encoder=True, home=False):
484
501
  """
485
502
  home all groups
@@ -549,10 +566,10 @@ class NewportXPS:
549
566
  out = {}
550
567
  for group in self.groups:
551
568
  err, stat = self._xps.GroupStatusGet(self._sid, group)
552
- self.check_error(err, msg="GroupStatus '%s'" % (group))
569
+ self.check_error(err, msg=f"GroupStatus '{group}'")
553
570
 
554
571
  err, val = self._xps.GroupStatusStringGet(self._sid, stat)
555
- self.check_error(err, msg="GroupStatusString '%s'" % (stat))
572
+ self.check_error(err, msg=f"GroupStatusString '{stat}'")
556
573
 
557
574
  out[group] = val
558
575
  return out
@@ -564,12 +581,13 @@ class NewportXPS:
564
581
  """
565
582
  out = {}
566
583
  for stage in self.stages:
567
- if stage in ('', None): continue
584
+ if stage in ('', None):
585
+ continue
568
586
  err, stat = self._xps.PositionerHardwareStatusGet(self._sid, stage)
569
- self.check_error(err, msg="Pos HardwareStatus '%s'" % (stage))
587
+ self.check_error(err, msg=f"Pos HardwareStatus '{stage}'")
570
588
 
571
589
  err, val = self._xps.PositionerHardwareStatusStringGet(self._sid, stat)
572
- self.check_error(err, msg="Pos HardwareStatusString '%s'" % (stat))
590
+ self.check_error(err, msg=f"Pos HardwareStatusString '{stat}'")
573
591
  out[stage] = val
574
592
  return out
575
593
 
@@ -580,12 +598,13 @@ class NewportXPS:
580
598
  """
581
599
  out = {}
582
600
  for stage in self.stages:
583
- if stage in ('', None): continue
601
+ if stage in ('', None):
602
+ continue
584
603
  err, stat = self._xps.PositionerErrorGet(self._sid, stage)
585
- self.check_error(err, msg="Pos Error '%s'" % (stage))
604
+ self.check_error(err, msg=f"Pos Error '{stage}'")
586
605
 
587
606
  err, val = self._xps.PositionerErrorStringGet(self._sid, stat)
588
- self.check_error(err, msg="Pos ErrorString '%s'" % (stat))
607
+ self.check_error(err, msg=f"Pos ErrorString '{stat}'")
589
608
 
590
609
  if len(val) < 1:
591
610
  val = 'OK'
@@ -599,8 +618,8 @@ class NewportXPS:
599
618
  set velocity for stage
600
619
  """
601
620
  if stage not in self.stages:
602
- print("Stage '%s' not found" % stage)
603
- return
621
+ raise XPSException(f"Stage '{stage}' not found")
622
+
604
623
  ret, v_cur, a_cur, jt0_cur, jt1_cur = \
605
624
  self._xps.PositionerSGammaParametersGet(self._sid, stage)
606
625
  if accl is None:
@@ -621,8 +640,6 @@ class NewportXPS:
621
640
  print("Do have a group to move")
622
641
  return
623
642
  ret = self._xps.GroupMoveAbort(self._sid, group)
624
- print('abort group ', group, ret)
625
-
626
643
 
627
644
  @withConnectedXPS
628
645
  def move_group(self, group=None, **kws):
@@ -631,7 +648,7 @@ class NewportXPS:
631
648
  if group is None or group not in self.groups:
632
649
  group = self.traj_group
633
650
  if group is None:
634
- print("Do have a group to move")
651
+ print("no group to move")
635
652
  return
636
653
  posnames = [p.lower() for p in self.groups[group]['positioners']]
637
654
  ret = self._xps.GroupPositionCurrentGet(self._sid, group, len(posnames))
@@ -671,15 +688,14 @@ class NewportXPS:
671
688
  relative (bool): whether move is relative [False]
672
689
  """
673
690
  if stage not in self.stages:
674
- print("Stage '%s' not found" % stage)
675
- return
691
+ raise XPSException(f"Stage '{stage}' not found")
676
692
 
677
693
  move = self._xps.GroupMoveAbsolute
678
694
  if relative:
679
695
  move = self._xps.GroupMoveRelative
680
696
 
681
697
  err, ret = move(self._sid, stage, [value])
682
- self.check_error(err, msg="Moving stage '%s'" % (stage))
698
+ self.check_error(err, msg=f"Moving stage '{stage}'")
683
699
  return ret
684
700
 
685
701
  @withConnectedXPS
@@ -691,11 +707,10 @@ class NewportXPS:
691
707
  stage (string): name of stage -- must be in self.stages
692
708
  """
693
709
  if stage not in self.stages:
694
- print("Stage '%s' not found: " % stage)
695
- return
710
+ raise XPSException(f"Stage '{stage}' not found")
696
711
 
697
712
  err, val = self._xps.GroupPositionCurrentGet(self._sid, stage, 1)
698
- self.check_error(err, msg="Get Stage Position '%s'" % (stage))
713
+ self.check_error(err, msg=f"Get Stage Position '{stage}'")
699
714
  return val
700
715
 
701
716
  read_stage_position = get_stage_position
@@ -735,27 +750,25 @@ class NewportXPS:
735
750
 
736
751
 
737
752
  @withConnectedXPS
738
- def define_line_trajectories(self, axis, group=None,
739
- start=0, stop=1, step=0.001, scantime=10.0,
753
+ def define_line_trajectories(self, axis, group=None, pixeltime=None,
754
+ scantime=None, start=0, stop=1, step=0.01,
740
755
  accel=None, upload=True, verbose=False):
741
756
  """defines 'forward' and 'backward' trajectories for a simple
742
- single element line scan in PVT Mode
757
+ single element line scan using PVT Mode
743
758
  """
744
759
  if group is not None:
745
760
  self.set_trajectory_group(group)
746
761
 
747
762
  if self.traj_group is None:
748
- print("Must define a trajectory group first!")
749
- return
763
+ raise XPSException("No trajectory group defined")
750
764
 
751
765
  for axname in (axis, axis.upper(), axis.lower(), axis.title()):
752
- stage = "%s.%s" % (self.traj_group, axname)
766
+ stage = f"{self.traj_group}.{axname}"
753
767
  if stage in self.stages:
754
768
  break
755
769
 
756
- # print(" Stage ", stage, self.stages[stage])
757
- max_velo = 0.75*self.stages[stage]['max_velo']
758
- max_accel = 0.5*self.stages[stage]['max_accel']
770
+ max_velo = self.stages[stage]['max_velo']
771
+ max_accel = self.stages[stage]['max_accel']
759
772
 
760
773
  if accel is None:
761
774
  accel = max_accel
@@ -767,19 +780,25 @@ class NewportXPS:
767
780
  step = scandir*abs(step)
768
781
 
769
782
  npulses = int((abs(stop - start) + abs(step)*1.1) / abs(step))
770
- scantime = float(abs(scantime))
771
- pixeltime= scantime / (npulses-1)
772
- scantime = pixeltime*npulses
783
+ if pixeltime is None:
784
+ if scantime is None:
785
+ raise ValueError("line trajectory must set pixeltime or scantime")
786
+ else:
787
+ pixeltime = float(abs(scantime))/(npulses-1)
788
+ scantime = float(abs(pixeltime))*npulses
773
789
 
774
790
  distance = (abs(stop - start) + abs(step))*1.0
775
791
  velocity = min(distance/scantime, max_velo)
776
-
792
+ if verbose:
793
+ print(f"trajecory: {scantime=:.4f}, {pixeltime=:.4f}, {npulses=}, {start=:.4f}, {stop=:.4f}, {step=:.4f}")
777
794
  ramptime = max(2.e-5, abs(velocity/accel))
778
- rampdist = velocity*ramptime
779
- offset = step/2.0 + scandir*rampdist
795
+ rampdist = 0.5*velocity*ramptime
796
+ offset = 0.5*step + scandir*rampdist
780
797
 
781
- trajbase = {'axes': [axis], 'pixeltime': pixeltime,
782
- 'npulses': npulses, 'nsegments': 3}
798
+ trajbase = {'axes': [axis],
799
+ 'type': 'line',
800
+ 'pixeltime': pixeltime, 'uploaded': False,
801
+ 'npulses': npulses+1, 'nsegments': 3}
783
802
 
784
803
  self.trajectories['foreward'] = {'start': [start-offset],
785
804
  'stop': [stop +offset]}
@@ -797,21 +816,19 @@ class NewportXPS:
797
816
  val = 0.0
798
817
  if ax == axis:
799
818
  val = base[attr]
800
- fore["%s_%s" % (ax, attr)] = val
819
+ fore[f"{ax}_{attr}"] = val
801
820
 
802
821
  back = fore.copy()
803
- back["%s_start" % axis] = fore["%s_stop" % axis]
804
- back["%s_stop" % axis] = fore["%s_start" % axis]
822
+ back[f"{axis}_start"] = fore[f"{axis}_stop"]
823
+ back[f"{axis}_stop"] = fore[f"{axis}_start"]
805
824
  for attr in ('velo', 'ramp', 'dist'):
806
- back["%s_%s" % (axis, attr)] *= -1.0
825
+ back[f"{axis}_{attr}"] *= -1.0
807
826
 
808
827
  if verbose:
809
- print("TRAJ Text Fore, Back:")
810
828
  print(self.linear_template % fore)
811
829
  print(self.linear_template % back)
812
830
 
813
831
  ret = True
814
-
815
832
  if upload:
816
833
  ret = False
817
834
  try:
@@ -819,36 +836,219 @@ class NewportXPS:
819
836
  self.linear_template % fore)
820
837
  self.upload_trajectory('backward.trj',
821
838
  self.linear_template % back)
839
+ self.trajectories['foreward']['uploaded'] = True
840
+ self.trajectories['backward']['uploaded'] = True
841
+ self.trajectories['foreward']['text'] = self.linear_template % fore
842
+ self.trajectories['backward']['text'] = self.linear_template % back
822
843
  ret = True
823
844
  except:
824
845
  raise ValueError("error uploading trajectory")
825
846
  return ret
826
847
 
827
848
  @withConnectedXPS
828
- def arm_trajectory(self, name, verbose=False):
849
+ def define_array_trajectory(self, positions, dtime=1.0, max_accels=None,
850
+ upload=True, name='array', verbose=True):
851
+ """define a PVT trajectory for the trajectory group from a dictionary of
852
+ position arrays for each positioner in the trajectory group.
853
+
854
+ Positioners that are not included in the positions dict will not be moved.
855
+
856
+ Arguments
857
+ ---------
858
+ positions: dict of {PosName: np.ndarray} for each positioner to move
859
+ dtime: float, time per segment
860
+ max_accels: dict of {PosName: max_acceleration} to use.
861
+ name: name of trajectory (file will be f"{name}.trj")
862
+ upload: bool, whether to upload trajectory
863
+
864
+ Returns:
865
+ -------
866
+ dict with information about trajcetory. This will include values for
867
+ pvt_buff: full text of trajectory buffer
868
+ type: 'array'
869
+ start: dict of starting values (backed up from first point so that
870
+ positioner can be accelerated to the desired velocity for the
871
+ second trajectory segment)
872
+ npulses: number of expected pulses.
873
+
874
+ Notes:
875
+ ------
876
+ 1. The np.ndarray for each positioner must be the same length, and will be
877
+ used as midpoints between trigger events.
878
+ 2. For ndarrays of lenght N, the trajectory will have N+1 segments: one
879
+ to ramp up to velocity to approach the first point, and the last to
880
+ decelerate to zero velocity.
881
+
882
+ """
883
+ tgroup = self.traj_group
884
+ if tgroup is None:
885
+ raise XPSException("No trajectory group defined")
886
+
887
+ all_axes = [a for a in self.groups[tgroup]['positioners']]
888
+
889
+ pdat = {}
890
+ input_ok = True
891
+ npts = None
892
+ for key, value in positions.items():
893
+ pname = key[:]
894
+
895
+ if key.startswith(tgroup):
896
+ pname = key[len(tgroup)+1:]
897
+ if pname not in all_axes:
898
+ print(f"Unknown positioner given: {pname}")
899
+ input_ok = False
900
+ if npts is None:
901
+ npts = len(value)
902
+ elif npts != len(value):
903
+ print(f"incorrect array length for {pname}")
904
+ input_ok = False
905
+
906
+ if isinstance(value, np.ndarray):
907
+ positions[key] = value.astype(np.float64).tolist()
908
+ else:
909
+ positions[key] = [float(x) for x in value]
910
+
911
+ if not input_ok:
912
+ return
913
+
914
+ npulses = npts+1
915
+ dtime = float(abs(dtime))
916
+ times = np.ones(npulses+1)*dtime
917
+
918
+ if max_accels is None:
919
+ max_accels = {}
920
+ pos, dpos = {}, {}
921
+ velo = {}
922
+ accel = {}
923
+ start = {}
924
+ for axes in all_axes:
925
+ stage = f'{tgroup}.{axes}'
926
+ maxv = self.stages[stage]['max_velo']
927
+ maxa = self.stages[stage]['max_accel']
928
+ if axes in max_accels:
929
+ maxa = min(max_accels[axes], maxa)
930
+ if axes in positions:
931
+ upos = positions[axes]
932
+ # mid are the trajectory trigger points, the
933
+ # mid points between the desired positions
934
+ mid = [3*upos[0]-2*upos[1], 2*upos[0] - upos[1]]
935
+ mid.extend(upos)
936
+ mid.extend([2*upos[-1]-upos[-2],
937
+ 3*upos[-1]-2*upos[-2],
938
+ ])
939
+ mid = np.array(mid)
940
+ pos[axes] = 0.5*(mid[1:] + mid[:-1])
941
+
942
+ # adjust first segment velocity to half max accel
943
+ p0, p1, p2 = pos[axes][0], pos[axes][1], pos[axes][2]
944
+ v0 = (p1-p0)/dtime
945
+ v1 = (p2-p1)/dtime
946
+ a0 = (v1-v0)/dtime
947
+ start[axes] = p1 - (p1-p0)*dtime*max(v0, 0.5*maxv)/max(a0, 0.5*maxa)
948
+
949
+ pos[axes] = np.diff(pos[axes] - start[axes])
950
+ velo[axes] = np.gradient(pos[axes])/times
951
+ velo[axes][-1] = 0.0
952
+ accel[axes] = np.gradient(velo[axes])/times
953
+
954
+ if (max(abs(velo[axes])) > maxv):
955
+ errmsg = f"max velocity {maxv} violated for {axes}"
956
+ raise ValueError(errmsg)
957
+ if (max(abs(accel[axes])) > maxa):
958
+ errmsg = f"max acceleration {maxa} violated for {axes}"
959
+ raise ValueError(errmsg)
960
+ else:
961
+ start[axes] = None
962
+ pos[axes] = np.zeros(npulses+1, dtype=np.float64)
963
+ velo[axes] = np.zeros(npulses+1, dtype=np.float64)
964
+ accel[axes] = np.zeros(npulses+1, dtype=np.float64)
965
+
966
+
967
+ traj = {'axes': all_axes,
968
+ 'type': 'array',
969
+ 'start': start, 'pixeltime': dtime,
970
+ 'npulses': npulses+1, 'nsegments': npulses+1,
971
+ 'uploaded': False}
972
+
973
+ buff = ['']
974
+
975
+ for n in range(npulses+1):
976
+ line = [f"{dtime:.8f}"]
977
+ for axes in all_axes:
978
+ p, v = pos[axes][n], velo[axes][n]
979
+ line.extend([f"{p:.8f}", f"{v:.8f}"])
980
+ buff.append(', '.join(line))
981
+ buff.append('')
982
+ buff = '\n'.join(buff)
983
+ traj['pvt_buffer'] = buff
984
+
985
+ if upload:
986
+ tfile = f"{name}.trj"
987
+ traj['name'] = name
988
+ try:
989
+ self.upload_trajectory(tfile, buff)
990
+ traj['uploaded'] = True
991
+ except:
992
+ traj['uploaded'] = False
993
+ self.trajectories[name] = traj
994
+ return traj
995
+
996
+
997
+ @withConnectedXPS
998
+ def move_to_trajectory_start(self, name):
999
+ """
1000
+ move to the start position of a named trajectory
1001
+ """
1002
+ tgroup = self.traj_group
1003
+ if tgroup is None:
1004
+ raise XPSException("No trajectory group defined")
1005
+
1006
+ traj = self.trajectories.get(name, None)
1007
+ if traj is None:
1008
+ raise XPSException(f"Cannot find trajectory named '{name}'")
1009
+
1010
+ if traj['type'] == 'line':
1011
+ for pos, axes in zip(traj['start'], traj['axes']):
1012
+ self.move_stage(f'{tgroup}.{axes}', pos)
1013
+
1014
+ elif traj['type'] == 'array':
1015
+ for axes, pos in traj['start'].items():
1016
+ if pos is not None:
1017
+ self.move_stage(f'{tgroup}.{axes}', pos)
1018
+
1019
+
1020
+ @withConnectedXPS
1021
+ def arm_trajectory(self, name, verbose=False, move_to_start=True):
829
1022
  """
830
- set up the trajectory from previously defined, uploaded trajectory
1023
+ prepare to run a named (assumed uploaded) trajectory
831
1024
  """
832
1025
  if self.traj_group is None:
833
1026
  print("Must set group name!")
834
1027
 
835
1028
  traj = self.trajectories.get(name, None)
836
1029
  if traj is None:
837
- raise XPSException("Cannot find trajectory named '%s'" % name)
1030
+ raise XPSException(f"Cannot find trajectory '{name}'")
1031
+
1032
+ if not traj['uploaded']:
1033
+ raise XPSException(f"trajectory '{name}' has not been uploaded")
1034
+
838
1035
 
839
1036
  self.traj_state = ARMING
840
- self.traj_file = '%s.trj' % name
1037
+ self.traj_file = f'{name}.trj'
841
1038
 
1039
+ if move_to_start:
1040
+ self.move_to_trajectory_start(name)
842
1041
  # move_kws = {}
843
1042
  outputs = []
844
1043
  for out in self.gather_outputs:
845
1044
  for i, ax in enumerate(traj['axes']):
846
- outputs.append('%s.%s.%s' % (self.traj_group, ax, out))
1045
+ outputs.append(f'{self.traj_group}.{ax}.{out}')
847
1046
 
848
- end_segment = traj['nsegments'] # - 1 + self.extra_triggers
1047
+ end_segment = traj['nsegments']
849
1048
  self.nsegments = end_segment
850
1049
 
851
- self.gather_titles = "%s\n#%s\n" % (self.gather_header, " ".join(outputs))
1050
+ o = " ".join(outputs)
1051
+ self.gather_titles = f"{self.gather_header}\n#{o}\n"
852
1052
  err, ret = self._xps.GatheringReset(self._sid)
853
1053
  self.check_error(err, msg="GatheringReset")
854
1054
  if verbose:
@@ -859,33 +1059,33 @@ class NewportXPS:
859
1059
 
860
1060
  if verbose:
861
1061
  print(" GatheringConfigurationSet outputs ", outputs)
862
- print(" GatheringConfigurationSet returned ", ret)
1062
+ print(" GatheringConfigurationSet returned ", ret, time.ctime())
863
1063
  print(" segments, pixeltime" , end_segment, traj['pixeltime'])
864
1064
 
865
1065
  err, ret = self._xps.MultipleAxesPVTPulseOutputSet(self._sid, self.traj_group,
866
1066
  2, end_segment,
867
1067
  traj['pixeltime'])
868
- self.check_error(err, msg="PVTPulseOutputSet", with_raise=False)
1068
+ self.check_error(err, msg="PVTPulseOutputSet", with_raise=True)
869
1069
  if verbose:
870
- print(" PVTPulse ", ret)
1070
+ print(" PVTPulse ", ret, time.ctime())
871
1071
  err, ret = self._xps.MultipleAxesPVTVerification(self._sid,
872
1072
  self.traj_group,
873
1073
  self.traj_file)
874
1074
 
875
- self.check_error(err, msg="PVTVerification", with_raise=False)
1075
+ self.check_error(err, msg="PVTVerification", with_raise=True)
876
1076
  if verbose:
877
- print(" PVTVerify ", ret)
1077
+ print(" PVTVerify ", ret, time.ctime())
878
1078
  self.traj_state = ARMED
879
1079
 
880
1080
  @withConnectedXPS
881
1081
  def run_trajectory(self, name=None, save=True, clean=False,
882
- output_file='Gather.dat', verbose=False):
1082
+ output_file='Gather.dat', verbose=False, move_to_start=True):
883
1083
 
884
1084
  """run a trajectory in PVT mode
885
1085
 
886
1086
  The trajectory *must be in the ARMED state
887
1087
  """
888
-
1088
+ dt = debugtime()
889
1089
  if 'xps-d' in self.firmware_version.lower():
890
1090
  self._xps.CleanTmpFolder(self._sid)
891
1091
 
@@ -893,47 +1093,60 @@ class NewportXPS:
893
1093
  self._xps.CleanCoreDumpFolder(self._sid)
894
1094
 
895
1095
  if name in self.trajectories and self.traj_state != ARMED:
896
- self.arm_trajectory(name, verbose=verbose)
1096
+ self.arm_trajectory(name, verbose=verbose, move_to_start=move_to_start)
897
1097
 
898
1098
  if self.traj_state != ARMED:
899
1099
  raise XPSException("Must arm trajectory before running!")
900
1100
 
901
- buffer = ('Always', '%s.PVT.TrajectoryPulse' % self.traj_group,)
1101
+ dt.add('armed')
1102
+ tgroup = self.traj_group
1103
+ buffer = ('Always', f'{tgroup}.PVT.TrajectoryPulse',)
902
1104
  err, ret = self._xps.EventExtendedConfigurationTriggerSet(self._sid, buffer,
903
1105
  ('0','0'), ('0','0'),
904
1106
  ('0','0'), ('0','0'))
905
1107
  self.check_error(err, msg="EventConfigTrigger")
906
1108
  if verbose:
907
1109
  print( " EventExtended Trigger Set ", ret)
908
-
1110
+ dt.add('event trigger set')
909
1111
  err, ret = self._xps.EventExtendedConfigurationActionSet(self._sid,
910
1112
  ('GatheringOneData',),
911
1113
  ('',), ('',),('',),('',))
912
1114
  self.check_error(err, msg="EventConfigAction")
913
1115
  if verbose:
914
1116
  print( " EventExtended Action Set ", ret)
915
-
1117
+ dt.add('event action set')
916
1118
  eventID, m = self._xps.EventExtendedStart(self._sid)
917
1119
  self.traj_state = RUNNING
918
1120
 
919
1121
  if verbose:
920
1122
  print( " EventExtended ExtendedStart ", eventID, m)
921
-
1123
+ dt.add('event start')
922
1124
  err, ret = self._xps.MultipleAxesPVTExecution(self._sid,
923
1125
  self.traj_group,
924
1126
  self.traj_file, 1)
925
1127
  self.check_error(err, msg="PVT Execute", with_raise=False)
926
1128
  if verbose:
927
- print( " PVT Execute ", ret)
928
-
1129
+ print( " PVT Execute done ", ret)
1130
+ dt.add('pvt execute')
929
1131
  ret = self._xps.EventExtendedRemove(self._sid, eventID)
930
1132
  ret = self._xps.GatheringStop(self._sid)
931
-
1133
+ dt.add('gathering stop')
932
1134
  self.traj_state = COMPLETE
933
1135
  npulses = 0
934
1136
  if save:
935
- self.read_and_save(output_file, verbose=verbose)
1137
+ npulses, buff = self.read_gathering(set_idle_when_done=False,
1138
+ verbose=verbose)
1139
+ dt.add('read gathering')
1140
+ if npulses > 0:
1141
+ self.save_gathering_file(output_file, buff,
1142
+ verbose=verbose,
1143
+ set_idle_when_done=False)
1144
+ dt.add('saved gathering')
1145
+ self.ngathered = npulses
1146
+ # self.read_and_save(output_file, verbose=verbose)
936
1147
  self.traj_state = IDLE
1148
+ if verbose:
1149
+ dt.show()
937
1150
  return npulses
938
1151
 
939
1152
  @withConnectedXPS
@@ -965,16 +1178,18 @@ class NewportXPS:
965
1178
  t0 = time.time()
966
1179
  while npulses < 1:
967
1180
  try:
968
- ret, npulses, nx = self._xps.GatheringCurrentNumberGet(self._sid)
1181
+ gdat = self._xps.GatheringCurrentNumberGet(self._sid)
969
1182
  except SyntaxError:
970
1183
  print("#XPS Gathering Read failed, will try again")
971
- pass
1184
+ if len(gdat) == 3:
1185
+ ret, npulses, nx = gdat
1186
+ if npulses < 1 or ret != 0:
1187
+ time.sleep(0.1)
1188
+
972
1189
  if time.time()-t0 > 5:
973
1190
  print("Failed to get gathering size after 5 seconds: return 0 points")
974
1191
  print("Gather Returned: ", ret, npulses, nx, self._xps, time.ctime())
975
1192
  return (0, ' \n')
976
- if npulses < 1 or ret != 0:
977
- time.sleep(0.05)
978
1193
  dt.add("gather num %d npulses=%d (%d)" % (ret, npulses, self.nsegments))
979
1194
  counter = 0
980
1195
  while npulses < 1 and counter < 5:
@@ -986,7 +1201,7 @@ class NewportXPS:
986
1201
  try:
987
1202
  ret, buff = self._xps.GatheringDataMultipleLinesGet(self._sid, 0, npulses)
988
1203
  except ValueError:
989
- print("Failed to read gathering: ", ret, buff)
1204
+ print("Failed to read gathering: ", ret)
990
1205
  return (0, ' \n')
991
1206
  dt.add("gather after multilinesget %d" % ret)
992
1207
  nchunks = -1
@@ -1032,7 +1247,7 @@ class NewportXPS:
1032
1247
  f.close()
1033
1248
  nlines = len(buff.split('\n')) - 1
1034
1249
  if verbose:
1035
- print('Wrote %i lines, %i bytes to %s' % (nlines, len(buff), fname))
1250
+ print(f'Wrote {nlines} lines, {len(buff)} bytes to {fname}')
1036
1251
  if set_idle_when_done:
1037
1252
  self.traj_state = IDLE
1038
1253
 
@@ -1063,7 +1278,7 @@ class NewportXPS:
1063
1278
  if accel_values is None:
1064
1279
  accel_values = []
1065
1280
  for posname in self.traj_positioners:
1066
- accel = self.stages["%s.%s"%(self.traj_group, posname)]['max_accel']
1281
+ accel = self.stages[f"{self.traj_group}.{posname}"]['max_accel']
1067
1282
  accel_values.append(accel)
1068
1283
  accel_values = np.array(accel_values)
1069
1284
 
@@ -1116,11 +1331,8 @@ class NewportXPS:
1116
1331
  for ind, positioner in enumerate(self.traj_positioners):
1117
1332
  self.trajectories[name][positioner + 'ramp'] = ramp[ind]
1118
1333
 
1119
- ret = False
1120
1334
  try:
1121
1335
  self.upload_trajectory(name + '.trj', trajectory_str)
1122
- ret = True
1123
- # print('Trajectory File uploaded.')
1124
1336
  except:
1125
1337
  print('Failed to upload trajectory file')
1126
1338
 
@@ -1131,10 +1343,9 @@ class NewportXPS:
1131
1343
  """run trajectory in PVT mode"""
1132
1344
  traj = self.trajectories.get(name, None)
1133
1345
  if traj is None:
1134
- print('Cannot find trajectory named %s' % name)
1135
- return
1346
+ raise XPSException(f'Cannot find trajectory named {name}')
1136
1347
 
1137
- traj_file = '%s.trj' % name
1348
+ traj_file = f'{name}.trj'
1138
1349
  dtime = traj['pulse_time']
1139
1350
  ramps = []
1140
1351
  for positioner in self.traj_positioners:
@@ -1148,51 +1359,42 @@ class NewportXPS:
1148
1359
 
1149
1360
  self._xps.GroupMoveRelative(self._sid, self.traj_group, ramps)
1150
1361
 
1151
- # self.gather_outputs = []
1152
- ## gather_titles = []
1153
-
1154
- # for positioner in self.traj_positioners:
1155
- # for out in self.gather_outputs:
1156
- # self.gather_outputs.append('%s.%s.%s' % (self.traj_group, positioner, out))
1157
- # gather_titles.append('%s.%s' % (positioner, out))
1158
- ## self.gather_titles = "%s\n#%s\n" % (xps_config['GATHER TITLES'],
1159
- ## " ".join(gather_titles))
1160
-
1161
1362
  outputs = []
1162
1363
  for out in self.gather_outputs:
1163
- for i, ax in enumerate(traj['axes']):
1164
- outputs.append('%s.%s.%s' % (self.traj_group, ax, out))
1165
- # move_kws[ax] = float(traj['start'][i])
1166
-
1167
- end_segment = traj['nsegments'] - 1 + self.extra_triggers
1168
- # self.move_group(self.traj_group, **move_kws)
1169
- self.gather_titles = "%s\n#%s\n" % (self.gather_header, " ".join(outputs))
1364
+ for ax in traj['axes']:
1365
+ outputs.append(f'{self.traj_group}.{ax}.{out}')
1170
1366
 
1367
+ o = " ".join(outputs)
1368
+ self.gather_titles = f"{self.gather_header}\n#{o}\n"
1171
1369
  self._xps.GatheringReset(self._sid)
1172
1370
  self._xps.GatheringConfigurationSet(self._sid, self.gather_outputs)
1173
1371
 
1174
- # print("step_number", step_number)
1175
- ret = self._xps.MultipleAxesPVTPulseOutputSet(self._sid, self.traj_group,
1176
- 2, step_number + 1, dtime)
1177
- ret = self._xps.MultipleAxesPVTVerification(self._sid, self.traj_group, traj_file)
1372
+ err, ret = self._xps.MultipleAxesPVTPulseOutputSet(self._sid, self.traj_group,
1373
+ 2, step_number + 1, dtime)
1374
+ self.check_error(err, msg="MultipleAxesPVTPulseOutputSet", with_raise=False)
1375
+
1376
+ err, _ = self._xps.MultipleAxesPVTVerification(self._sid, self.traj_group, traj_file)
1377
+ self.check_error(err, msg="MultipleAxesPVTVerification", with_raise=False)
1178
1378
 
1179
1379
  buffer = ('Always', self.traj_group + '.PVT.TrajectoryPulse')
1180
- o = self._xps.EventExtendedConfigurationTriggerSet(self._sid, buffer,
1181
- ('0', '0'), ('0', '0'),
1182
- ('0', '0'), ('0', '0'))
1380
+ err, ret = self._xps.EventExtendedConfigurationTriggerSet(self._sid, buffer,
1381
+ ('0', '0'), ('0', '0'),
1382
+ ('0', '0'), ('0', '0'))
1383
+ self.check_error(err, msg="EventExtendedConfigurationTriggerSet", with_raise=False)
1183
1384
 
1184
- o = self._xps.EventExtendedConfigurationActionSet(self._sid, ('GatheringOneData',),
1185
- ('',), ('',), ('',), ('',))
1385
+ err, ret = self._xps.EventExtendedConfigurationActionSet(self._sid, ('GatheringOneData',),
1386
+ ('',), ('',), ('',), ('',))
1387
+ self.check_error(err, msg="EventExtendedConfigurationActionSet", with_raise=False)
1186
1388
 
1187
- eventID, m = self._xps.EventExtendedStart(self._sid)
1389
+ eventID, _ = self._xps.EventExtendedStart(self._sid)
1188
1390
 
1189
- ret = self._xps.MultipleAxesPVTExecution(self._sid, self.traj_group, traj_file, 1)
1190
- o = self._xps.EventExtendedRemove(self._sid, eventID)
1191
- o = self._xps.GatheringStop(self._sid)
1391
+ self._xps.MultipleAxesPVTExecution(self._sid, self.traj_group, traj_file, 1)
1392
+ self._xps.EventExtendedRemove(self._sid, eventID)
1393
+ self._xps.GatheringStop(self._sid)
1192
1394
 
1193
1395
  npulses = 0
1194
1396
  if save:
1195
- npulses, outbuff = self.read_and_save(outfile)
1397
+ npulses, _ = self.read_and_save(outfile)
1196
1398
 
1197
1399
  self._xps.GroupMoveRelative(self._sid, self.traj_group, ramps)
1198
1400
  return npulses
@@ -1200,7 +1402,6 @@ class NewportXPS:
1200
1402
 
1201
1403
 
1202
1404
  if __name__ == '__main__':
1203
- import sys
1204
1405
  ipaddr = sys.argv[1]
1205
1406
  x = NewportXPS(ipaddr)
1206
1407
  x.read_systemini()