@@ -658,11 +658,22 @@ def __init__(self, name, parent_device, **kwargs):
658
658
raise LabscriptError ('Error instantiating device %s. The parent (%s) must be an instance of ClockLine.' % (name , parent_device_name ))
659
659
Device .__init__ (self , name , parent_device , 'internal' , ** kwargs ) # This 'internal' should perhaps be more descriptive?
660
660
661
+ @property
662
+ def minimum_clock_high_time (self ):
663
+ if getattr (self , "clock_limit" , None ) is None :
664
+ return 0
665
+
666
+ # Convert clock limit to minimum pulse period and then divide by 2 to
667
+ # get minimum half period. This is the fastest assuming the minimum high
668
+ # time corresponds to half the fastest clock pulse supported.
669
+ return 1 / self .clock_limit / 2
670
+
661
671
662
672
class ClockLine (Device ):
663
673
description = 'Generic ClockLine'
664
674
allowed_children = [IntermediateDevice ]
665
675
_clock_limit = None
676
+ _minimum_clock_high_time = 0
666
677
667
678
@set_passed_properties (property_names = {})
668
679
def __init__ (self , name , pseudoclock , connection , ramping_allowed = True , ** kwargs ):
@@ -675,6 +686,10 @@ def add_device(self, device):
675
686
Device .add_device (self , device )
676
687
if getattr (device , 'clock_limit' , None ) is not None and (self ._clock_limit is None or device .clock_limit < self .clock_limit ):
677
688
self ._clock_limit = device .clock_limit
689
+ if getattr (device , 'minimum_clock_high_time' , None ) is not None :
690
+ self ._minimum_clock_high_time = max (
691
+ device .minimum_clock_high_time , self ._minimum_clock_high_time
692
+ )
678
693
679
694
# define a property to make sure no children overwrite this value themselves
680
695
# The calculation of maximum clock_limit should be done by the add_device method above
@@ -689,6 +704,10 @@ def clock_limit(self):
689
704
return self .parent_device .clock_limit
690
705
return self ._clock_limit
691
706
707
+ @property
708
+ def minimum_clock_high_time (self ):
709
+ return self ._minimum_clock_high_time
710
+
692
711
693
712
class Pseudoclock (Device ):
694
713
"""Parent class of all pseudoclocks.
@@ -757,113 +776,147 @@ def collect_change_times(self, all_outputs, outputs_by_clockline):
757
776
change_times [clock_line ].extend (output_change_times )
758
777
all_change_times .extend (output_change_times )
759
778
ramps_by_clockline [clock_line ].extend (output .get_ramp_times ())
760
-
761
- # print 'initial_change_times for %s: %s'%(clock_line.name,change_times[clock_line])
762
-
779
+
763
780
# Change to a set and back to get rid of duplicates:
764
781
if not all_change_times :
765
782
all_change_times .append (0 )
766
783
all_change_times .append (self .parent_device .stop_time )
767
- # include trigger times in change_times, so that pseudoclocks always have an instruction immediately following a wait:
784
+ # include trigger times in change_times, so that pseudoclocks always
785
+ # have an instruction immediately following a wait:
768
786
all_change_times .extend (self .parent_device .trigger_times )
769
-
770
- ####################################################################################################
771
- # Find out whether any other clockline has a change time during a ramp on another clockline. #
772
- # If it does, we need to let the ramping clockline know it needs to break it's loop at that time #
773
- ####################################################################################################
787
+
788
+ ########################################################################
789
+ # Find out whether any other clockline has a change time during a ramp #
790
+ # on another clockline. If it does, we need to let the ramping #
791
+ # clockline know it needs to break it's loop at that time #
792
+ ########################################################################
774
793
# convert all_change_times to a numpy array
775
794
all_change_times_numpy = array (all_change_times )
776
-
795
+
777
796
# quantise the all change times to the pseudoclock clock resolution
778
- # all_change_times_numpy = (all_change_times_numpy/self.clock_resolution).round()*self.clock_resolution
779
- all_change_times_numpy = self .quantise_to_pseudoclock (all_change_times_numpy )
780
-
797
+ all_change_times_numpy = self .quantise_to_pseudoclock (
798
+ all_change_times_numpy
799
+ )
800
+
781
801
# Loop through each clockline
782
- # print ramps_by_clockline
783
802
for clock_line , ramps in ramps_by_clockline .items ():
784
803
# for each clockline, loop through the ramps on that clockline
785
804
for ramp_start_time , ramp_end_time in ramps :
786
- # for each ramp, check to see if there is a change time in all_change_times which intersects
787
- # with the ramp. If there is, add a change time into this clockline at that point
788
- indices = np .where ((ramp_start_time < all_change_times_numpy ) & (all_change_times_numpy < ramp_end_time ))
805
+ # for each ramp, check to see if there is a change time in
806
+ # all_change_times which intersects with the ramp. If there is,
807
+ # add a change time into this clockline at that point
808
+ indices = np .where (
809
+ (ramp_start_time < all_change_times_numpy ) &
810
+ (all_change_times_numpy < ramp_end_time )
811
+ )
789
812
for idx in indices [0 ]:
790
813
change_times [clock_line ].append (all_change_times_numpy [idx ])
791
-
814
+
792
815
# Get rid of duplicates:
793
816
all_change_times = list (set (all_change_times_numpy ))
794
817
all_change_times .sort ()
795
-
818
+
796
819
# Check that the pseudoclock can handle updates this fast
797
820
for i , t in enumerate (all_change_times [:- 1 ]):
798
821
dt = all_change_times [i + 1 ] - t
799
822
if dt < 1.0 / self .clock_limit :
800
- raise LabscriptError ('Commands have been issued to devices attached to %s at t= %s s and %s s. ' % (self .name , str (t ),str (all_change_times [i + 1 ])) +
801
- 'This Pseudoclock cannot support update delays shorter than %s sec.' % (str (1.0 / self .clock_limit )))
823
+ raise LabscriptError (
824
+ "Commands have been issued to devices attached to "
825
+ f"{ self .name } at t={ t } and t={ all_change_times [i + 1 ]} . "
826
+ "This Pseudoclock cannot support update delays shorter "
827
+ f"than { 1.0 / self .clock_limit } seconds."
828
+ )
802
829
803
- ####################################################################################################
804
- # For each clockline, make sure we have a change time for triggers, stop_time, t=0 and #
805
- # check that no change tiems are too close together #
806
- ####################################################################################################
830
+ ########################################################################
831
+ # For each clockline, make sure we have a change time for triggers, #
832
+ # stop_time, t=0 and check that no change times are too close together #
833
+ ########################################################################
807
834
for clock_line , change_time_list in change_times .items ():
808
- # include trigger times in change_times, so that pseudoclocks always have an instruction immediately following a wait:
835
+ # include trigger times in change_times, so that pseudoclocks always
836
+ # have an instruction immediately following a wait:
809
837
change_time_list .extend (self .parent_device .trigger_times )
810
-
838
+
811
839
# If the device has no children, we still need it to have a
812
840
# single instruction. So we'll add 0 as a change time:
813
841
if not change_time_list :
814
842
change_time_list .append (0 )
815
-
843
+
816
844
# quantise the all change times to the pseudoclock clock resolution
817
- # change_time_list = (array(change_time_list)/self.clock_resolution).round()*self.clock_resolution
818
845
change_time_list = self .quantise_to_pseudoclock (change_time_list )
819
-
846
+
820
847
# Get rid of duplicates if trigger times were already in the list:
821
848
change_time_list = list (set (change_time_list ))
822
849
change_time_list .sort ()
823
850
851
+ # Also add the stop time as as change time. First check that it
852
+ # isn't too close to the time of the last instruction:
853
+ if not self .parent_device .stop_time in change_time_list :
854
+ dt = self .parent_device .stop_time - change_time_list [- 1 ]
855
+ if abs (dt ) < 1.0 / clock_line .clock_limit :
856
+ raise LabscriptError (
857
+ "The stop time of the experiment is "
858
+ f"t={ self .parent_device .stop_time } , but the last "
859
+ f"instruction for a device attached to { self .name } is "
860
+ f"at t={ change_time_list [- 1 ]} . One or more connected "
861
+ "devices cannot support update delays shorter than "
862
+ f"{ 1.0 / clock_line .clock_limit } seconds. Please set the "
863
+ "stop_time a bit later."
864
+ )
865
+
866
+ change_time_list .append (self .parent_device .stop_time )
867
+
868
+ # Sort change times so self.stop_time will be in the middle
869
+ # somewhere if it is prior to the last actual instruction.
870
+ # Whilst this means the user has set stop_time in error, not
871
+ # catching the error here allows it to be caught later by the
872
+ # specific device that has more instructions after
873
+ # self.stop_time. Thus we provide the user with sligtly more
874
+ # detailed error info.
875
+ change_time_list .sort ()
876
+
824
877
# index to keep track of in all_change_times
825
878
j = 0
826
879
# Check that no two instructions are too close together:
827
880
for i , t in enumerate (change_time_list [:- 1 ]):
828
881
dt = change_time_list [i + 1 ] - t
829
882
if dt < 1.0 / clock_line .clock_limit :
830
- raise LabscriptError ('Commands have been issued to devices attached to clockline %s at t= %s s and %s s. ' % (clock_line .name , str (t ),str (change_time_list [i + 1 ])) +
831
- 'One or more connected devices on ClockLine %s cannot support update delays shorter than %s sec.' % (clock_line .name , str (1.0 / clock_line .clock_limit )))
832
-
883
+ raise LabscriptError (
884
+ "Commands have been issued to devices attached to "
885
+ f"clockline { clock_line .name } at t={ t } and "
886
+ f"t={ change_time_list [i + 1 ]} . One or more connected "
887
+ f"devices on ClockLine { clock_line .name } cannot "
888
+ "support update delays shorter than "
889
+ f"{ 1.0 / clock_line .clock_limit } seconds"
890
+ )
891
+
833
892
all_change_times_len = len (all_change_times )
834
893
# increment j until we reach the current time
835
894
while all_change_times [j ] < t and j < all_change_times_len - 1 :
836
895
j += 1
837
896
# j should now index all_change_times at "t"
838
- # Check that the next all change_time is not too close (and thus would force this clock tick to be faster than the clock_limit)
897
+ # Check that the next all change_time is not too close (and thus
898
+ # would force this clock tick to be faster than the minimum
899
+ # clock high time)
839
900
dt = all_change_times [j + 1 ] - t
840
- if dt < 1.0 / clock_line .clock_limit :
841
- raise LabscriptError ('Commands have been issued to devices attached to %s at t= %s s and %s s. ' % (self .name , str (t ),str (all_change_times [j + 1 ])) +
842
- 'One or more connected devices on ClockLine %s cannot support update delays shorter than %s sec.' % (clock_line .name , str (1.0 / clock_line .clock_limit )))
843
-
844
- # Also add the stop time as as change time. First check that it isn't too close to the time of the last instruction:
845
- if not self .parent_device .stop_time in change_time_list :
846
- dt = self .parent_device .stop_time - change_time_list [- 1 ]
847
- if abs (dt ) < 1.0 / clock_line .clock_limit :
848
- raise LabscriptError ('The stop time of the experiment is t= %s s, but the last instruction for a device attached to %s is at t= %s s. ' % ( str (self .stop_time ), self .name , str (change_time_list [- 1 ])) +
849
- 'One or more connected devices cannot support update delays shorter than %s sec. Please set the stop_time a bit later.' % str (1.0 / clock_line .clock_limit ))
850
-
851
- change_time_list .append (self .parent_device .stop_time )
852
-
853
- # Sort change times so self.stop_time will be in the middle
854
- # somewhere if it is prior to the last actual instruction. Whilst
855
- # this means the user has set stop_time in error, not catching
856
- # the error here allows it to be caught later by the specific
857
- # device that has more instructions after self.stop_time. Thus
858
- # we provide the user with sligtly more detailed error info.
859
- change_time_list .sort ()
860
-
861
- # because we made the list into a set and back to a list, it is now a different object
862
- # so modifying it won't update the list in the dictionary.
863
- # So store the updated list in the dictionary
901
+ if dt < (2 * clock_line .minimum_clock_high_time ):
902
+ raise LabscriptError (
903
+ "Commands have been issued to devices attached to "
904
+ f"{ self .name } at t={ t } and t={ all_change_times [j + 1 ]} . "
905
+ "One or more connected devices on ClockLine "
906
+ f"{ clock_line .name } cannot support clock ticks with a "
907
+ "digital high time shorter than "
908
+ f"{ clock_line .minimum_clock_high_time } which is more "
909
+ "than half the available time between the event at "
910
+ f"t={ t } on ClockLine { clock_line .name } and the next "
911
+ "event on another ClockLine."
912
+ )
913
+
914
+ # because we made the list into a set and back to a list, it is now
915
+ # a different object so modifying it won't update the list in the
916
+ # dictionary. So store the updated list in the dictionary
864
917
change_times [clock_line ] = change_time_list
865
918
return all_change_times , change_times
866
-
919
+
867
920
def expand_change_times (self , all_change_times , change_times , outputs_by_clockline ):
868
921
"""For each time interval delimited by change_times, constructs
869
922
an array of times at which the clock for this device needs to
@@ -2412,7 +2465,11 @@ def go_high(self):
2412
2465
self .add_instruction (0 ,1 )
2413
2466
self ._static_value = 1
2414
2467
else :
2415
- raise LabscriptError ('%s %s has already been set to %s. It cannot also be set to %s.' % (self .description , self .name , self .instruction_to_string [self ._static_value ], self .instruction_to_string [value ]))
2468
+ raise LabscriptError (
2469
+ f"{ self .description } { self .name } has already been set to "
2470
+ f"{ self .instruction_to_string (self ._static_value )} . It cannot "
2471
+ "also be set to 1."
2472
+ )
2416
2473
2417
2474
def go_low (self ):
2418
2475
"""Command a static low output.
@@ -2424,7 +2481,11 @@ def go_low(self):
2424
2481
self .add_instruction (0 ,0 )
2425
2482
self ._static_value = 0
2426
2483
else :
2427
- raise LabscriptError ('%s %s has already been set to %s. It cannot also be set to %s.' % (self .description , self .name , self .instruction_to_string [self ._static_value ], self .instruction_to_string [value ]))
2484
+ raise LabscriptError (
2485
+ f"{ self .description } { self .name } has already been set to "
2486
+ f"{ self .instruction_to_string (self ._static_value )} . It cannot "
2487
+ "also be set to 0."
2488
+ )
2428
2489
2429
2490
def get_change_times (self ):
2430
2491
"""Enforces no change times.
0 commit comments