@@ -190,6 +190,263 @@ def compile_and_transfer_sequence(self, sequence, driver=None):
190190
191191 return self ._transfer_sequences (driver , to_send , infos )
192192
193+ def compile_loop (self , sequence , for_file = False , factor = True , ** kwargs ):
194+ """ Transform a sequence of pulse to a dict of waveform suitable for
195+ sequence mode of the AWG
196+
197+ Parameters
198+ ----------
199+ pulses : list(Pulse)
200+ List of pulse generated by the compilation of a sequence.
201+
202+ Returns
203+ -------
204+ result : bool
205+ Boolean indicating whether or not the compilation succeeded.
206+
207+ to_send or traceback : dict
208+ Dict of {channel: array of (bytearrays,intarray,intarray)} to send
209+ to the AWG in case of success containing waveform, position(s) and
210+ repeat(s) or the traceback of the issues in case of failure.
211+
212+
213+ """
214+ items , errors = self .preprocess_sequence (sequence )
215+
216+ if errors :
217+ return False , {}, errors
218+
219+ duration = max ([pulse .stop for pulse in items ])
220+ if sequence .time_constrained :
221+ # Total length of the sequence to send to the AWG
222+ duration = sequence .duration
223+
224+ # Collect the channels used in the pulses' sequence
225+ used_channels = set ([pulse .channel [:3 ] for pulse in items ])
226+
227+ # Coefficient to convert the start and stop of pulses in second and
228+ # then in index integer for array
229+ time_to_index = TIME_CONVERSION [self .time_unit ]['s' ] * \
230+ self .sampling_frequency
231+
232+ # Length of the sequence
233+ sequence_length = int (round (duration * time_to_index ))
234+
235+ # make zero space a multiple repeat of 256 zero samples
236+ zero_length = 256
237+ azeros = np .ones (zero_length , dtype = np .uint16 )* (2 ** 13 )
238+ mzeros = np .zeros (zero_length , dtype = np .int8 )
239+ array_analog = {}
240+ array_M1 = {}
241+ array_M2 = {}
242+ repeats = []
243+ for channel in used_channels :
244+ array_analog [channel ] = []
245+ array_M1 [channel ] = []
246+ array_M2 [channel ] = []
247+ intervals_to_pulses_a = {}
248+ intervals_to_pulses_m1 = {}
249+ intervals_to_pulses_m2 = {}
250+ for channel in used_channels :
251+ intervals_to_pulses_a [channel ] = {}
252+ intervals_to_pulses_m1 [channel ] = {}
253+ intervals_to_pulses_m2 [channel ] = {}
254+
255+ if factor :
256+ # intervals are places that have pulses on at least one channel
257+ intervals = []
258+ for pulse in [i for i in items if i .duration != 0.0 ]:
259+
260+ waveform = pulse .waveform
261+ start_index = int (round (pulse .start * time_to_index ))
262+ stop_index = start_index + len (waveform )
263+ intervals .append ((start_index , stop_index ))
264+ # sort and merge intervals
265+ # XXX this is mostly duplicated and should be refactored
266+ intervals .sort ()
267+ i = 1
268+ while i < len (intervals ):
269+ while i < len (intervals ) and intervals [i ][0 ] < intervals [i - 1 ][1 ]:
270+ newlast = intervals [i ][1 ]
271+ del intervals [i ]
272+ intervals [i - 1 ] = (intervals [i - 1 ][0 ],
273+ max (newlast , intervals [i - 1 ][1 ]))
274+ i += 1
275+
276+ # check that the minimum length of the each pulse and space between
277+ # them are > 256 samples fix if the condition is not satisfied
278+ self .merge_intervals (intervals , sequence_length )
279+
280+ # fill up the space between start of sequence and first sample of pulse
281+ # with zero samples
282+ if intervals [0 ][0 ] != 1 :
283+ zcount = int ((intervals [0 ][0 ] - 1 ) / zero_length ) - 1
284+ zrem = zero_length + (intervals [0 ][0 ] - 1 ) % zero_length
285+ azrem = np .ones (zrem , dtype = np .uint16 )* (2 ** 13 )
286+ mzrem = np .zeros (zrem , dtype = np .int8 )
287+ if zcount > 0 :
288+ repeats .append (zcount )
289+ repeats .append (1 )
290+ for channel in used_channels :
291+ if zcount > 0 :
292+ array_analog [channel ].append (azeros )
293+ array_M1 [channel ].append (mzeros )
294+ array_M2 [channel ].append (mzeros )
295+ array_analog [channel ].append (azrem )
296+ array_M1 [channel ].append (mzrem )
297+ array_M2 [channel ].append (mzrem )
298+
299+ for idx , interval in enumerate (intervals ):
300+ for channel in used_channels :
301+ afill = 2 ** 13 * np .ones (interval [1 ] - interval [0 ],
302+ dtype = np .uint16 )
303+ mfill1 = np .zeros (interval [1 ] - interval [0 ], dtype = np .int8 )
304+ mfill2 = np .zeros (interval [1 ] - interval [0 ], dtype = np .int8 )
305+ array_analog [channel ].append (afill )
306+ array_M1 [channel ].append (mfill1 )
307+ array_M2 [channel ].append (mfill2 )
308+ intervals_to_pulses_a [channel ][interval ] = afill
309+ intervals_to_pulses_m1 [channel ][interval ] = mfill1
310+ intervals_to_pulses_m2 [channel ][interval ] = mfill2
311+
312+ remaining = 0
313+ if idx + 1 == len (intervals ):
314+ remaining = sequence_length - interval [1 ] + 1
315+ else :
316+ remaining = intervals [idx + 1 ][0 ] - intervals [idx ][1 ]
317+ zcount = int (remaining / zero_length ) - 1
318+ zrem = zero_length + remaining % zero_length
319+ azrem = np .ones (zrem , dtype = np .uint16 )* (2 ** 13 )
320+ mzrem = np .zeros (zrem , dtype = np .int8 )
321+ repeats .append (1 )
322+ if zcount > 0 :
323+ repeats .append (zcount )
324+ repeats .append (1 )
325+
326+ if remaining != 0 :
327+ for channel in used_channels :
328+ if zcount > 0 :
329+ array_analog [channel ].append (azeros )
330+ array_M1 [channel ].append (mzeros )
331+ array_M2 [channel ].append (mzeros )
332+ array_analog [channel ].append (azrem )
333+ array_M1 [channel ].append (mzrem )
334+ array_M2 [channel ].append (mzrem )
335+ else :
336+ start_index = 0
337+ stop_index = sequence_length
338+ intervals = [(start_index , stop_index )]
339+ for channel in used_channels :
340+ a_fill = np .ones (sequence_length , dtype = np .uint16 )* (2 ** 13 )
341+ m1_fill = np .zeros (sequence_length , dtype = np .int8 )
342+ m2_fill = np .zeros (sequence_length , dtype = np .int8 )
343+ array_analog [channel ].append (a_fill )
344+ array_M1 [channel ].append (m1_fill )
345+ array_M2 [channel ].append (m2_fill )
346+ intervals_to_pulses_a [channel ][intervals [0 ]]= a_fill
347+ intervals_to_pulses_m1 [channel ][intervals [0 ]]= m1_fill
348+ intervals_to_pulses_m2 [channel ][intervals [0 ]]= m2_fill
349+ repeats = [1 ]
350+
351+ for pulse in [i for i in items if i .duration != 0.0 ]:
352+ waveform = pulse .waveform
353+ channel = pulse .channel [:3 ]
354+ channeltype = pulse .channel [4 :]
355+
356+ start_index = int (round (pulse .start * time_to_index ))
357+ stop_index = start_index + len (waveform )
358+
359+ interval_i = 0
360+ while intervals [interval_i ][1 ] < start_index :
361+ interval_i += 1
362+ interval = intervals [interval_i ]
363+
364+ wav_slice = slice (start_index - interval [0 ],
365+ stop_index - interval [0 ])
366+ if channeltype == 'A' and pulse .kind == 'Analogical' :
367+ intervals_to_pulses_a [channel ][interval ][wav_slice ] += \
368+ (np .rint (8191 * waveform )).astype (np .uint16 )
369+ elif channeltype == 'M1' and pulse .kind == 'Logical' :
370+ intervals_to_pulses_m1 [channel ][interval ][wav_slice ] += \
371+ waveform
372+ elif channeltype == 'M2' and pulse .kind == 'Logical' :
373+ intervals_to_pulses_m2 [channel ][interval ][wav_slice ] += \
374+ waveform
375+ else :
376+ msg = 'Selected channel does not match kind for pulse {} ({}).'
377+ return (False , None , None , dict (),
378+ {'Kind issue' : msg .format (pulse .index ,
379+ (pulse .kind , pulse .channel ))}
380+ )
381+
382+ # Check the overflows
383+ traceback = {}
384+ for channel in used_channels :
385+ for i in range (len (array_analog [channel ])):
386+ analog = array_analog [channel ][i ]
387+ m1 = array_M1 [channel ][i ]
388+ m2 = array_M2 [channel ][i ]
389+ if analog .max () > 16383 or analog .min () < 0 :
390+ mes = 'Analogical values out of range.'
391+ traceback ['{}_A' .format (channel )] = mes
392+
393+ elif m1 .max () > 1 or m1 .min () < 0 :
394+ mes = 'Overflow in marker 1.'
395+ traceback ['{}_M1' .format (channel )] = mes
396+
397+ elif m2 .max () > 1 or m2 .min () < 0 :
398+ mes = 'Overflow in marker 2.'
399+ traceback ['{}_M2' .format (channel )] = mes
400+
401+ if traceback :
402+ return False , None , None , dict (), traceback
403+
404+ # Invert marked logical channels.
405+ for i_ch in self .inverted_log_channels :
406+ ch , m = i_ch .split ('_' )
407+ if m == 'M1' :
408+ for waveform in array_M1 [ch ]:
409+ np .logical_not (waveform , waveform )
410+ else :
411+ for waveform in array_M2 [ch ]:
412+ np .logical_not (waveform , waveform )
413+
414+ # Byte arrays to send to the AWG
415+ wfs = {}
416+ already_added = {}
417+ for channel in used_channels :
418+ wfs [int (channel [- 1 ])] = []
419+ for i in range (len (array_analog [channel ])):
420+ addr = id (array_analog [channel ][i ])
421+ if addr in already_added :
422+ added = already_added [addr ]
423+ wfs [int (channel [- 1 ])].append (added )
424+
425+ else :
426+ waveform_new = (array_analog [channel ][i ] +
427+ array_M1 [channel ][i ]* (2 ** 14 ) +
428+ array_M2 [channel ][i ]* (2 ** 15 ))
429+ aux = np .empty (2 * len (waveform_new ), dtype = np .uint8 )
430+ if for_file :
431+ wfadded = waveform_new
432+ else :
433+ aux [::2 ] = waveform_new % 2 ** 8
434+ aux [1 ::2 ] = waveform_new // 2 ** 8
435+ wfadded = bytearray (aux )
436+ wfs [int (channel [- 1 ])].append (wfadded )
437+ already_added [addr ] = wfadded
438+
439+ # Build sequence infos
440+ name = self ._cache ['sequence_name' ]
441+ infos = dict (sampling_frequency = self .sampling_frequency ,
442+ sequence_ch1 = '' ,
443+ sequence_ch2 = '' ,
444+ sequence_ch3 = '' ,
445+ sequence_ch4 = '' )
446+ for c in used_channels :
447+ infos ['sequence_ch%s' % c [2 ]] = name + '_' + c
448+ return True , wfs , repeats , infos , traceback
449+
193450 def list_sequence_infos (self ):
194451 """List the sequence infos returned after a successful completion.
195452
0 commit comments