def write_APS2_file(awgData, fileName): ''' Main function to pack channel sequences into an APS2 h5 file. ''' # Convert QGL IR into a representation that is closer to the hardware. awgData['ch12']['linkList'], wfLib = preprocess( awgData['ch12']['linkList'], awgData['ch12']['wfLib'], awgData['ch12']['correctionT']) # compress marker data for field in ['ch12m1', 'ch12m2', 'ch12m3', 'ch12m4']: if 'linkList' in awgData[field].keys(): PatternUtils.convert_lengths_to_samples(awgData[field]['linkList'], SAMPLING_RATE) compress_marker(awgData[field]['linkList']) else: awgData[field]['linkList'] = [] #Create the waveform vectors wfInfo = [] wfInfo.append(create_wf_vector({key: wf.real for key, wf in wfLib.items()})) wfInfo.append(create_wf_vector({key: wf.imag for key, wf in wfLib.items()})) # build instruction vector instructions = create_instr_data([ awgData[s]['linkList'] for s in ['ch12', 'ch12m1', 'ch12m2', 'ch12m3', 'ch12m4'] ], wfInfo[0][1]) #Open the HDF5 file if os.path.isfile(fileName): os.remove(fileName) with h5py.File(fileName, 'w') as FID: FID['/'].attrs['Version'] = 3.0 FID['/'].attrs['channelDataFor'] = np.uint16([1, 2]) #Create the groups and datasets for chanct in range(2): chanStr = '/chan_{0}'.format(chanct + 1) chanGroup = FID.create_group(chanStr) #Write the waveformLib to file FID.create_dataset(chanStr + '/waveforms', data=wfInfo[chanct][0]) #Write the instructions to channel 1 if np.mod(chanct, 2) == 0: FID.create_dataset(chanStr + '/instructions', data=instructions)
def preprocess(seqs, shapeLib, T): for seq in seqs: PatternUtils.propagate_frame_changes(seq) seqs = PatternUtils.convert_lengths_to_samples(seqs, SAMPLING_RATE, ADDRESS_UNIT) PatternUtils.quantize_phase(seqs, 1.0/2**13) wfLib = build_waveforms(seqs, shapeLib) PatternUtils.correct_mixers(wfLib, T) return seqs, wfLib
def write_sequence_file(awgData, fileName): ''' Main function to pack channel sequences into an APS2 h5 file. ''' # Convert QGL IR into a representation that is closer to the hardware. awgData['ch12']['linkList'], wfLib = preprocess(awgData['ch12']['linkList'], awgData['ch12']['wfLib'], awgData['ch12']['correctionT']) # compress marker data for field in ['ch12m1', 'ch12m2', 'ch12m3', 'ch12m4']: if 'linkList' in awgData[field].keys(): PatternUtils.convert_lengths_to_samples(awgData[field]['linkList'], SAMPLING_RATE) compress_marker(awgData[field]['linkList']) else: awgData[field]['linkList'] = [] #Create the waveform vectors wfInfo = [] wfInfo.append(create_wf_vector({key:wf.real for key,wf in wfLib.items()})) wfInfo.append(create_wf_vector({key:wf.imag for key,wf in wfLib.items()})) # build instruction vector instructions = create_instr_data([awgData[s]['linkList'] for s in ['ch12', 'ch12m1', 'ch12m2', 'ch12m3', 'ch12m4']], wfInfo[0][1]) #Open the HDF5 file if os.path.isfile(fileName): os.remove(fileName) with h5py.File(fileName, 'w') as FID: FID['/'].attrs['Version'] = 3.0 FID['/'].attrs['channelDataFor'] = np.uint16([1,2]) #Create the groups and datasets for chanct in range(2): chanStr = '/chan_{0}'.format(chanct+1) chanGroup = FID.create_group(chanStr) #Write the waveformLib to file FID.create_dataset(chanStr+'/waveforms', data=wfInfo[chanct][0]) #Write the instructions to channel 1 if np.mod(chanct,2) == 0: FID.create_dataset(chanStr+'/instructions', data=instructions)
def preprocess(seqs, shapeLib, T): for seq in seqs: PatternUtils.propagate_frame_changes(seq) seqs = PatternUtils.convert_lengths_to_samples(seqs, SAMPLING_RATE, ADDRESS_UNIT) PatternUtils.quantize_phase(seqs, 1.0 / 2**13) wfLib = build_waveforms(seqs, shapeLib) PatternUtils.correct_mixers(wfLib, T) return seqs, wfLib
def __init__(self, waveform): self.label = waveform.label self.key = waveform.key self.amp = waveform.amp self.length = PatternUtils.convert_length_to_samples( waveform.length, SAMPLING_RATE, ADDRESS_UNIT) self.phase = waveform.phase self.frameChange = waveform.frameChange self.isTimeAmp = waveform.isTimeAmp self.frequency = waveform.frequency self.repeat = 1 self.markerDelay1 = None self.markerDelay2 = None
def preprocess(seqs, shapeLib, T): seqs, miniLLrepeat = unroll_loops(seqs) for seq in seqs: PatternUtils.propagate_frame_changes(seq) seqs = PatternUtils.convert_lengths_to_samples(seqs, SAMPLING_RATE, ADDRESS_UNIT) PatternUtils.quantize_phase(seqs, 1.0/2**13) compress_sequences(seqs) wfLib = build_waveforms(seqs, shapeLib) PatternUtils.correct_mixers(wfLib, T) for ct in range(len(seqs)): seqs[ct] = apply_min_pulse_constraints(seqs[ct], wfLib) return seqs, miniLLrepeat, wfLib
def preprocess(seqs, shapeLib, T): seqs, miniLLrepeat = unroll_loops(seqs) for seq in seqs: PatternUtils.propagate_frame_changes(seq) seqs = PatternUtils.convert_lengths_to_samples(seqs, SAMPLING_RATE, ADDRESS_UNIT) PatternUtils.quantize_phase(seqs, 1.0 / 2**13) compress_sequences(seqs) wfLib = build_waveforms(seqs, shapeLib) PatternUtils.correct_mixers(wfLib, T) for ct in range(len(seqs)): seqs[ct] = apply_min_pulse_constraints(seqs[ct], wfLib) return seqs, miniLLrepeat, wfLib
def preprocess(seqs, shapeLib, T): for seq in seqs: for ct, e in enumerate(seq): if isinstance(e, Compiler.Waveform): seq[ct] = APSWaveform(e) seqs, miniLLrepeat = unroll_loops(seqs) for seq in seqs: PatternUtils.propagate_frame_changes(seq, wf_type=APSWaveform) PatternUtils.quantize_phase(seqs, 1.0 / 2**13, wf_type=APSWaveform) compress_sequences(seqs) wfLib = build_waveforms(seqs, shapeLib) PatternUtils.correct_mixers(wfLib, T) for ct in range(len(seqs)): seqs[ct] = apply_min_pulse_constraints(seqs[ct], wfLib) return seqs, miniLLrepeat, wfLib
def merge_APS_markerData(IQLL, markerLL, markerNum): ''' Helper function to merge two marker channels into an IQ channel. ''' if len(markerLL) == 0: return assert len(IQLL) <= len(markerLL), "Sequence length mismatch" if len(IQLL) < len(markerLL): for ct in range(len(markerLL) - len(IQLL)): IQLL.append([]) for seq in markerLL: PatternUtils.convert_lengths_to_samples(seq, SAMPLING_RATE, ADDRESS_UNIT) markerAttr = 'markerDelay' + str(markerNum) #Step through the all the miniLL's together for miniLL_IQ, miniLL_m in zip_longest(IQLL, markerLL): #Find the switching points of the marker channels switchPts = [] prevAmplitude = 0 t = 0 for entry in miniLL_m: if hasattr(entry, 'amp') and prevAmplitude != entry.amp: switchPts.append(t) prevAmplitude = entry.amp t += entry.length if len(switchPts) == 0: # need at least a WAIT on an empty IQ LL in order to match segment sequencing if len(miniLL_IQ) == 0: miniLL_IQ.append(ControlFlow.qwait()) continue # Push on an extra switch point if we have an odd number of switches (to maintain state) if len(switchPts) % 2 == 1: switchPts.append(t) #Assume switch pts seperated by 0 or 1 point are single trigger blips blipPts = (np.diff(switchPts) <= 1).nonzero()[0] for pt in blipPts[::-1]: del switchPts[pt+1] # if the IQ sequence is empty, make an ideally length-matched sequence if len(miniLL_IQ) == 0: miniLL_IQ.append(ControlFlow.qwait()) miniLL_IQ.append(padding_entry(max(switchPts[0], MIN_ENTRY_LENGTH))) for length in np.diff(switchPts): miniLL_IQ.append(padding_entry(max(length, MIN_ENTRY_LENGTH))) #Find the cummulative length for each entry of IQ channel timePts = np.cumsum([0] + [entry.length for entry in miniLL_IQ]) #Ensure the IQ LL is long enough to support the blips if max(switchPts) >= timePts[-1]: dt = max(switchPts) - timePts[-1] if hasattr(miniLL_IQ[-1], 'isTimeAmp') and miniLL_IQ[-1].isTimeAmp: miniLL_IQ[-1].length += dt + 4 else: # inject before any control flow statements at the end of the sequence idx = len(miniLL_IQ) while idx > 0 and isinstance(miniLL_IQ[idx-1], ControlFlow.ControlInstruction): idx -=1 miniLL_IQ.insert(idx, padding_entry(max(dt+4, MIN_ENTRY_LENGTH))) #Now map onto linklist elements curIQIdx = 0 trigQueue = [] for switchPt in switchPts: # skip if: # 1) control-flow instruction or label (i.e. not a waveform) # 2) the trigger count is too long # 3) the previous trigger pulse entends into the current entry while (not isinstance(miniLL_IQ[curIQIdx], Compiler.Waveform) or (switchPt - timePts[curIQIdx]) > (ADDRESS_UNIT * MAX_TRIGGER_COUNT) or len(trigQueue) > 1): # update the trigger queue, dropping triggers that have played trigQueue = [t - miniLL_IQ[curIQIdx].length for t in trigQueue] trigQueue = [t for t in trigQueue if t >= 0] curIQIdx += 1 # add padding pulses if needed if curIQIdx >= len(miniLL_IQ): pad = max(MIN_ENTRY_LENGTH, min(trigQueue, 0)) miniLL_IQ.append(padding_entry(pad)) #Push on the trigger count #If our switch point is before the start of the LL entry then we are in trouble... if switchPt - timePts[curIQIdx] < 0: #See if the previous entry was a TA pair and whether we can split it needToShift = switchPt - timePts[curIQIdx-1] assert needToShift > MIN_ENTRY_LENGTH + ADDRESS_UNIT, "Sequential marker blips too close together." if isinstance(miniLL_IQ[curIQIdx-1], Compiler.Waveform) and \ miniLL_IQ[curIQIdx-1].isTimeAmp and \ miniLL_IQ[curIQIdx-1].length > (needToShift + MIN_ENTRY_LENGTH): miniLL_IQ.insert(curIQIdx, deepcopy(miniLL_IQ[curIQIdx-1])) miniLL_IQ[curIQIdx-1].length = needToShift-ADDRESS_UNIT miniLL_IQ[curIQIdx].length -= needToShift-ADDRESS_UNIT miniLL_IQ[curIQIdx].markerDelay1 = None miniLL_IQ[curIQIdx].markerDelay2 = None setattr(miniLL_IQ[curIQIdx], markerAttr, ADDRESS_UNIT) #Recalculate the timePts timePts = np.cumsum([0] + [entry.length for entry in miniLL_IQ]) else: setattr(miniLL_IQ[curIQIdx], markerAttr, 0) print("Had to push marker blip out to start of next entry.") else: setattr(miniLL_IQ[curIQIdx], markerAttr, switchPt - timePts[curIQIdx]) trigQueue.insert(0, switchPt - timePts[curIQIdx]) # update the trigger queue trigQueue = [t - miniLL_IQ[curIQIdx].length for t in trigQueue] trigQueue = [t for t in trigQueue if t >= 0] curIQIdx += 1 #Replace any remaining empty entries with None for miniLL_IQ in IQLL: for entry in miniLL_IQ: if isinstance(entry, Compiler.Waveform) and not hasattr(entry, markerAttr): setattr(entry, markerAttr, None)
def merge_APS_markerData(IQLL, markerLL, markerNum): ''' Helper function to merge two marker channels into an IQ channel. ''' if len(markerLL) == 0: return assert len(IQLL) <= len(markerLL), "Sequence length mismatch" if len(IQLL) < len(markerLL): for ct in range(len(markerLL) - len(IQLL)): IQLL.append([]) for seq in markerLL: PatternUtils.convert_lengths_to_samples(seq, SAMPLING_RATE, ADDRESS_UNIT, Compiler.Waveform) markerAttr = 'markerDelay' + str(markerNum) #Step through the all the miniLL's together for miniLL_IQ, miniLL_m in zip_longest(IQLL, markerLL): #Find the switching points of the marker channels switchPts = [] prevAmplitude = 0 t = 0 for entry in miniLL_m: if hasattr(entry, 'amp') and prevAmplitude != entry.amp: switchPts.append(t) prevAmplitude = entry.amp t += entry.length if len(switchPts) == 0: # need at least a WAIT on an empty IQ LL in order to match segment sequencing if len(miniLL_IQ) == 0: miniLL_IQ.append(ControlFlow.qwait()) continue # Push on an extra switch point if we have an odd number of switches (to maintain state) if len(switchPts) % 2 == 1: switchPts.append(t) #Assume switch pts seperated by 0 or 1 point are single trigger blips blipPts = (np.diff(switchPts) <= 1).nonzero()[0] for pt in blipPts[::-1]: del switchPts[pt + 1] # if the IQ sequence is empty, make an ideally length-matched sequence if len(miniLL_IQ) == 0: miniLL_IQ.append(ControlFlow.qwait()) miniLL_IQ.append(padding_entry(max(switchPts[0], MIN_ENTRY_LENGTH))) for length in np.diff(switchPts): miniLL_IQ.append(padding_entry(max(length, MIN_ENTRY_LENGTH))) #Find the cummulative length for each entry of IQ channel timePts = np.cumsum([0] + [entry.length for entry in miniLL_IQ]) #Ensure the IQ LL is long enough to support the blips if max(switchPts) >= timePts[-1]: dt = max(switchPts) - timePts[-1] if hasattr(miniLL_IQ[-1], 'isTimeAmp') and miniLL_IQ[-1].isTimeAmp: miniLL_IQ[-1].length += dt + 4 else: # inject before any control flow statements at the end of the sequence idx = len(miniLL_IQ) while idx > 0 and isinstance(miniLL_IQ[idx - 1], ControlFlow.ControlInstruction): idx -= 1 miniLL_IQ.insert(idx, padding_entry(max(dt + 4, MIN_ENTRY_LENGTH))) #Now map onto linklist elements curIQIdx = 0 trigQueue = [] for ct, switchPt in enumerate(switchPts): # skip if: # 1) control-flow instruction or label (i.e. not a waveform) # 2) the trigger count is too long # 3) the previous trigger pulse entends into the current entry while (not isinstance(miniLL_IQ[curIQIdx], APSWaveform) or (switchPt - timePts[curIQIdx]) > (ADDRESS_UNIT * MAX_TRIGGER_COUNT) or len(trigQueue) > 1): # update the trigger queue, dropping triggers that have played trigQueue = [t - miniLL_IQ[curIQIdx].length for t in trigQueue] trigQueue = [t for t in trigQueue if t >= 0] curIQIdx += 1 # add padding pulses if needed if curIQIdx >= len(miniLL_IQ): if len(trigQueue) > 0: pad = max(MIN_ENTRY_LENGTH, min(trigQueue, 0)) else: pad = MIN_ENTRY_LENGTH miniLL_IQ.append(padding_entry(pad)) #Push on the trigger count #If our switch point is before the start of the LL entry then we are in trouble... if switchPt - timePts[curIQIdx] < 0: #See if the previous entry was a TA pair and whether we can split it needToShift = switchPt - timePts[curIQIdx - 1] assert needToShift > MIN_ENTRY_LENGTH + ADDRESS_UNIT, "Sequential marker blips too close together." if isinstance(miniLL_IQ[curIQIdx-1], APSWaveform) and \ miniLL_IQ[curIQIdx-1].isTimeAmp and \ miniLL_IQ[curIQIdx-1].length > (needToShift + MIN_ENTRY_LENGTH): miniLL_IQ.insert(curIQIdx, deepcopy(miniLL_IQ[curIQIdx - 1])) miniLL_IQ[curIQIdx - 1].length = needToShift - ADDRESS_UNIT miniLL_IQ[curIQIdx].length -= needToShift - ADDRESS_UNIT miniLL_IQ[curIQIdx].markerDelay1 = None miniLL_IQ[curIQIdx].markerDelay2 = None setattr(miniLL_IQ[curIQIdx], markerAttr, ADDRESS_UNIT) #Recalculate the timePts timePts = np.cumsum([0] + [entry.length for entry in miniLL_IQ]) else: setattr(miniLL_IQ[curIQIdx], markerAttr, 0) print( "Had to push marker blip out to start of next entry.") else: setattr(miniLL_IQ[curIQIdx], markerAttr, switchPt - timePts[curIQIdx]) trigQueue.insert(0, switchPt - timePts[curIQIdx]) # update the trigger queue trigQueue = [t - miniLL_IQ[curIQIdx].length for t in trigQueue] trigQueue = [t for t in trigQueue if t >= 0] curIQIdx += 1 # add padding pulses if needed if ct + 1 < len(switchPts) and curIQIdx >= len(miniLL_IQ): if len(trigQueue) > 0: pad = max(MIN_ENTRY_LENGTH, min(trigQueue, 0)) else: pad = MIN_ENTRY_LENGTH miniLL_IQ.append(padding_entry(pad))