diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..7f90ab9 --- /dev/null +++ b/.gitignore @@ -0,0 +1,6 @@ +# Windows default autosave extension +**/*.asv +# OSX / *nix default autosave extension +**/*.m~ +# Compiled MEX binaries (all platforms) +**/*.mex* \ No newline at end of file diff --git a/.hgignore b/.hgignore deleted file mode 100755 index 95cf8ac..0000000 --- a/.hgignore +++ /dev/null @@ -1,12 +0,0 @@ -# use glob syntax. -syntax: glob -# ignore emacs autosave files -*~ -# ignore matlab autosave files -*.asv - -# switch to regexp syntax. -syntax: regexp -# ignore unused backup subdirectories. -^bak/ -^new/ diff --git a/@AWG/AWG.m b/@AWG/AWG.m new file mode 100644 index 0000000..9dcd929 --- /dev/null +++ b/@AWG/AWG.m @@ -0,0 +1,159 @@ +classdef AWG < handle & matlab.mixin.Heterogeneous + % AWG interface class which provides a generic interface to any + % arbitrary wavfeform generetor. The hardware specific implementation + % is done in derived classes. By deriving from the SuperClass handle + % an AWG instance is always a reference to an object. + + % defining properties of an harware instrument + properties (Constant,Abstract,GetAccess = public) + nChannels + possibleResolutions + end + + % object specific generic AWG properties + properties (GetAccess = public, SetAccess=protected) + identifier; + + %pulse to waveform conversion settings + resolution; %in bits + clk; + offset; + scale; + + storedPulsegroups = AWGSTORAGE(); + activePulsegroup = 'none'; + + end + + properties (GetAccess = public, SetAccess = ?VAWG) + %virtual channel assigned to each hardware channel. + %0 means no virtual channel assigned to the channel + virtualChannels; + end + + % hardware specific methods + methods (Abstract) + %make this pulsegroup playable by AWG + addPulseGroup(self,grpdef); + + %remove this pulsegroup from memory and forget about it + removePulseGroup(self,name); + + %update the changed pulses + updatePulseGroup(self,grpdef); + + %wait for trigger + arm(self); + + %this function is for debugging purposes + issueSoftwareTrigger(self); + + val = isPlaybackInProgress(self); + +% %add a pulsegroup by name to the pulsegroups playable by this +% %machineuse is/ deprecated +% add(self,pulsegroup) +% +% load(self,grp, ind) +% +% erase(self,groups,options) +% +% syncwaveforms(self) +% +% rm(self,grp, ctrl) + end + + % Set methods controlling if argument is valid + methods + function obj = AWG(id) + obj.identifier = id; + + obj.virtualChannels = zeros(1,obj.nChannels); + + obj.offset = zeros(1,obj.nChannels); + obj.scale = ones(1,obj.nChannels); + obj.resolution = obj.possibleResolutions(1); + end + + function virtChan = getVirtualChannel(self,chan) + virtChan = self.virtualChannels(chan); + if virtChan == 0 + virtChan = []; + end + end + + function hardwareChannels = getHardwareChannel(self,virtualChannel) + if ~isscalar(virtualChannel) + error('Can not convert multiple virt channels at once, since one may refer to multiple harware channels.'); + end + hardwareChannels = find( self.virtualChannels == virtualChannel ); + end + + function setActivePulseGroup(self,pulsegroupName) + if isKey(self.storedPulsegroups,pulsegroupName) + self.activePulsegroup = pulsegroupName; + else + error('Can not activate pulsegroup "%s". Since it is not known',sequenceName); + end + end + + % set mapping function: output = scale * x + offset + function setVoltageMapping(this,channel,newScale,newOffset) + if channel>this.nChannels + error('Unknown channel %i',channel); + end + this.scale(channel) = newScale; + this.offset(channel) = newOffset; + end + + % set output resolution to bits if it is allowed + function setResolution(self,bits) + + if find(self.possibleResolutions==bits) + self.resolution = bits; + else + disp(bits) + error('%s does not support this resolution.',self.type) + end + end + + % set zerolen to positive pulselength if pulse is zero, otherwise + % to negative pulselength + function zerolen = zeroLength(self,grp,ind,zerolen) + + if isempty(ind) + ind = 1:length(grp.pulses); + end + + epsilon = self.scale/2^(self.resolution-1); + for i = 1:length(grp.pulses) + + dind = find([grp.pulses(i).data.clk] == self.clk); + npts = size(grp.pulses(i).data(dind).wf, 2); + + for virtChan=1:size(grp.pulses(i).data(dind).wf,1) + + hardChan = self.getHardwareChannel(grp.chan(virtChan)); + + if hardChan + + if any(abs(grp.pulses(i).data(dind).wf(virtChan,:)) > epsilon(hardChan) ) + zerolen(ind(i),virtChan) = -npts; + else + zerolen(ind(i),virtChan) = npts; + end + + end + end + + end + + end + + end + + methods (Static) + + end + +end \ No newline at end of file diff --git a/@AWG/waveforms.m b/@AWG/waveforms.m new file mode 100644 index 0000000..e69de29 diff --git a/@PXDAC/PXDAC.m b/@PXDAC/PXDAC.m new file mode 100644 index 0000000..dcb677c --- /dev/null +++ b/@PXDAC/PXDAC.m @@ -0,0 +1,296 @@ +classdef PXDAC < AWG + properties (Constant,GetAccess = public) + nChannels = 4; + possibleResolutions = 14; %may implement 8 if needed + + %in bytes + totalMemory = 2^30; + + % memory manager chunk size + minimalChunkSize = 2^13; + end + + properties (Constant,Abstract,GetAccess = public) + allowedVoltageRange; + end + + properties (SetAccess = protected, GetAccess = public) + handle; + serialNumber; + + + + % wfMap; % equivalent to wf on AWG + + % Map to structs with fields + % start, length (position in memory) + % waveforms (array of PXDACPULSE) + % inherited storedPulsegroups; + + + %if the ChannelMask changes the whole RAM is dropped + activeChannelMask; + end + + methods (Access = protected) + % constructor + function obj = PXDAC(id,index) + obj = obj@AWG(id); + + %check architecture + if ~strcmpi(computer('arch'), 'win64') + error('This driver only supports 64 bit windows'); + end + + %load library + if ~libisloaded('PXDAC4800_64') + loadlibrary('PXDAC4800_64.dll', 'pxdac4800_wrapper.h'); + end + + + %check if device is present + deviceCount = calllib('PXDAC4800_64','GetDeviceCountXD48'); + if deviceCount == 0 + error('No device present.'); + elseif index>deviceCount + error('Device %i is not present. There are only %i devices.',index,deviceCount); + end + + obj.handle = libpointer('ulongPtr',uint32(0)); + + % connect + obj.library('ConnectToDeviceXD48',... + obj.handle,... + index); + + + temp = libpointer('uint32Ptr', uint32(0)); + obj.library('GetSerialNumberXD48',obj.handle,temp); + obj.serialNumber = get(temp); + obj.serialNumber = obj.serialNumber.Value; + clear('temp'); + + % init with defaults + % obj.resetToPowerupDefault(); + % + obj.activeChannelMask = uint16(15); + + obj.clk = 100e6; + + obj.library('SetTriggerModeXD48',obj.handle,2);%single shot trigger mode + + if ~libisloaded('PXDACMemoryManager') + loadlibrary('C:\Users\humpohl\Documents\Visual Studio 2013\Projects\PXDACMemoryManager\x64\Debug\PXDACMemoryManager.dll','C:\Users\humpohl\Documents\Visual Studio 2013\Projects\PXDACMemoryManager\PXDAC_memory_manager.h') + end + + %initialize memory manager + chunkexponent = 22; %2^22 * sizeof(U16) ~ 8 MB + maxsamples = 2^29; % 1 GB + + %make chunk size smaller as long as allocation fails + %bigger chunks are much faster to upload but are not as easy to + %create for the operating system + status = -1; + while status~=0 + chunksamples = 2^chunkexponent; + + status = calllib('PXDACMemoryManager','initializeU16',obj.handle,uint32(maxsamples),uint32(chunksamples)); + chunkexponent = chunkexponent-1; + + if chunkexponent == 0 + error('can not allocate enouch DMA buffers for PXDAC.'); + end + end + fprintf('Initialized memory manager with 2^%d samples per chunk.\n', chunkexponent); + + + fprintf('%s: successfully connected to %s (your mother) with serial number %d\n',id,class(obj),obj.serialNumber); + end + + registerPulses(self,grp); + + end + + methods + function delete(self) + fprintf('Deleting PXDAC %s\n',self.identifier); + + status = calllib('PXDACMemoryManager','free_memory'); + if status + warning('Failure "%s" while freeing PXDAC manager memory.',PXDAC.statusToErrorMessage(status)); + end + + fprintf('Disconnecting from device...\n') + status = calllib('PXDAC4800_64','DisconnectFromDeviceXD48',self.handle); + if status + warning('Error "%s" while disconnecting from device %d',PXDAC.statusToErrorMessage(status),self.serialNumber); + else + fprintf('Successfully disconnected from %d\n',self.serialNumber); + end + end + + function setChannelMask(self,mask) + if mask~=self.activeChannelMask + warning('Channel mask changed. All stored pulsegroups will be removed.'); + self.clearBoardMemory(); + self.activeChannelMask = mask; + self.library('SetActiveChannelMaskXD48',self.handle,mask); + end + end + + %free board memory to have enough space and return start position + [start, index] = freeEnoughMemory(self,requiredize) + + uploadPulsegroupToCard(self,name) + + + + + + function setActivePulsegroup(self,pulsegroupName) + if isKey(self.storedPulsegroups,pulsegroupName) + self.activePulsegroup = pulsegroupName; + if isempty(self.storedPulsegroups(pulsegroupName).start) + warning('The pulsegroup %s is set active, but is not in PXDAC memory. Make sure to upload the pulsegroup before starting playback.',sequenceName); + end + else + error('Can not activate pulsegroup "%s". Since it is not known.',sequenceName); + end + end + + + % implemented abstact methods + %remove this pulsegroup from memory and forget about it + function removePulseGroup(self,name) + if strcmp(self.activePulsegroup,name) + self.activePulsegroup = 'none'; + end + self.storedPulsegroups.remove(name); + end + + %update the changed pulses + function updatePulseGroup(self,grpdef) + error('notimplemented'); + end + + %wait for trigger + function arm(self) + self.waitForTrigger(); + end + + %this function is for debugging purposes + function issueSoftwareTrigger(self) + error('not implemented'); + end + + + function clearBoardMemory(self) + if ~isempty(self.storedPulsegroups) + self.storedPulsegroups(1:end).start = []; + end + self.activePulsegroup = 'none'; + self.activeChannelMask = uint16(0); + end + + + function waitForTrigger(self) + + if strcmp(self.activePulsegroup,'none') + error('No pulsegroup activated on %s.',self.identifier); + elseif ~isKey(self.storedPulsegroups,self.activePulsegroup) + error('The pulsegroup %s is not known to %s although it is set as active pulsegroup. This hints a bug.',self.activePulsegroup,self.identifier); + end + + self.uploadPulsegroupToCard(self.activePulsegroup); + + pulsegroup = self.storedPulsegroups(self.activePulsegroup); + if pulsegroup.repetitions == Inf + pulsegroup.repetitions = 0; + end + + status = uint32(0); + self.library('GetFPGAStatusXD48',... + self.handle,... + status); + if status > 0 + fprintf('Recieved suspicious status from card.'); + if bitget(status,7) + error('Data playback in progress.') + end + end + + self.library('SetPlaybackClockSourceXD48',self.handle,0); + self.library('SetClockDivider1XD48',self.handle,12); + self.library('SetClockDivider2XD48',self.handle,1); + + + self.library('SetExternalTriggerEnableXD48',... + self.handle,int32(1)); + + self.library('SetTriggerModeXD48',... + self.handle,... + int32(2));% XD48TRIGMODE_SINGLE_SHOT (2) Trigger runs memory data once; subsequent triggers ignored + + + self.library('BeginRamPlaybackXD48',... + self.handle,... + uint32(self.acitveSequence.start),... + uint32(self.acitveSequence.length),... + uint32(self.acitveSequence.length*self.acitveSequence.repetitions)); + end + + function setOutputVoltage(self,channel,ppVoltage) + x = (ppVoltage-self.allowedVoltageRange(1))/(self.allowedVoltageRange(2)-self.allowedVoltageRange(1)); + if x > 1 + error('Voltage %d to large',ppVoltage); + elseif x < 0 + error('Voltage %d to small',ppVoltage); + end + + + self.library(sprintf('SetOutputVoltageCh%iXD48',channel),... + self.handle,... + int32(x*1023)); + end + + function ppVoltage = getOutputVoltage(self,channel) + %output voltage int + ppVoltageInt = calllib('PXDAC4800_64',sprintf('GetOutputVoltageCh%iXD48',channel),... + self.handle,0); + + %convert to double + temp = libpointer('double',zeros(1,1)); + self.library('GetOutputVoltageRangeVoltsXD48',... + ppVoltageInt,... + temp,self.handle) + ppVoltage = temp.Value; + end + + function playbackInProgress = isPlaybackInProgress(self) + libReturn = calllib('PXDAC4800_64','IsPlaybackInProgressXD48', self.handle); + if (libReturn < 0) + error(statusToErrorMessage(libReturn)); + end + playbackInProgress = (libReturn > 0); + end + end + + methods (Static) + function errormsg = statusToErrorMessage(status) + errormsg = calllib('PXDAC4800_64','GetErrorMessXD48',... + status,... + libpointer('stringPtr'),0,libpointer('voidPtr',[])); + error(errormsg); + end + + function testStatus(status) + if status < 0 + error(statusToErrorMessage); + end + end + + function library(fn, varargin) + PXDAC.testStatus( calllib('PXDAC4800_64', fn, varargin{:}) ); + end + end +end \ No newline at end of file diff --git a/@PXDAC/addPulseGroup.m b/@PXDAC/addPulseGroup.m new file mode 100644 index 0000000..9b28b96 --- /dev/null +++ b/@PXDAC/addPulseGroup.m @@ -0,0 +1,57 @@ +function addPulseGroup(self,grpdef) + +if ~isfield(grpdef, 'pulseind') || ~isfield(grpdef, 'repetitions') || ~isfield(grpdef,'pulses') || ~isfield(grpdef,'name') + error('missing field'); +end + +if ~isfield(grpdef,'ctrl') + grpdef.ctrl = ''; +end + +if length(grpdef.nrep)~=length(grpdef.pulseind) + error('dim mismatch'); +end + +% if self.storedPulsegroups.isKey(grpdef.name) +% warning('Ignoring stored pulsegroup %s for now.',grpdef.name); +% return; +% end + + +if isfield(grpdef, 'jump') + % this error should not be ignored since jumps on a TekAWG may be + % executed + error('The only situation where jump makes sense on PXDAC is for reordering. This is not implemented.') +end + +usetrig = isempty(strfind(grpdef.ctrl, 'notrig')); +if usetrig + warning('PXDAC has no trigger capability enabled yet'); +end + +if any(grpdef.nrep == Inf) || any(grpdef.nrep == 0) + error('infinitive loop not supported by PXDAC.'); +end + +% stores/caches the pulsedata in interleaved form for faster upload +self.registerPulses(grpdef); + +npls = size(grpdef.pulseind, 2); +for i = 1:npls + %too stupid to solve without for loop + self.storedPulsegroups(grpdef.name).pulseSequence(i).index = grpdef.pulseind(i); + self.storedPulsegroups(grpdef.name).pulseSequence(i).nrep = grpdef.nrep(i); +end + +% perform the actual upload on board memory +if strfind(grpdef.ctrl,'loop') + repeats = 0; +else + repeats = 1; +end + +self.storedPulsegroups(grpdef.name).repetitions = repeats; + +self.uploadPulsegroupToCard(grpdef.name); + +end \ No newline at end of file diff --git a/@PXDAC/freeEnoughMemory.m b/@PXDAC/freeEnoughMemory.m new file mode 100644 index 0000000..56db097 --- /dev/null +++ b/@PXDAC/freeEnoughMemory.m @@ -0,0 +1,41 @@ +function [start, index] = freeEnoughMemory(self,requiredSize) + uploadedGroups = find( ~isempty(self.storedPulsegroups.mData.start) ); + + %simple algorithm: delete the oldest groups until there is enough + %memory + + if isempty(uploadedGroups) + start = 0; index = 1; + return; + end + + alignment = 2^13; + + while true + start = 0; + index = 1; + for index = 1:uploadedGroups + if self.storedPulsegroups.mData(index).start-start < requiredSize + start = uint32( ceil( double(self.storedPulsegroups.mData(index).start + self.storedPulsegroups.mData(index).start)/double(alignment) ) * alignment ); + else + %found enough free memory + break; + + end + end + + if self.totalMemory - start >= requiredSize + %found enough free memory + break; + end + + oldestGroup = find( storedPulsegroups.mData(uploadedGroups).lastActivation == min(storedPulsegroups.mData(uploadedGroups).lastActivation) ); + + self.storedPulsegroups.mData(oldestGroup).start = []; + uploadedGroups(oldestGroup) = []; + + if isempty(uploadedGroups) + error('This is a bug or the pulsegroup is too large'); + end + end +end \ No newline at end of file diff --git a/@PXDAC/pxdac4800_wrapper.h b/@PXDAC/pxdac4800_wrapper.h new file mode 100644 index 0000000..c8b39d7 --- /dev/null +++ b/@PXDAC/pxdac4800_wrapper.h @@ -0,0 +1,101 @@ +#ifndef PXDAC4800WRAPPER +#define PXDAC4800WRAPPER + +#define AWGAPI + +typedef unsigned long* HXD48; +typedef struct _XD48S_CYCLE_CALC_CTX_tag +{ + unsigned int struct_size; + unsigned int flags; // XD48CYCLECALCF_* + unsigned int max_samples; // 0,Default: Maximal + unsigned int align_override; // 0,Default: 64 bytes + double dDacDataRateMHz; // 0,Default: Board's current + // -- Used when finding closest match + double dSearchAlignMHz; // 0, Default: ~100KHz + double dSearchDeltaMHz; // 0, Default: 128KHz + double dMaxDeviationMHz; // 0, Default: No max + double dClosestPPC; // out: Closest points-per-cycle + double dClosestMHz; // out: Output frequency for match +} XD48S_CYCLE_CALC_CTX; + +char* GetErrorMessXD48(int res, char* bufp, int flags, HXD48 hBrd); + +int GetMaxByteCountForActiveChanMaskXD48 (HXD48 hBrd, int chan_mask, unsigned int* pmax_bytes); + +int AllocateDmaBufferXD48 (HXD48 hBrd, unsigned int bytes, void** bufpp); + +int GetDeviceCountXD48(); + +int ConnectToDeviceXD48(HXD48* phDev, unsigned int brdNum); +int DisconnectFromDeviceXD48 (HXD48 hBrd); + +int GetSerialNumberXD48 (HXD48 hBrd, unsigned int* snp); + +int SetPowerupDefaultsXD48(HXD48 hBrd); + +int SetActiveChannelMaskXD48(HXD48 hBrd, int val); + +int FreeDmaBufferXD48 (HXD48 hBrd, void* bufp); + +int IsDcXD48 (HXD48 hBrd); + +// trigger configuration +int SetTriggerModeXD48(HXD48 hBrd, int val); +int SetExternalTriggerEnableXD48(HXD48 hBrd, int bEnable); + + +// helper functions +int CalculateCycleCountsXD48( + HXD48 hBrd, + double dPtsPerCycle, + unsigned int* pSampleCount, + XD48S_CYCLE_CALC_CTX* ctxp ); // = 0 is valid +int InterleaveData16bit2ChanXD48( + const unsigned short* src_ch1p, + const unsigned short* src_ch2p, + unsigned int samps_per_chan, + unsigned short* dstp); +int InterleaveData16bit4ChanXD48( + const unsigned short* src_ch1p, + const unsigned short* src_ch2p, + const unsigned short* src_ch3p, + const unsigned short* src_ch4p, + unsigned int samps_per_chan); + + +int LoadRamBufXD48( HXD48 hBrd, + unsigned int offset_bytes, + unsigned int length_bytes, + const void* bufp, + int bAsynchronous); +int IssueSoftwareTriggerXD48(HXD48 hBrd); + + +// Channel 1 output voltage; [0, 1023] +int SetOutputVoltageCh1XD48(HXD48 hBrd, int val); +// Channel 1 output voltage; [0, 1023] +int GetOutputVoltageCh1XD48(HXD48 hBrd, int bFromCache); + +// Channel 2 output voltage; [0, 1023] +int SetOutputVoltageCh2XD48(HXD48 hBrd, int val); +// Channel 2 output voltage; [0, 1023] +int GetOutputVoltageCh2XD48(HXD48 hBrd, int bFromCache); + +// Channel 3 output voltage; [0, 1023] +int SetOutputVoltageCh3XD48(HXD48 hBrd, int val); +// Channel 3 output voltage; [0, 1023] +int GetOutputVoltageCh3XD48(HXD48 hBrd, int bFromCache ); + +// Channel 4 output voltage; [0, 1023] +int SetOutputVoltageCh4XD48(HXD48 hBrd, int val); +// Channel 4 output voltage; [0, 1023] +int GetOutputVoltageCh4XD48(HXD48 hBrd, int bFromCache ); + +// Obtain peak-to-peak voltage for given output voltage encoding [0, 1023] +int GetOutputVoltageRangeVoltsXD48(int val, + double* pPeakToPeakVolts, + HXD48 hBrd); + + +#endif diff --git a/@PXDAC/registerPulses.m b/@PXDAC/registerPulses.m new file mode 100644 index 0000000..e08698e --- /dev/null +++ b/@PXDAC/registerPulses.m @@ -0,0 +1,53 @@ +function registerPulses(self,grp) + + usedHWchanels = []; + + for c = grp.chan + if any(self.virtualChannels == c) + usedHWchanels = [usedHWchanels self.getHardwareChannel( self.getHardwareChannel(grp.chan(c))) ]; + end + end + + channelMask = uint16(sum(2.^(usedHWchanels-1))); + + %create pulsegroup object + if ~isKey(self.storedPulsegroups,grp.name) + self.storedPulsegroups.add( PXDACPULSEGROUP(grp.name) ); + end + + dind = find([grp.pulses(1).data.clk] == self.clk); + + %reserve memory (at least i hope so) + self.storedPulsegroups(grp.name).waveformArray = repmat( PXDACPULSE.empty(0,0), 1,length(grp.pulses) ); + + for i = 1:length(grp.pulses) + + + pulse = PXDACPULSE(channelMask,size(grp.pulses(i).data.wf,2)); + + for virtChan = 1:size(grp.pulses(i).data(dind).wf, 1) + + for hardChan = self.getHardwareChannel(grp.chan(virtChan)); + + %map to interval [0,2] + data = ((self.offset(min(hardChan,end)) + grp.pulses(i).data(dind).wf(virtChan, :))./self.scale(hardChan) + 1); + + %convert to uint16 0-2^14-1 + int16wf = uint16(min(... + data*(2^(14-1) - 1),... + 2^(14)-1)); + + pulse.writeToChannel(hardChan,int16wf); + + end + + + end + + self.storedPulsegroups(grp.name).waveformArray(i) = pulse; + + end + + + + end \ No newline at end of file diff --git a/@PXDAC/uploadPulsegroupToCard.m b/@PXDAC/uploadPulsegroupToCard.m new file mode 100644 index 0000000..6ecc5cd --- /dev/null +++ b/@PXDAC/uploadPulsegroupToCard.m @@ -0,0 +1,75 @@ +function uploadPulsegroupToCard(self,name) + + + +if ~ischar(name) + error('No valid sequence name provided'); +end + +if ~isKey(self.storedPulsegroups,name) + error('Pulsegroup %s can not be loaded into memory because it does not exist.',name); +end + +pulsegroup = self.storedPulsegroups(name); +waveformArray = self.storedPulsegroups(name).waveformArray; + +if ~isempty(pulsegroup.start) + if pulsegroup.lastMemoryUpdate > pulsegroup.lastload + fprintf('pulsegroup %s already uploaded.\n',name); + return; + else + fprintf('pulsegroup %s is outdated and will be reloaded.\n',name); + pulsegroup.start = []; + end +end + + +%check mask +if waveformArray(1).channelMask ~= self.activeChannelMask + self.setChannelMask(waveformArray(1).channelMask); +end + + +totalByteSize = [waveformArray(pulsegroup.pulseSequence.index).byteSize]*[pulsegroup.pulseSequence.nrep]'; + + + +%find free coherent space in memory +[start,index] = self.freeEnoughMemory( totalByteSize ); + + +if isempty(index) || isempty(start) + error('Out of board memory. Please erase some sequences from board.'); +end + +% +self.storedPulsegroups.move(name,index); + +self.storedPulsegroups(name).start = start; +self.storedPulsegroups(name).totalByteSize = totalByteSize; + +for playedPulse = pulsegroup.pulseSequence + + pulse = waveformArray(playedPulse.index); + %check channelMask + if pulse.channelMask ~= self.activeChannelMask + error('Channel masks are not consistent.'); + end + wfSize = pulse.byteSize; + + for i = 1:playedPulse.nrep + + calllib('PXDACMemoryManager','writeRAW',... + uint32(start),... + uint32(wfSize),... + pulse.rawData); + + + + start = start + wfSize; + end +end +PXDAC.testStatus( calllib('PXDACMemoryManager','synchronize',false) ); +self.storedPulsegroups(name).lastMemoryUpdate = now; + +end \ No newline at end of file diff --git a/@PXDAC_AC/PXDAC_AC.m b/@PXDAC_AC/PXDAC_AC.m new file mode 100644 index 0000000..6ea35e1 --- /dev/null +++ b/@PXDAC_AC/PXDAC_AC.m @@ -0,0 +1,16 @@ +classdef PXDAC_AC < PXDAC + properties (Constant,GetAccess = public) + allowedVoltageRange = [0.47 1.450]; + end + + methods + function obj = PXDAC_AC(id,index) + obj = obj@PXDAC(id,index); + + %check version + if calllib('PXDAC4800_64','IsDcXD48',obj.handle) + error('Detected a DC coupled card in PXDAC_AC constructor.'); + end + end + end +end \ No newline at end of file diff --git a/@PXDAC_DC/PXDAC_DC.m b/@PXDAC_DC/PXDAC_DC.m new file mode 100644 index 0000000..fa2a7ff --- /dev/null +++ b/@PXDAC_DC/PXDAC_DC.m @@ -0,0 +1,16 @@ +classdef PXDAC_DC < PXDAC + properties (Constant,GetAccess = public) + allowedVoltageRange = [0.400 1.450]; + end + + methods + function obj = PXDAC_DC(id,index) + obj = obj@PXDAC(id,index); + + %check version + if ~calllib('PXDAC4800_64','IsDcXD48',obj.handle) + error('Detected a AC coupled card in PXDAC_DC constructor.'); + end + end + end +end \ No newline at end of file diff --git a/@Tek7082/Tek7082.m b/@Tek7082/Tek7082.m new file mode 100644 index 0000000..b0ad7ab --- /dev/null +++ b/@Tek7082/Tek7082.m @@ -0,0 +1,13 @@ +classdef Tek7082 < TekAWG + properties (Constant,GetAccess = public) + nChannels = 2; + possibleResolutions = 14; + end + + methods + %constructor + function obj = Tek7082(id,handle) + obj = obj@TekAWG(id,handle); + end + end +end \ No newline at end of file diff --git a/@TekAWG/TekAWG.m b/@TekAWG/TekAWG.m new file mode 100644 index 0000000..f02e4f3 --- /dev/null +++ b/@TekAWG/TekAWG.m @@ -0,0 +1,61 @@ +classdef TekAWG < AWG + + properties (GetAccess = public, SetAccess = protected) + handle; + + triglen = 1000; + zeropls; % length of zeropulses stored on the awg + + zerochan; % !!! functinoality not clear + + + seqpulses = []; % !!! functinoality not clear + + waveforms = {}; + end + + methods + % constructor + function obj = TekAWG(id,handle) + obj = obj@AWG(id); + obj.handle = handle; + + class(obj); + + obj.clk = 1.2e9; + + obj.zerochan = ones(1,obj.nChannels); + %check for global awgdata + end + + function last = lastFreeSequenceLine(self) + if isempty(self.storedPulsegroups) + last = 1; + return; + end + + last = self.storedPulsegroups(end).seqind + sum(self.storedPulsegroups(end).nline); + end + + + % implemented abstact methods + %make this pulsegroup playable by AWG + addPulseGroup(self,grpdef); + + %remove this pulsegroup from memory and forget about it + removePulseGroup(self,name); + + %update the changed pulses + updatePulseGroup(self,grpdef); + + %wait for trigger + arm(self); + + %this function is for debugging purposes + issueSoftwareTrigger(self); + + syncwaveforms(self) + + loadwfm(self,data, marker, name, chan,define) + end +end \ No newline at end of file diff --git a/@TekAWG/add.m b/@TekAWG/add.m new file mode 100644 index 0000000..1f1deef --- /dev/null +++ b/@TekAWG/add.m @@ -0,0 +1,278 @@ +function add(self,groups) +% awgadd(groups) +% Add groups to end of sequence. Store group name and target index in +% awgdata.pulsgroups.name, seqind. + +% Group control 'seq' creates sequence combined groups +% ------------------------------------------------------------------------- +% Add groups like: pg.pulses.groups = {'group_1', 'group_2', 'group_4'}; +% At least one of the subgroup names should be the same name as the group +% name itself followed by an underscore and a number, e.g. +% pg.name = 'groups' for the above example of pg.pulses.groups. +% Set group control: pg.ctrl = 'loop seq'; +% Set order of pulses froms groups with pg.pulseind. Subgroups are indexed +% by row, pulses of the group are indexed by column. The value gives the +% number of pulse to use from original subgroup. A zero indicates not to +% use a pulse from the corresponding group in the respective position. For +% 8 pulses per group this might look like: +% pg.pulseind(1,:) = [1:8 zeros(1,16)]; +% pg.pulseind(2,:) = [zeros(1,8) 1:8 zeros(1,8)]; +% pg.pulseind(3,:) = [zeros(1,16) 1:8]; +% ------------------------------------------------------------------------- +% +% (c) 2010 Hendrik Bluhm. Please see LICENSE and COPYRIGHT information in plssetup.m. + +global plsdata; + +% astart=toc; +self.control('clr'); +self.control('stop'); + +if ~iscell(groups) + groups = {groups}; +end + +dosave = false; % keeps track of whether awgdata changed. +% gstart=toc; + + +for k = 1:length(groups) + load([plsdata.grpdir, 'pg_', groups{k}]); + + while plsinfo('stale',groups{k}) + fprintf('Latest pulses of group %s not loaded; %s > %s.\n', groups{k}, ... + datestr(lastupdate), datestr(plslog(end).time(end))); + % tstart=toc; + plsmakegrp(groups{k},'upload'); + % ts2 = toc; + self.control('wait'); + % fprintf('Load time=%f secs, wait time=%f\n',toc-tstart,toc-ts2); + load([plsdata.grpdir, 'pg_', groups{k}]); + end + + if strcmp(grpdef.ctrl(1:min([end find(grpdef.ctrl == ' ', 1)-1])), 'grp')... + && ~isempty(strfind(grpdef.ctrl, 'seq')) % combine groups at sequence level. + + % retrieve channels of component groups + clear chan; + for m = 1:length(grpdef.pulses.groups) + gd=plsinfo('gd', grpdef.pulses.groups{m}); + rf={'varpar', 'pulseind', 'time'}; % Required fields that may be missing + for qq=1:length(rf) + if ~isfield(gd,rf{qq}) + gd=setfield(gd,rf{qq},[]); + end + end + chan(m) = orderfields(gd); + end + chan = {chan.chan}; + seqmerge = true; + else + if ~isfield(grpdef, 'pulseind') + zerolen = plsinfo('zl', grpdef.name); % hack TB + grpdef.pulseind = 1:size(zerolen, 1); + end + + seqmerge = false; + end + + if ~isfield(grpdef, 'nrep') + grpdef.nrep = 1; + end + + + npls = size(grpdef.pulseind, 2); + nchan = self.nChannels; % alternatively use awgdata or data size + usetrig = (grpdef.nrep(1) ~= Inf) && isempty(strfind(grpdef.ctrl, 'notrig')); + + + + if ~iskey(self.storedPulsegroups,grpdef.name) % group is loaded -> update + error('awg.load should create a pulsegroup entry with the lastload time.'); + end + + existentNeededFields = sum( isfield(self.storedPulsegroups(grpdef.name),{'seqind','npulse','nline'}) ); + if ~isempty( self.storedPulsegroups(grpdef.name).seqind ) + + startline = self.storedPulsegroups(grpdef.name).seqind; + if npls + usetrig ~= sum(self.storedPulsegroups(grpdef.name).npulse); + error('Number of pulses changed in group %s. Use awgrm first!', grpdef.name); + end + + if strfind(grpdef.ctrl,'pack') + zlmult = npls; + npls=1; + else + zlmult=1; + end + + if isfield(plslog, 'readout') && exist('zerolen', 'var') + if any(self.storedPulsegroups(grpdef.name).nrep ~= grpdef.nrep) || ... + any(any(self.storedPulsegroups(grpdef.name).readout ~= plslog(end).readout)) || ... + any(any(self.storedPulsegroups(grpdef.name).zerolen ~= zerolen)) % nrep or similar changed + dosave = 1; + end + else + if any(self.storedPulsegroups(grpdef.name).nrep ~= grpdef.nrep) % nrep changed + dosave = 1; + end + end; + + else + % group loaded for the first time + startline = self.lastFreeSequenceLine(); + + self.storedPulsegroups(grpdef.name).seqind = startline; + self.storedPulsegroups(grpdef.name).npulse = [npls usetrig]; + self.storedPulsegroups(grpdef.name).nline = []; + + + if strfind(grpdef.ctrl,'pack') + self.storedPulsegroups(grpdef.name).nline = 1+usetrig; + % Hack alert; way too much code assumes zl == pulselen. For + % packed groups, we ignore it and work out the correct length + % ourselves. + zlmult=npls; + npls=1; + else + zlmult=1; + self.storedPulsegroups(grpdef.name).nline = npls+usetrig; + end + fprintf(self.handle, sprintf('SEQ:LENG %d', startline + self.storedPulsegroups(grpdef.name).nline-1)); + dosave = 1; + + end + + + if ~isfield(grpdef, 'jump') + if strfind(grpdef.ctrl, 'loop') + grpdef.jump = [npls; 1]; + else + grpdef.jump = []; + end + end + + + self.storedPulsegroups(grpdef.name).nrep = grpdef.nrep; + self.storedPulsegroups(grpdef.name).lastload = plslog(end).time(1); + + % Added block below to fix 'seq', where zerolen and plslog(end).readout + % are not available 02.05.2014 PC + if ~exist('zerolen', 'var') + zerolen = []; + for groupInd = 1:length(grpdef.pulses.groups) + tempGrp = load([plsdata.grpdir, 'pg_', grpdef.pulses.groups{groupInd}]); + zerolen = vertcat(zerolen, tempGrp.zerolen); + end + plslog(end).readout = tempGrp.plslog(end).readout; + self.storedPulsegroups(grpdef.name).lastload = now; + clear tempGrp groupInd + end; + + self.storedPulsegroups(grpdef.name).zerolen = zerolen; % Cache some handy stuff here. + self.storedPulsegroups(grpdef.name).readout = plslog(end).readout; + + if usetrig + fprintf(self.handle, sprintf('SEQ:ELEM%d:WAV1 "trig_%08d"', startline, self.triglen)); + for j = 2:nchan + fprintf(self.handle, sprintf('SEQ:ELEM%d:WAV%d "zero_%08d_%d"', startline, j, self.triglen, self.zerochan(j))); + end + if isfield(self,'slave') && ~isempty(self.slave) && (self.slave) + fprintf(self.handle, sprintf('SEQ:ELEM%d:TWAIT 1\n', startline)); + end + end + + + for i = 1:npls + ind = i-1 + startline + usetrig; + if ~seqmerge % pulses combined here. + for j = 1:nchan + ch = j; %self.getHardwareChannel(grpdef.chan(nchan)); + if ~isempty(ch) && zerolen(grpdef.pulseind(i), ch) < 0 + % channel in group and not zero + fprintf(self.handle, sprintf('SEQ:ELEM%d:WAV%d "%s_%05d_%d"', ind, j, ... + grpdef.name, grpdef.pulseind(i), ch)); + else + % hack alert. We should really make zerolen a cell array. fixme. + fprintf(self.handle, sprintf('SEQ:ELEM%d:WAV%d "zero_%08d_%d"', ind, j, ... + zlmult*abs(zerolen(grpdef.pulseind(i), 1)),self.zerochan(j))); % think of a way to make clocks sync + end + end + else % completely overhauled 02.05.2014 PC + + error('implement'); + for m = 1:length(grpdef.pulses.groups) + for j = 1:nchan % channels of component groups + ch = getHardwareChannel(chan{m}); + if grpdef.pulseind(m, i) > 0 % Do not add if pulseind == 0 + if ~isempty(ch) && zerolen(grpdef.pulseind(m, i), ch) < 0 + % channel in group and not zero + fprintf(self.handle, sprintf('SEQ:ELEM%d:WAV%d "%s_%05d_%d"', ind, j, ... + grpdef.pulses.groups{m}, grpdef.pulseind(m, i), ch)); + else + fprintf(self.handle, sprintf('SEQ:ELEM%d:WAV%d "zero_%08d_%d"', ind, j, ... + zlmult*abs(zerolen(grpdef.pulseind(m, i), 1))*self.clk/awgdata(1).clk,self.zerochan(j))); + end + end; + end + end + end + if grpdef.nrep(min(i, end)) == Inf || grpdef.nrep(min(i, end)) == 0 ... + || (i == npls && isempty(strfind(grpdef.ctrl, 'loop')) && (isempty(grpdef.jump) || all(grpdef.jump(1, :) ~= i))) + fprintf(self.handle, sprintf('SEQ:ELEM%d:LOOP:INF 1', ind)); + else + fprintf(self.handle, 'SEQ:ELEM%d:LOOP:INF 0', ind); % default + fprintf(self.handle, sprintf('SEQ:ELEM%d:LOOP:COUN %d', ind, grpdef.nrep(min(i, end)))); + end + + fprintf(self.handle, sprintf('SEQ:ELEM%d:GOTO:STAT 0', ind)); + + if grpdef.nrep(min(i, end)) == Inf && isreal(grpdef.pulses) && ... + (length(self.seqpulses) < ind || self.seqpulses(ind) ~= grpdef.pulses(grpdef.pulseind(i))); + dosave = 1; + self.seqpulses(ind) = grpdef.pulses(grpdef.pulseind(i)); + end + if ~mod(i, 100) + fprintf('%i/%i pulses added.\n', i, npls); + end + end + %fprintf('Group load time: %g secs\n',toc-gstart); + + % jstart=toc; + % event jumps + %SEQ:ELEM%d:JTARget:IND + %SEQ:ELEM%d:JTARget:TYPE + + for j = 1:size(grpdef.jump, 2) + fprintf(self.handle, sprintf('SEQ:ELEM%d:GOTO:IND %d', startline+usetrig-1 + grpdef.jump(:, j))); + fprintf(self.handle, sprintf('SEQ:ELEM%d:GOTO:STAT 1', startline+usetrig-1 + grpdef.jump(1, j))); + end + + if ~exist('seqlog','var') + seqlog.time = now; + else + seqlog(end+1).time = now; + end + seqlog(end).nrep = grpdef.nrep; + seqlog(end).jump = grpdef.jump; + + save([plsdata.grpdir, 'pg_', groups{k}], '-append', 'seqlog'); + %fprintf('Jump program time: %f secs\n',toc-jstart); + % wstart=toc; + self.control('wait'); + %fprintf('Wait time: %f secs; total time %f secs\n',toc-wstart,toc-astart); + nerr=0; + + err=query(self.handle, 'SYST:ERR?'); + if ~isempty(strfind(err, 'No error')) + nerr=nerr+1; + end + + if nerr == 0 + fprintf('Added group %s on index %i. %s', grpdef.name, gind, err); + logentry('Added group %s on index %i.', grpdef.name, gind); + end +end +if dosave + self.savedata; +end \ No newline at end of file diff --git a/@TekAWG/addPulseGroup.m b/@TekAWG/addPulseGroup.m new file mode 100644 index 0000000..372b0c5 --- /dev/null +++ b/@TekAWG/addPulseGroup.m @@ -0,0 +1,128 @@ +function addPulseGroup(self,grpdef) + self.loadPulsesOfGroup(grpdef); + + + npls = size(grpdef.pulseind, 2); + + + + usetrig = isempty(strfind(grpdef.ctrl, 'notrig')); + + + if ~isempty( self.storedPulsegroups(grpdef.name).seqind ) + + startline = self.storedPulsegroups(grpdef.name).seqind; + if npls + usetrig ~= sum(self.storedPulsegroups(grpdef.name).npulse); + error('Number of pulses changed in group %s. Use awgrm first!', grpdef.name); + end + + + if isfield(plslog, 'readout') && exist('zerolen', 'var') + if any(self.storedPulsegroups(grpdef.name).nrep ~= grpdef.nrep) || ... + any(any(self.storedPulsegroups(grpdef.name).readout ~= plslog(end).readout)) || ... + any(any(self.storedPulsegroups(grpdef.name).zerolen ~= zerolen)) % nrep or similar changed + dosave = 1; + end + else + if any(self.storedPulsegroups(grpdef.name).nrep ~= grpdef.nrep) % nrep changed + dosave = 1; + end + end; + + else + % group loaded for the first time + startline = self.lastFreeSequenceLine(); + + self.storedPulsegroups(grpdef.name).seqind = startline; + self.storedPulsegroups(grpdef.name).npulse = [npls usetrig]; + self.storedPulsegroups(grpdef.name).nline = npls+usetrig; + + fprintf(self.handle, sprintf('SEQ:LENG %d', startline + self.storedPulsegroups(grpdef.name).nline-1)); + dosave = 1; + + end + + %insert nrep logic here + loop = strfind(grpdef.ctrl, 'loop'); + + self.storedPulsegroups(grpdef.name).nrep = grpdef.repetitions; + + if usetrig + fprintf(self.handle, sprintf('SEQ:ELEM%d:WAV1 "trig_%08d"', startline, self.triglen)); + for j = 2:nchan + fprintf(self.handle, sprintf('SEQ:ELEM%d:WAV%d "zero_%08d_%d"', startline, j, self.triglen, self.zerochan(j))); + end + if isfield(self,'slave') && ~isempty(self.slave) && (self.slave) + fprintf(self.handle, sprintf('SEQ:ELEM%d:TWAIT 1\n', startline)); + end + end + + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% continue work here + + for i = 1:npls + ind = i-1 + startline + usetrig; + + for ch = 1:nchan + if ~isempty(ch) && zerolen(grpdef.pulseind(i), ch) < 0 + % channel in group and not zero + fprintf(self.handle, sprintf('SEQ:ELEM%d:WAV%d "%s_%05d_%d"', ind, j, ... + grpdef.name, grpdef.pulseind(i), ch)); + else + % hack alert. We should really make zerolen a cell array. fixme. + fprintf(self.handle, sprintf('SEQ:ELEM%d:WAV%d "zero_%08d_%d"', ind, j, ... + abs(zerolen(grpdef.pulseind(i), 1)),self.zerochan(j))); % think of a way to make clocks sync + end + end + + if grpdef.nrep(min(i, end)) == Inf || grpdef.nrep(min(i, end)) == 0 ... + || (i == npls && isempty(strfind(grpdef.ctrl, 'loop')) && (isempty(grpdef.jump) || all(grpdef.jump(1, :) ~= i))) + fprintf(self.handle, sprintf('SEQ:ELEM%d:LOOP:INF 1', ind)); + else + fprintf(self.handle, 'SEQ:ELEM%d:LOOP:INF 0', ind); % default + fprintf(self.handle, sprintf('SEQ:ELEM%d:LOOP:COUN %d', ind, grpdef.nrep(min(i, end)))); + end + + fprintf(self.handle, sprintf('SEQ:ELEM%d:GOTO:STAT 0', ind)); + + if grpdef.nrep(min(i, end)) == Inf && isreal(grpdef.pulses) && ... + (length(self.seqpulses) < ind || self.seqpulses(ind) ~= grpdef.pulses(grpdef.pulseind(i))); + dosave = 1; + self.seqpulses(ind) = grpdef.pulses(grpdef.pulseind(i)); + end + if ~mod(i, 100) + fprintf('%i/%i pulses added.\n', i, npls); + end + end + %fprintf('Group load time: %g secs\n',toc-gstart); + + % jstart=toc; + % event jumps + %SEQ:ELEM%d:JTARget:IND + %SEQ:ELEM%d:JTARget:TYPE + + for j = 1:size(grpdef.jump, 2) + fprintf(self.handle, sprintf('SEQ:ELEM%d:GOTO:IND %d', startline+usetrig-1 + grpdef.jump(:, j))); + fprintf(self.handle, sprintf('SEQ:ELEM%d:GOTO:STAT 1', startline+usetrig-1 + grpdef.jump(1, j))); + end + + if ~exist('seqlog','var') + seqlog.time = now; + else + seqlog(end+1).time = now; + end + seqlog(end).nrep = grpdef.nrep; + seqlog(end).jump = grpdef.jump; + + save([plsdata.grpdir, 'pg_', groups{k}], '-append', 'seqlog'); + %fprintf('Jump program time: %f secs\n',toc-jstart); + % wstart=toc; + self.control('wait'); + %fprintf('Wait time: %f secs; total time %f secs\n',toc-wstart,toc-astart); + nerr=0; + + err=query(self.handle, 'SYST:ERR?'); + if ~isempty(strfind(err, 'No error')) + nerr=nerr+1; + end + +end \ No newline at end of file diff --git a/@TekAWG/control.m b/@TekAWG/control.m new file mode 100644 index 0000000..8cfaa13 --- /dev/null +++ b/@TekAWG/control.m @@ -0,0 +1,154 @@ +function val = control(self,cntrl, chans) +% awgcntrl(cntrl, chans) +% cntrl: stop, start, on off, wait, raw|amp, israw, extoff|exton, isexton, err, clr +% several commands given are processed in order. +% isamp and isexton return a vector of length chans specifying which are +% amp or in exton mode + +% (c) 2010 Hendrik Bluhm. Please see LICENSE and COPYRIGHT information in plssetup.m. + +val=[]; +if nargin <2 + chans = []; +end + +breaks = [regexp(cntrl, '\<\w'); regexp(cntrl, '\w\>')]; + +for k = 1:size(breaks, 2); + switch cntrl(breaks(1, k):breaks(2, k)) + case 'stop' + fprintf(self.handle, 'AWGC:STOP'); + + + case 'start' + fprintf(self.handle, 'AWGC:RUN'); + awgcntrl('wait'); + + case 'off' + + for i = ch(self, chans) %%%%%%%%%%%%%%%%%%%%%% hä? + fprintf(self.handle, 'OUTPUT%i:STAT 0', i); + end + + case 'on' + for i = ch(self, chans) + fprintf(self.handle, 'OUTPUT%i:STAT 1', i); + end + + + + case 'wait' + to = self.handle.timeout; + self.handle.timeout = 600; + query(self.handle, '*OPC?'); + self.handle.timeout = to; + + + case 'raw' + if any(any(~self.control('israw'))) + + if ~is7k(self) + for i = ch(self, chans) + fprintf(self.handle, 'AWGC:DOUT%i:STAT 1', i); + end + end + + else + fprintf('Already raw\n'); + end + + case 'amp' + if any(any(awg.control('israw'))) + + if ~is7k(self) + for i = ch(self, chans) + fprintf(self.handle, 'AWGC:DOUT%i:STAT 0', i); + end + end + + else + fprintf('Already amp\n'); + end + + case 'israw' + + val=[]; + if ~is7k(self) + for i = ch(self, chans) + fprintf(self.handle, 'AWGC:DOUT%i:STAT?',i); + val(end+1) = fscanf(self.handle,'%f'); + end + end + + case 'exton' %adds external DC to outputs specified in chans + + if ~is7k(self) + for i = ch(self,chans) + fprintf(self.handle, 'SOUR%i:COMB:FEED "ESIG"', i); + end + end + + + case 'extoff' %turns off external DC + + if ~is7k(self) + for i = ch(self,chans) + fprintf(self.handle, 'SOUR%i:COMB:FEED ""', i); + end + end + + case 'isexton' + val=[]; + if ~is7k(self) + for i = ch(self, chans) + + fprintf(self.handle, 'SOUR%i:COMB:FEED?',i); + val(end+1) = strcmp(fscanf(self.handle, '%f'), 'ESIG'); + end + end + + case 'err' + err=query(self.handle, 'SYST:ERR?'); + if strcmp(err(1:end-1), '0,"No error"') + % Supress blank error messages. + else + fprintf('%d: %s\n',a,err); + end + + + case 'clr' + i = 0; + err2 = sprintf('n/a.\n'); + while 1 + err = query(self.handle, 'SYST:ERR?'); + if strcmp(err(1:end-1), '0,"No error"') + if i > 0 + fprintf('%s: %i errors. Last %s', self.identifier, i, err2); + end + break; + end + err2 = err; + i = i + 1; + end + + case 'norm' + for i = 1:4 + fprintf(self.handle, 'SOUR%i:VOLT:AMPL .6', i); + end + case 'dbl' + for i = 1:4 + fprintf(self.handle, 'SOUR%i:VOLT:AMPL 1.2', i); + end + end + end +end + +function chans=ch(awg, chans) + if isempty(chans) + chans=1:length(awg.channelMap); + end +end + +function val=is7k(awg) + val=length(awg.channelMap) <= 2; +end \ No newline at end of file diff --git a/@TekAWG/erase.m b/@TekAWG/erase.m new file mode 100644 index 0000000..9ede804 --- /dev/null +++ b/@TekAWG/erase.m @@ -0,0 +1,96 @@ +function erase(self,groups,options) +% awgclear(groups) +% OR +% awgclear('all') +% awgclear('pack') removes all groups, adds back groups loaded in sequences +% awgclear('all','paranoid') removes all waveforms, including those not known to be loaded. +% awgclear('pack','paranoid') similar + +% (c) 2010 Hendrik Bluhm. Please see LICENSE and COPYRIGHT information in plssetup.m. + +if ~exist('options','var') + options=''; +end +global plsdata; + +if strcmp(groups, 'pack') + grps={self.pulsegroups.name}; + self.erase('all',options); + self.add(grps); + return; +end + +if strcmp(groups, 'all') + % Mark only groups known to be loaded as loaded. + if isempty(strfind(options,'paranoid')) + g=self.knownwaveforms(); + else % Mark all pulse groups as not loaded + g=plsinfo('ls'); + end + + fprintf(self.handle,'WLIS:WAV:DEL ALL\n') + self.zeropls=[]; + + + logentry('Cleared all pulses.'); + for i=1:length(g) + load([plsdata.grpdir, 'pg_', g{i}, '.mat'], 'plslog'); + if(plslog(end).time(end) <= 0) + fprintf('Skipping group ''%s''; already unloaded\n',g{i}); + else + plslog(end).time(end+1) = -now; + save([plsdata.grpdir, 'pg_', g{i}, '.mat'], 'plslog','-append'); + fprintf('Marking group ''%s'' as unloaded\n',g{i}); + end + end + self.rm('all'); + return; +end + +if strcmp(groups,'unused') + g=self.waf; + g2={self.pulsegroups.name}; + groups=setdiff(g,g2); + for i=1:length(groups) + fprintf('Unloading %s\n',groups{i}); + end +end + +if ischar(groups) + groups = {groups}; +end +tic; + +if isreal(groups) + groups = sort(groups, 'descend'); + for i = groups + wf = query(self.awg, sprintf('WLIS:NAME? %d', i)); + if ~query(self.awg, sprintf('WLIS:WAV:PRED? %s', wf), '%s\n', '%i') + fprintf(self.awg, 'WLIS:WAV:DEL %s', wf); + end + if toc > 20 + fprintf('%i/%i\n', i, length(groups)); + tic; + end + end + self.control('wait'); + return; +end + + +for k = 1:length(groups) + load([plsdata.grpdir, 'pg_', groups{k}], 'plslog'); + + wfms=self.knownwaveforms(groups{k},'delete'); + for i=1:length(wfms) + fprintf(self.handle, sprintf('WLIS:WAV:DEL "%s"', wfms{i})); + end + + + plslog(end).time(end+1) = -now; + save([plsdata.grpdir, 'pg_', groups{k}], '-append', 'plslog'); + logentry('Cleared group %s.', groups{k}); + fprintf('Cleared group %s.\n', groups{k}); + + self.rm(groups{k}); +end diff --git a/@TekAWG/load.m b/@TekAWG/load.m new file mode 100644 index 0000000..024fb9e --- /dev/null +++ b/@TekAWG/load.m @@ -0,0 +1,114 @@ +function load(self,grp,ind) +fprintf('awg.load\n') +% zerolen = awgload(grp) +% load pulses from group to AWG. + +% (c) 2010 Hendrik Bluhm. Please see LICENSE and COPYRIGHT information in plssetup.m. + +self.control('stop'); %changing pulses while running is slow. +self.syncwaveforms(); % make sure the waveform list is up-to-date. + +dind=find([grp.pulses(1).data.clk] == self.clk); + + +% fixme; emit an error if this changes zerochan and wlist.size ~= 25. +% The screwiness here is to get each channel with a unique offset/scale combo +[offsets offsetchan self.zerochan] = unique(self.offset./self.scale); +offsets=self.offset(offsetchan); + +dosave = false; + +% create trig pulse (and corresponding 0) if waveform list empty. +if query(self.handle, 'WLIS:SIZE?', '%s\n', '%i') == 25 % nothing loaded (except predefined) + zdata=zeros(1,self.triglen); + zmarker=repmat(1,1,self.triglen); + self.loadwfm(zdata,zmarker,sprintf('trig_%08d',self.triglen),1,1); + + for l=1:length(offsets) + self.loadwfm(zdata,zmarker,sprintf('zero_%08d_%d',self.triglen,l),offsetchan(l),1); + end + dosave = true; + self.zeropls = self.triglen; +end + + + +nzpls=0; + + + +for i = 1:length(grp.pulses) + npts = size(grp.pulses(i).data(dind).wf, 2); + if ~any(self.zeropls == npts) % create zero if not existing yet + zdata=zeros(1,npts); + for l=1:length(offsets) + zname=sprintf('zero_%08d_%d', npts, l); + self.loadwfm(zdata,zdata,zname,offsetchan(l),1); + end + zdata=[]; + self.zeropls(end+1) = npts; + dosave = true; + end + + for j = 1:size(grp.pulses(i).data(dind).wf, 1) + + ch = self.getHardwareChannel(grp.chan(j)); + + %virtual channels not belonging to this AWG + if isempty(ch) + continue; + end + + % data of channel 2 is uploaded seperatly and not as a zeropulse + if any(abs(grp.pulses(i).data(dind).wf(j, :)) > self.scale(ch)/(2^self.resolution)) || any(grp.pulses(i).data(dind).marker(j,:) ~= 0) + name = sprintf('%s_%05d_%d', grp.name, ind(i), j); + + if isempty(strmatch(name,self.waveforms)) + fprintf(self.handle, sprintf('WLIS:WAV:NEW "%s",%d,INT', name, npts)); + self.waveforms{end+1}=name; + err = query(self.handle, 'SYST:ERR?'); + if ~isempty(strfind(err,'E11113')) + fprintf(err(1:end-1)); + error('Error loading waveform; AWG is out of memory. Try awgclear(''all''); '); + end + end + self.loadwfm(grp.pulses(i).data(dind).wf(j,:), uint16(grp.pulses(i).data(dind).marker(j,:)), name, ch, 0); + zerolen(ind(i), j) = -npts; + nzpls=1; + else + zerolen(ind(i), j) = npts; + end + end +end + +% If no non-zero pulses were loaded, make a dummy waveform so awgclear +% knows this group was in memory. +% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%consider removoing +if nzpls == 0 + name=sprintf('%s_1_1',grp.name); + npts=256; + if isempty(strmatch(name,self.waveforms)) + fprintf(self.handle, sprintf('WLIS:WAV:NEW "%s",%d,INT', name, npts)); + self.waveforms{end+1}=name; + end + self.loadwfm(zeros(1,npts), zeros(1,npts), name, 1, 0); +end + +if ~isKey(self.storedPulsegroups,grp.name) + self.storedPulsegroups.add( TekPULSEGROUP(grp.name) ); +end + +%update it's load time. +self.storedPulsegroups(grp.name).lastload = now; + + +end + +if dosave + self.savedata; +end +end + + + + diff --git a/@TekAWG/loadPulsesOfGroup.m b/@TekAWG/loadPulsesOfGroup.m new file mode 100644 index 0000000..a695210 --- /dev/null +++ b/@TekAWG/loadPulsesOfGroup.m @@ -0,0 +1,107 @@ +function loadPulsesOfGroup(self,grp) + +% load pulses from group to AWG. +self.control('stop'); %changing pulses while running is slow. +self.syncwaveforms(); % make sure the waveform list is up-to-date. + +dind=find([grp.pulses(1).data.clk] == self.clk); + + +% fixme; emit an error if this changes zerochan and wlist.size ~= 25. +% The screwiness here is to get each channel with a unique offset/scale combo +[offsets offsetchan self.zerochan] = unique(self.offset./self.scale); +offsets=self.offset(offsetchan); + +dosave = false; + +% create trig pulse (and corresponding 0) if waveform list empty. +if query(self.handle, 'WLIS:SIZE?', '%s\n', '%i') == 25 % nothing loaded (except predefined) + zdata=zeros(1,self.triglen); + zmarker=ones(1,self.triglen); + self.loadwfm(zdata,zmarker,sprintf('trig_%08d',self.triglen),1,1); + + for l=1:length(offsets) + self.loadwfm(zdata,zmarker,sprintf('zero_%08d_%d',self.triglen,l),offsetchan(l),1); + end + dosave = true; + self.zeropls = self.triglen; +end + + + +nonzeropls = false; + +zerolen = zeros(length(grp.pulses), size(grp.pulses.data.wf, 2) ); + +for i = 1:length(grp.pulses) + npts = size(grp.pulses(i).data(dind).wf, 2); + if ~any(self.zeropls == npts) % create zero if not existing yet + zdata=zeros(1,npts); + for l=1:length(offsets) + zname=sprintf('zero_%08d_%d', npts, l); + self.loadwfm(zdata,zdata,zname,offsetchan(l),1); + end + self.zeropls(end+1) = npts; + dosave = true; + end + + for j = 1:size(grp.pulses(i).data(dind).wf, 1) + + ch = self.getHardwareChannel(grp.chan(j)); + + %virtual channels not belonging to this AWG + if isempty(ch) + continue; + end + + % data of channel 2 is uploaded seperatly and not as a zeropulse + if any(abs(grp.pulses(i).data(dind).wf(j, :)) > self.scale(ch)/(2^self.resolution)) || any(grp.pulses(i).data(dind).marker(j,:) ~= 0) + name = sprintf('%s_%05d_%d', grp.name, ind(i), j); + + if isempty(strmatch(name,self.waveforms)) + fprintf(self.handle, sprintf('WLIS:WAV:NEW "%s",%d,INT', name, npts)); + self.waveforms{end+1}=name; + err = query(self.handle, 'SYST:ERR?'); + if ~isempty(strfind(err,'E11113')) + fprintf(err(1:end-1)); + error('Error loading waveform; AWG is out of memory. Try awg.clear(''all''); '); + end + end + self.loadwfm(grp.pulses(i).data(dind).wf(j,:), uint16(grp.pulses(i).data(dind).marker(j,:)), name, ch, 0); + nonzeropls = true; + zerolen(i,ch) = npts; + else + zerolen(i,ch) = -npts; + end + end +end + +% If no non-zero pulses were loaded, make a dummy waveform so awgclear +% knows this group was in memory. +% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%consider removoing +if nonzeropls + name=sprintf('%s_1_1',grp.name); + npts=256; + if isempty(strmatch(name,self.waveforms)) + fprintf(self.handle, sprintf('WLIS:WAV:NEW "%s",%d,INT', name, npts)); + self.waveforms{end+1}=name; + end + self.loadwfm(zeros(1,npts), zeros(1,npts), name, 1, 0); +end + + +%update it's load time. +if isKey(self.storedPulsegroups,grp.name) + %Only one level of indexing is supported by a containers.Map... + self.storedPulsegroups(grp.name).lastload = now; +else + self.storedPulsegroups.add( TekPULSEGROUP(grp.name) ); +end + +self.storedPulsegroups.zerolen = zerolen; + +if dosave + self.savedata; +end + +end \ No newline at end of file diff --git a/@TekAWG/loadwfm.m b/@TekAWG/loadwfm.m new file mode 100644 index 0000000..a7cd747 --- /dev/null +++ b/@TekAWG/loadwfm.m @@ -0,0 +1,35 @@ +function loadwfm(self,data, marker, name, chan,define) +% Send waveform 'data,marker' to the awg with name 'name' intended for channel c. +% data is scaled and offset by awgdata.scale and awgdata.offset *before* sending. +start = now; +if exist('define','var') && define + fprintf(self.handle, sprintf('WLIS:WAV:NEW "%s",%d,INT', name, length(data))); + self.waveforms{end+1}=name; +end +chunksize=65536; +if(size(data,1) > size(data,2)) + data=data'; +end + data=(self.offset(min(chan,end)) + data)./self.scale(chan) + 1; + tb=find(data > 2); + tl=find(data < 0); + if ~isempty(tb) || ~isempty(tl) + % fprintf('Pulse exceeds allowed range: %g - %g\n',min(data),max(data)); + data(tb) = 2; + data(tl) = 0; + end % 14 bit data offset is hard-coded in the AWG. + data = uint16(min(data*(2^(14-1) - 1), 2^(14)-1)) + uint16(marker) * 2^14; + npts = length(data); + for os=0:chunksize:npts + if os + chunksize >= npts + fwrite(self.handle, [sprintf('WLIS:WAV:DATA "%s",%d,%d,#7%07d', name, os, npts-os,2 * (npts-os)),... + typecast(data((os+1):end), 'uint8')]); + else + fwrite(self.handle, [sprintf('WLIS:WAV:DATA "%s",%d,%d,#7%07d', name, os, chunksize,2 * chunksize),... + typecast(data((os+1):(os+chunksize)), 'uint8')]); + end + fprintf(self.handle,''); + end + time=(now-start)*24*60*60; + fprintf('Load time for pulse %s: %g seconds for %g points (%g bytes/sec)\n',name,time, npts,npts*2/time); +end \ No newline at end of file diff --git a/@TekAWG/rm.m b/@TekAWG/rm.m new file mode 100644 index 0000000..3839fe7 --- /dev/null +++ b/@TekAWG/rm.m @@ -0,0 +1,46 @@ +function rm(self,grp, ctrl) +% awgrm(grp, ctrl) +% grp: 'all' or group name +% ctrl: 'after' remove all following groups +% (otherwise, specified group is removed by removing it and following ones +% and then reloading the latter. + +% (c) 2010 Hendrik Bluhm. Please see LICENSE and COPYRIGHT information in plssetup.m. + + +if strcmp(grp, 'all') + self.control('stop'); + + fprintf(self.handle, 'SEQ:LENG 0'); + self.pulsegroups = []; + self.seqpulses = []; + + self.savedata(); + return; +end + +grp = self.grpind(grp); %strmatch(grp, strvcat(awgdata.pulsegroups.name), 'exact'); +grp(2:end) = []; +if isnan(grp) + return; +end + +self.control('stop'); + +if exist('ctrl','var') && strfind(ctrl, 'after') + + fprintf(self.handle, 'SEQ:LENG %d', self.pulsegroups(grp).seqind-1 + sum(self.pulsegroups(grp).nline)); + self.seqpulses(self.pulsegroups(grp).seqind + sum(self.pulsegroups(grp).npulse):end) = []; + self.pulsegroups(grp+1:end) = []; + + % may miss trigger line. + return; +end + +fprintf(self.handle, 'SEQ:LENG %d', self.pulsegroups(grp).seqind-1); +self.seqpulses(self.pulsegroups(grp).seqind:end) = []; +groups = {self.pulsegroups(grp+1:end).name}; +self.pulsegroups(grp:end) = []; + +% log unloading here if necessary +self.add(groups); diff --git a/@TekAWG/syncwaveforms.m b/@TekAWG/syncwaveforms.m new file mode 100644 index 0000000..13112d1 --- /dev/null +++ b/@TekAWG/syncwaveforms.m @@ -0,0 +1,18 @@ +function syncwaveforms(self) +% Make sure the list of pulses in awgdata is consistent with the awg. +% we assume if the number of pulses is right, everything is.\ +self.control('clr'); + + + npls=str2num(query(self.handle,'WLIS:SIZE?')); + if (length(self.waveforms) == npls) + return; + end + fprintf('TekAWG %s waveform list out of date. Syncing.',self.identifier); + self.waveforms=cell(npls,1); + for l=1:npls + r=query(self.handle,sprintf('WLIS:NAME? %d',l-1)); + self.waveforms{l}=r(2:end-2); %-2 since communication adds newline at end + end + fprintf('.. Done.\n'); +end diff --git a/@TekAWG/upload.m b/@TekAWG/upload.m new file mode 100644 index 0000000..1dc36be --- /dev/null +++ b/@TekAWG/upload.m @@ -0,0 +1,111 @@ +function upload(self,name) + +global plsdata; + + +if ~iscell(name) + name = {name}; +end + +ind = []; +% if ~exist('opts','var') +% opts=struct(); +% end +% +% opts=def(opts,'time',[]); + +for k = 1:length(name) + + if(~isstruct(name{k})) + zerolen = []; % avoid using zerolen from previous group. + plslog=[]; + load([plsdata.grpdir, 'pg_', name{k}]); + else + grpdef=name{k}; + end + + + if exist('plslog','var') && length(plslog) > 100 + fprintf('Group %s has %d log entries.\n',name{k},length(plslog)); + end +% if exist('plslog','var') && ~isempty(plslog) && ~isempty(opts.time) +% le=plsinfo_logentry(plslog,opts.time); +% grpdef.params=plslog(le).params; +% grpdef.matrix=plslog(le).matrix; +% grpdef.varpar=plslog(le).varpar; +% grpdef.offset=plslog(le).offset; +% grpdef.dict=plslog(le).dict; +% grpdef.readout=plslog(le).readout; +% end + + + pack = false;%~isempty(strfind(grpdef.ctrl,'pack')); + + if ~ isfield(grpdef, 'varpar') + grpdef.varpar = []; + end + if ~ isfield(grpdef, 'params') + grpdef.params = []; + end + if isfield(grpdef, 'time')&& ~isempty(grpdef.time) + fprintf('Ignoring opts.time\n'); + opts.time=grpdef.time; + + end + + if plsinfo('stale',grpdef.name) + % modified since last upload + + + + + % Actually handle the upload... + zerolen = self.load(grpdef, ind); + + + % save update time in log. + plslog(end+1).time = now; + plslog(end).params = grpdef.params; + plslog(end).matrix = grpdef.matrix; + plslog(end).varpar = grpdef.varpar; + plslog(end).offset = grpdef.offset; + + if isfield(grpdef.pulses(1).data,'readout') + readout=[]; + for ll=1:length(grpdef.pulses) + if ~isempty(grpdef.pulses(ll).data(1).readout) + readout(:,:,ll) = grpdef.pulses(ll).data(1).readout; + end + end + if any(abs(diff(readout,[],3)) > 1e-10) + warning('Readout changes between pulses in %s\n',name{k}); + end + if(size(readout,1) > 0) + plslog(end).readout = readout(:,:,1); + else + plslog(end).readout=[]; + end + end + + if isfield(grpdef, 'dict') + plslog(end).dict = grpdef.dict; + end + + if isfield(grpdef, 'trafofn') + plslog(end).trafofn = grpdef.trafofn; + end + + if length(plslog) > 2 % copy in case not all pulses updated. First plslog has no xval + plslog(end).xval = plslog(end-1).xval; + end + %plslog(end).xval(:, ind) = vertcat(grpdef.pulses.xval)'; + plslog(end).xval = vertcat(grpdef.pulses.xval)'; % temporary bug fix + plslog(end).ind = ind; + + save([plsdata.grpdir, 'pg_', name{k}], '-append','-v6', 'plslog', 'zerolen'); + logentry('Uploaded group %s, revisions %i.', grpdef.name, length(plslog)); + % fprintf(' in upload of group %s.\n', grpdef.name); + else + fprintf('Skipping group %s.\n', grpdef.name); + end +end \ No newline at end of file diff --git a/@VAWG/VAWG.m b/@VAWG/VAWG.m new file mode 100644 index 0000000..12aa4e2 --- /dev/null +++ b/@VAWG/VAWG.m @@ -0,0 +1,148 @@ +classdef VAWG < handle + properties (SetAccess = protected, GetAccess = public) + awgs; + + %channel mapping + virtualToHardware = {}; + + %trigger length in nanoseconds + triggerLength = 4000; + end + + methods + add(self,groups); + + function zerolen = zero(self,grp,ind,zerolen) + for awg = 1:length(self.awgs) + zerolen = self.awgs(awg).zeroLength(grp,ind,zerolen); + end + + end + + function index = addAWG(self,awg) + if ~isa(awg,'AWG') + error('Object is no AWG.'); + else + if ~isempty(self.awgs) + if find( strcmp({self.awgs.identifier},awg.identifier) ) + error('AWG with identifier %s already exists.',awg.identifier); + end + end + + self.awgs = [self.awgs(:) awg]; + index = length(self.awgs); + end + end + + function removeAWG(self,awg) + index = self.getIndex(awg); + + self.awgs(index).virtualChannels = []; + + for virt = 1:length(self.virtualToHardware) + todelete = []; + for entry = 1:length( self.virtualToHardware{virt} ) + if self.virtualToHardware{virt}{entry}(1) == index + todelete(end+1) = entry; + end + end + self.virtualToHardware{virt}(todelete) = []; + end + + + self.awgs(index) = []; + end + + function index = getIndex(self,awg) + if isa(awg,'AWG') + index = find( eq(awg,self.awgs) ); + elseif ischar(awg) + index = find( strcmp({self.awgs.identifier},awg) ); + elseif isinteger(awg) || isfloat(awg) + index = awg; + else + error('Recieved an invalid type to determine index.'); + end + + if length(self.awgs) self.awgs(index).nChannels + error('AWG %s only has %i hardware channels. Requested was %i',self.awgs(index).nChannels,hardware); + end + + if size(self.virtualToHardware,2) < virtual + self.virtualToHardware{virtual}{1} = [index hardware]; + else + self.virtualToHardware{virtual}{end+1} = [index hardware]; + end + + self.awgs(index).virtualChannels(hardware) = virtual; + end + + function removeVirtualChannel(self,virtualChannel) + if size(self.virtualToHardWare,2)0; + + if sum(activePlaybacks) ~= 0 && sum(activePlaybacks) ~= length(self.awgs) + warning('One AWGs is still playing while another one has finished!'); + end + end + + function setTriggerLength(self,triglen) + self.triggerLength = triglen; + end + function triglen = getTriggerLength(self) + triglen = self.triggerLength; + end + end +end \ No newline at end of file diff --git a/@VAWG/add.m b/@VAWG/add.m new file mode 100644 index 0000000..4946cb7 --- /dev/null +++ b/@VAWG/add.m @@ -0,0 +1,195 @@ +function add(self,groups) +% awgadd(groups) +% Add groups to end of sequence. Store group name and target index in +% awgdata.pulsgroups.name, seqind. + +% Group control 'seq' creates sequence combined groups +% ------------------------------------------------------------------------- +% Add groups like: pg.pulses.groups = {'group_1', 'group_2', 'group_4'}; +% Give the container group a regular expression name, that matches all +% subgroups (and ONLY them, not any other group on the AWG), like +% 'tomo_[A-Za-z0-9]{7}' +% +% Set order of pulses froms groups with pg.pulseind. Subgroups are indexed +% by row, pulses of the group are indexed by column. The value gives the +% number of pulse to use from original subgroup. A zero indicates not to +% use a pulse from the corresponding group in the respective position. For +% 8 pulses per group this might look like: +% +% pg.pulseind(1,:) = [1:8 zeros(1,16)]; +% pg.pulseind(2,:) = [zeros(1,8) 1:8 zeros(1,8)]; +% pg.pulseind(3,:) = [zeros(1,16) 1:8]; +% ------------------------------------------------------------------------- + +% Group control 'loop seq pack' creates sequence combined groups. Each +% subgroup will be one single waveform. Each subgroup needs to have group +% control 'loop pack' +% ------------------------------------------------------------------------- +% As 'seq' but pg.pulseind of the container group references the +% subgroups (since each of the subgroups will be one waveform later). +% ------------------------------------------------------------------------- +% +% (c) 2010 Hendrik Bluhm. Please see LICENSE and COPYRIGHT information in plssetup.m. + +if ~iscell(groups) + groups = {groups}; +end + +for k = 1:length(groups) + + + %updates groupdef + grpdef = makeGroupDef(groups{k},'uploadsimulation'); + + + + if strcmp(grpdef.ctrl(1:min([end find(grpdef.ctrl == ' ', 1)-1])), 'grp')... + && ~isempty(strfind(grpdef.ctrl, 'seq')) % combine groups at sequence level. + % retrieve channels of component groups + clear chan; + for m = 1:length(grpdef.pulses.groups) + gd=plsinfo('gd', grpdef.pulses.groups{m}); + rf={'varpar', 'pulseind', 'time'}; % Required fields that may be missing + for qq=1:length(rf) + if ~isfield(gd,rf{qq}) + gd=setfield(gd,rf{qq},[]); + end + end + chan(m) = orderfields(gd); + end + chan = {chan.chan}; + seqmerge = true; + else + if ~isfield(grpdef, 'pulseind') || isempty(grpdef.pulseind) + warning('pulseind not specified. Ask for policy.') + zerolen = self.zero(grpdef,[],[]); + grpdef.pulseind = 1:size(zerolen, 1); + end + seqmerge = false; + end + + + + + npls = size(grpdef.pulseind, 2); + % nchan = length(self.awgs.nChannels); % alternatively use awgdata or data size + + if ~isfield(grpdef, 'nrep') + grpdef.nrep = ones(1,npls); + elseif length(grpdef.nrep) npls + error('length(grpdef.nrep) > npls'); + end + + + + +% if ~isfield(grpdef, 'jump') +% if strfind(grpdef.ctrl, 'loop') +% grpdef.jump = [npls; 1]; +% else +% grpdef.jump = []; +% end +% end + + groupDefAWG = struct('name',grpdef.name,'pulses',[],'pulseind',[],'repetitions',[],'ctrl',grpdef.ctrl); + if isfield(grpdef,'chan') + groupDefAWG.chan = grpdef.chan; + end + + if isfield(grpdef,'ctrl') + groupDefAWG.ctrl = grpdef.ctrl; + end + + % fill pulseind + if ~seqmerge % pulses combined here. + groupDefAWG.pulses = grpdef.pulses; + groupDefAWG.pulseind = grpdef.pulseind; + groupDefAWG.nrep = grpdef.nrep; + + elseif strfind(grpdef.ctrl,'pack') % added 14.11.2014 PC + %do nothing + groupDefAWG.pulses = grpdef.pulses; + groupDefAWG.pulseind = grpdef.pulseind; + groupDefAWG.nrep = grpdef.nrep; + + else % completely overhauled 02.05.2014 PC + error('work out what to du here'); + + groupDefAWG.nrep = grpdef.nrep; + %guess: + groupDefAWG.pulses = zeros(1,npls) + + for m = 1:length(grpdef.pulses.groups) + pulses = find( grpdef.pulseind(m,:) > 0 ) + + if any(groupDefAWG.pulseind(pulses)) + error('You can only play one pulse at a time.'); + end + + groupDefAWG.pulseind(pulses) = grpdef.pulseind(m,pulses)+length(groupDefAWG.pulses); + + groupDefAWG.pulses = [groupDefAWG.pulses(:) grpdef.groups.pulses{m}(:)]; + end + + +% for m = 1:length(grpdef.pulses.groups) +% for j = 1:nchan % channels of component groups +% ch = find(awgdata(a).chans(j) == chan{m}); +% if grpdef.pulseind(m, i) > 0 % Do not add if pulseind == 0 +% if ~isempty(ch) && zerolen(grpdef.pulseind(m, i), ch) < 0 +% % channel in group and not zero +% fprintf(awgdata(a).awg, sprintf('SEQ:ELEM%d:WAV%d "%s_%05d_%d"', ind, j, ... +% grpdef.pulses.groups{m}, grpdef.pulseind(m, i), ch)); +% else +% fprintf(awgdata.awg, sprintf('SEQ:ELEM%d:WAV%d "zero_%08d_%d"', ind, j, ... +% zlmult*abs(zerolen(grpdef.pulseind(m, i), 1))*awgdata(a).clk/awgdata(1).clk,awgdata(a).zerochan(j))); +% end +% end; +% end +% +% end + end + + if isfield(grpdef,'jump') + error('jump not implemented yet. (too lazy)'); + end + + for awg = self.awgs + awg.addPulseGroup(groupDefAWG); + end +% end +% if ~exist('seqlog','var') +% seqlog.time = now; +% else +% seqlog(end+1).time = now; +% end +% seqlog(end).nrep = grpdef.nrep; +% seqlog(end).jump = grpdef.jump; +% +% save([plsdata.grpdir, 'pg_', groups{k}], '-append', 'seqlog'); +% %fprintf('Jump program time: %f secs\n',toc-jstart); +% wstart=toc; +% awgcntrl('wait'); +% %fprintf('Wait time: %f secs; total time %f secs\n',toc-wstart,toc-astart); +% nerr=0; +% for a=1:length(awgdata(a)) +% err=query(awgdata(a).awg, 'SYST:ERR?'); +% if ~isempty(strfind(err, 'No error')) +% nerr=nerr+1; +% end +% end +% if nerr == 0 +% fprintf('Added group %s on index %i. %s', grpdef.name, gind, err); +% logentry('Added group %s on index %i.', grpdef.name, gind); +% end +% +% if dosave +% awgsavedata; +% end + +end diff --git a/AWGPULSEGROUP.m b/AWGPULSEGROUP.m new file mode 100644 index 0000000..9f83bd5 --- /dev/null +++ b/AWGPULSEGROUP.m @@ -0,0 +1,14 @@ +% pulsegroup representation in AWG driver +classdef AWGPULSEGROUP < matlab.mixin.Heterogeneous & handle + properties + name; + lastload = -Inf; + repetitions = []; + end + + methods + function obj = AWGPULSEGROUP(name) + obj.name = name; + end + end +end \ No newline at end of file diff --git a/AWGSTORAGE.m b/AWGSTORAGE.m new file mode 100644 index 0000000..df94078 --- /dev/null +++ b/AWGSTORAGE.m @@ -0,0 +1,137 @@ +%this class is a map/dictionary implementation which supports multi level +%access like storage.member(3).bla +classdef AWGSTORAGE < handle + properties + mData = AWGPULSEGROUP.empty(); + end + + methods + function i = index(self,identifier) + i = find( strcmp({self.mData.name},identifier) ); + if size(i,2) > 1 + warning('contains %i elements with the key %s.',size(i,2),identifier); + end + end + + function val = subsref(self,S) + if strcmp(S(1).type,'()') + if ischar(S(1).subs{1}) + if size(S(1).subs,2) ~= 1 + error('only one key access impleneted'); + end + + index = self.index(S(1).subs{1}); + if isempty(index) + error('Key %s not found',S(1).subs{1}); + end + + if size(S,2) ~= 1 + val = subsref( self.mData(index),S(2:end)); + else + val = self.mData(index); + end + else + val = subsref(self.mData,S); + end + else + try + val = builtin('subsref',self,S); + catch + builtin('subsref',self,S); + end + end + end + + function self = subsasgn(self,S,val) + if strcmp(S(1).type,'()') + if ischar(S(1).subs{1}) + + if size(S(1).subs,2) ~= 1 + error('AWGSTORAGE only supports the access via key of a single element at once for now.'); + end + + index = self.index(S(1).subs{1}); + if isempty(index) + if size(S,2) ~= 1 + error('Can not access members of element %s since it does not exist',self.index(S(1).subs{1})); + else + error('Key %s not found. No implicit object creation for simplicity/debugging. Use AWGSTORAGE@add to add an object',S(1).subs{1}); + end + else + self.mData(index) = subsasgn(self.mData(index),S(2:end),val); + end + + + else + self.mData = subsasgn( self.mData, S,val); + end + else + self = builtin('subsasgn',self,S,val); + end + end + + function remove(self,id) + if ischar(id) + index = self.index(id); + self.mData(index) = []; + else + self.mData(id) = []; + end + end + + function add(self,object) + self.insert(object, size(self.mData,2)+1) + end + + function insert(self,object,position) + if ~(isfield(object,'name') || isprop(object,'name') ) + error('May only add objects with the field "name"'); + elseif ~ischar(object.name) + error('The name field must be a string'); + end + + if ~isempty( self.mData ) + if ~isempty( self.index(object.name) ) + error('Object with key %s already exists.',object.name); + end + end + + self.mData = [self.mData(1:position-1) object self.mData(position:end)]; + end + + function s = length(self) + try + s = length(self.mData(1:end)); + catch + s = 1; + end + end + + function swap(self,i,j) + temp = self.mData(i); + self.mData(i) = self.mData(j); + self.mData(j) = temp; + end + + function move(self,key,newPosition) + oldPosition = self.index(key); + if newPosition < oldPosition + self.mData = [self.mData(1:newPosition-1) self.mData(oldPosition) self.mData((newPosition+1):oldPosition-1) self.mData((oldPosition+1):end)]; + elseif newPosition > oldPosition + self.mData = [self.mData(1:oldPosition-1) self.mData((oldPosition+1):newPosition-1) self.mData(oldPosition) self.mData((newPosition+1):end)]; + end + end + + function empty = isempty(self) + empty = isempty(self.mData); + end + + function iskey = isKey(self,key) + if isempty(self) + iskey = false; + else + iskey = self.index(key)>0; + end + end + end +end \ No newline at end of file diff --git a/COPYING b/COPYING deleted file mode 100755 index 94a9ed0..0000000 --- a/COPYING +++ /dev/null @@ -1,674 +0,0 @@ - GNU GENERAL PUBLIC LICENSE - Version 3, 29 June 2007 - - Copyright (C) 2007 Free Software Foundation, Inc. - Everyone is permitted to copy and distribute verbatim copies - of this license document, but changing it is not allowed. - - Preamble - - The GNU General Public License is a free, copyleft license for -software and other kinds of works. - - The licenses for most software and other practical works are designed -to take away your freedom to share and change the works. By contrast, -the GNU General Public License is intended to guarantee your freedom to -share and change all versions of a program--to make sure it remains free -software for all its users. We, the Free Software Foundation, use the -GNU General Public License for most of our software; it applies also to -any other work released this way by its authors. You can apply it to -your programs, too. - - When we speak of free software, we are referring to freedom, not -price. Our General Public Licenses are designed to make sure that you -have the freedom to distribute copies of free software (and charge for -them if you wish), that you receive source code or can get it if you -want it, that you can change the software or use pieces of it in new -free programs, and that you know you can do these things. - - To protect your rights, we need to prevent others from denying you -these rights or asking you to surrender the rights. Therefore, you have -certain responsibilities if you distribute copies of the software, or if -you modify it: responsibilities to respect the freedom of others. - - For example, if you distribute copies of such a program, whether -gratis or for a fee, you must pass on to the recipients the same -freedoms that you received. You must make sure that they, too, receive -or can get the source code. And you must show them these terms so they -know their rights. - - Developers that use the GNU GPL protect your rights with two steps: -(1) assert copyright on the software, and (2) offer you this License -giving you legal permission to copy, distribute and/or modify it. - - For the developers' and authors' protection, the GPL clearly explains -that there is no warranty for this free software. For both users' and -authors' sake, the GPL requires that modified versions be marked as -changed, so that their problems will not be attributed erroneously to -authors of previous versions. - - Some devices are designed to deny users access to install or run -modified versions of the software inside them, although the manufacturer -can do so. This is fundamentally incompatible with the aim of -protecting users' freedom to change the software. The systematic -pattern of such abuse occurs in the area of products for individuals to -use, which is precisely where it is most unacceptable. Therefore, we -have designed this version of the GPL to prohibit the practice for those -products. If such problems arise substantially in other domains, we -stand ready to extend this provision to those domains in future versions -of the GPL, as needed to protect the freedom of users. - - Finally, every program is threatened constantly by software patents. -States should not allow patents to restrict development and use of -software on general-purpose computers, but in those that do, we wish to -avoid the special danger that patents applied to a free program could -make it effectively proprietary. To prevent this, the GPL assures that -patents cannot be used to render the program non-free. - - The precise terms and conditions for copying, distribution and -modification follow. - - TERMS AND CONDITIONS - - 0. Definitions. - - "This License" refers to version 3 of the GNU General Public License. - - "Copyright" also means copyright-like laws that apply to other kinds of -works, such as semiconductor masks. - - "The Program" refers to any copyrightable work licensed under this -License. Each licensee is addressed as "you". "Licensees" and -"recipients" may be individuals or organizations. - - To "modify" a work means to copy from or adapt all or part of the work -in a fashion requiring copyright permission, other than the making of an -exact copy. The resulting work is called a "modified version" of the -earlier work or a work "based on" the earlier work. - - A "covered work" means either the unmodified Program or a work based -on the Program. - - To "propagate" a work means to do anything with it that, without -permission, would make you directly or secondarily liable for -infringement under applicable copyright law, except executing it on a -computer or modifying a private copy. Propagation includes copying, -distribution (with or without modification), making available to the -public, and in some countries other activities as well. - - To "convey" a work means any kind of propagation that enables other -parties to make or receive copies. Mere interaction with a user through -a computer network, with no transfer of a copy, is not conveying. - - An interactive user interface displays "Appropriate Legal Notices" -to the extent that it includes a convenient and prominently visible -feature that (1) displays an appropriate copyright notice, and (2) -tells the user that there is no warranty for the work (except to the -extent that warranties are provided), that licensees may convey the -work under this License, and how to view a copy of this License. If -the interface presents a list of user commands or options, such as a -menu, a prominent item in the list meets this criterion. - - 1. Source Code. - - The "source code" for a work means the preferred form of the work -for making modifications to it. "Object code" means any non-source -form of a work. - - A "Standard Interface" means an interface that either is an official -standard defined by a recognized standards body, or, in the case of -interfaces specified for a particular programming language, one that -is widely used among developers working in that language. - - The "System Libraries" of an executable work include anything, other -than the work as a whole, that (a) is included in the normal form of -packaging a Major Component, but which is not part of that Major -Component, and (b) serves only to enable use of the work with that -Major Component, or to implement a Standard Interface for which an -implementation is available to the public in source code form. A -"Major Component", in this context, means a major essential component -(kernel, window system, and so on) of the specific operating system -(if any) on which the executable work runs, or a compiler used to -produce the work, or an object code interpreter used to run it. - - The "Corresponding Source" for a work in object code form means all -the source code needed to generate, install, and (for an executable -work) run the object code and to modify the work, including scripts to -control those activities. However, it does not include the work's -System Libraries, or general-purpose tools or generally available free -programs which are used unmodified in performing those activities but -which are not part of the work. For example, Corresponding Source -includes interface definition files associated with source files for -the work, and the source code for shared libraries and dynamically -linked subprograms that the work is specifically designed to require, -such as by intimate data communication or control flow between those -subprograms and other parts of the work. - - The Corresponding Source need not include anything that users -can regenerate automatically from other parts of the Corresponding -Source. - - The Corresponding Source for a work in source code form is that -same work. - - 2. Basic Permissions. - - All rights granted under this License are granted for the term of -copyright on the Program, and are irrevocable provided the stated -conditions are met. This License explicitly affirms your unlimited -permission to run the unmodified Program. The output from running a -covered work is covered by this License only if the output, given its -content, constitutes a covered work. This License acknowledges your -rights of fair use or other equivalent, as provided by copyright law. - - You may make, run and propagate covered works that you do not -convey, without conditions so long as your license otherwise remains -in force. You may convey covered works to others for the sole purpose -of having them make modifications exclusively for you, or provide you -with facilities for running those works, provided that you comply with -the terms of this License in conveying all material for which you do -not control copyright. Those thus making or running the covered works -for you must do so exclusively on your behalf, under your direction -and control, on terms that prohibit them from making any copies of -your copyrighted material outside their relationship with you. - - Conveying under any other circumstances is permitted solely under -the conditions stated below. Sublicensing is not allowed; section 10 -makes it unnecessary. - - 3. Protecting Users' Legal Rights From Anti-Circumvention Law. - - No covered work shall be deemed part of an effective technological -measure under any applicable law fulfilling obligations under article -11 of the WIPO copyright treaty adopted on 20 December 1996, or -similar laws prohibiting or restricting circumvention of such -measures. - - When you convey a covered work, you waive any legal power to forbid -circumvention of technological measures to the extent such circumvention -is effected by exercising rights under this License with respect to -the covered work, and you disclaim any intention to limit operation or -modification of the work as a means of enforcing, against the work's -users, your or third parties' legal rights to forbid circumvention of -technological measures. - - 4. Conveying Verbatim Copies. - - You may convey verbatim copies of the Program's source code as you -receive it, in any medium, provided that you conspicuously and -appropriately publish on each copy an appropriate copyright notice; -keep intact all notices stating that this License and any -non-permissive terms added in accord with section 7 apply to the code; -keep intact all notices of the absence of any warranty; and give all -recipients a copy of this License along with the Program. - - You may charge any price or no price for each copy that you convey, -and you may offer support or warranty protection for a fee. - - 5. Conveying Modified Source Versions. - - You may convey a work based on the Program, or the modifications to -produce it from the Program, in the form of source code under the -terms of section 4, provided that you also meet all of these conditions: - - a) The work must carry prominent notices stating that you modified - it, and giving a relevant date. - - b) The work must carry prominent notices stating that it is - released under this License and any conditions added under section - 7. This requirement modifies the requirement in section 4 to - "keep intact all notices". - - c) You must license the entire work, as a whole, under this - License to anyone who comes into possession of a copy. This - License will therefore apply, along with any applicable section 7 - additional terms, to the whole of the work, and all its parts, - regardless of how they are packaged. This License gives no - permission to license the work in any other way, but it does not - invalidate such permission if you have separately received it. - - d) If the work has interactive user interfaces, each must display - Appropriate Legal Notices; however, if the Program has interactive - interfaces that do not display Appropriate Legal Notices, your - work need not make them do so. - - A compilation of a covered work with other separate and independent -works, which are not by their nature extensions of the covered work, -and which are not combined with it such as to form a larger program, -in or on a volume of a storage or distribution medium, is called an -"aggregate" if the compilation and its resulting copyright are not -used to limit the access or legal rights of the compilation's users -beyond what the individual works permit. Inclusion of a covered work -in an aggregate does not cause this License to apply to the other -parts of the aggregate. - - 6. Conveying Non-Source Forms. - - You may convey a covered work in object code form under the terms -of sections 4 and 5, provided that you also convey the -machine-readable Corresponding Source under the terms of this License, -in one of these ways: - - a) Convey the object code in, or embodied in, a physical product - (including a physical distribution medium), accompanied by the - Corresponding Source fixed on a durable physical medium - customarily used for software interchange. - - b) Convey the object code in, or embodied in, a physical product - (including a physical distribution medium), accompanied by a - written offer, valid for at least three years and valid for as - long as you offer spare parts or customer support for that product - model, to give anyone who possesses the object code either (1) a - copy of the Corresponding Source for all the software in the - product that is covered by this License, on a durable physical - medium customarily used for software interchange, for a price no - more than your reasonable cost of physically performing this - conveying of source, or (2) access to copy the - Corresponding Source from a network server at no charge. - - c) Convey individual copies of the object code with a copy of the - written offer to provide the Corresponding Source. This - alternative is allowed only occasionally and noncommercially, and - only if you received the object code with such an offer, in accord - with subsection 6b. - - d) Convey the object code by offering access from a designated - place (gratis or for a charge), and offer equivalent access to the - Corresponding Source in the same way through the same place at no - further charge. You need not require recipients to copy the - Corresponding Source along with the object code. If the place to - copy the object code is a network server, the Corresponding Source - may be on a different server (operated by you or a third party) - that supports equivalent copying facilities, provided you maintain - clear directions next to the object code saying where to find the - Corresponding Source. Regardless of what server hosts the - Corresponding Source, you remain obligated to ensure that it is - available for as long as needed to satisfy these requirements. - - e) Convey the object code using peer-to-peer transmission, provided - you inform other peers where the object code and Corresponding - Source of the work are being offered to the general public at no - charge under subsection 6d. - - A separable portion of the object code, whose source code is excluded -from the Corresponding Source as a System Library, need not be -included in conveying the object code work. - - A "User Product" is either (1) a "consumer product", which means any -tangible personal property which is normally used for personal, family, -or household purposes, or (2) anything designed or sold for incorporation -into a dwelling. In determining whether a product is a consumer product, -doubtful cases shall be resolved in favor of coverage. For a particular -product received by a particular user, "normally used" refers to a -typical or common use of that class of product, regardless of the status -of the particular user or of the way in which the particular user -actually uses, or expects or is expected to use, the product. A product -is a consumer product regardless of whether the product has substantial -commercial, industrial or non-consumer uses, unless such uses represent -the only significant mode of use of the product. - - "Installation Information" for a User Product means any methods, -procedures, authorization keys, or other information required to install -and execute modified versions of a covered work in that User Product from -a modified version of its Corresponding Source. The information must -suffice to ensure that the continued functioning of the modified object -code is in no case prevented or interfered with solely because -modification has been made. - - If you convey an object code work under this section in, or with, or -specifically for use in, a User Product, and the conveying occurs as -part of a transaction in which the right of possession and use of the -User Product is transferred to the recipient in perpetuity or for a -fixed term (regardless of how the transaction is characterized), the -Corresponding Source conveyed under this section must be accompanied -by the Installation Information. But this requirement does not apply -if neither you nor any third party retains the ability to install -modified object code on the User Product (for example, the work has -been installed in ROM). - - The requirement to provide Installation Information does not include a -requirement to continue to provide support service, warranty, or updates -for a work that has been modified or installed by the recipient, or for -the User Product in which it has been modified or installed. Access to a -network may be denied when the modification itself materially and -adversely affects the operation of the network or violates the rules and -protocols for communication across the network. - - Corresponding Source conveyed, and Installation Information provided, -in accord with this section must be in a format that is publicly -documented (and with an implementation available to the public in -source code form), and must require no special password or key for -unpacking, reading or copying. - - 7. Additional Terms. - - "Additional permissions" are terms that supplement the terms of this -License by making exceptions from one or more of its conditions. -Additional permissions that are applicable to the entire Program shall -be treated as though they were included in this License, to the extent -that they are valid under applicable law. If additional permissions -apply only to part of the Program, that part may be used separately -under those permissions, but the entire Program remains governed by -this License without regard to the additional permissions. - - When you convey a copy of a covered work, you may at your option -remove any additional permissions from that copy, or from any part of -it. (Additional permissions may be written to require their own -removal in certain cases when you modify the work.) You may place -additional permissions on material, added by you to a covered work, -for which you have or can give appropriate copyright permission. - - Notwithstanding any other provision of this License, for material you -add to a covered work, you may (if authorized by the copyright holders of -that material) supplement the terms of this License with terms: - - a) Disclaiming warranty or limiting liability differently from the - terms of sections 15 and 16 of this License; or - - b) Requiring preservation of specified reasonable legal notices or - author attributions in that material or in the Appropriate Legal - Notices displayed by works containing it; or - - c) Prohibiting misrepresentation of the origin of that material, or - requiring that modified versions of such material be marked in - reasonable ways as different from the original version; or - - d) Limiting the use for publicity purposes of names of licensors or - authors of the material; or - - e) Declining to grant rights under trademark law for use of some - trade names, trademarks, or service marks; or - - f) Requiring indemnification of licensors and authors of that - material by anyone who conveys the material (or modified versions of - it) with contractual assumptions of liability to the recipient, for - any liability that these contractual assumptions directly impose on - those licensors and authors. - - All other non-permissive additional terms are considered "further -restrictions" within the meaning of section 10. If the Program as you -received it, or any part of it, contains a notice stating that it is -governed by this License along with a term that is a further -restriction, you may remove that term. If a license document contains -a further restriction but permits relicensing or conveying under this -License, you may add to a covered work material governed by the terms -of that license document, provided that the further restriction does -not survive such relicensing or conveying. - - If you add terms to a covered work in accord with this section, you -must place, in the relevant source files, a statement of the -additional terms that apply to those files, or a notice indicating -where to find the applicable terms. - - Additional terms, permissive or non-permissive, may be stated in the -form of a separately written license, or stated as exceptions; -the above requirements apply either way. - - 8. Termination. - - You may not propagate or modify a covered work except as expressly -provided under this License. Any attempt otherwise to propagate or -modify it is void, and will automatically terminate your rights under -this License (including any patent licenses granted under the third -paragraph of section 11). - - However, if you cease all violation of this License, then your -license from a particular copyright holder is reinstated (a) -provisionally, unless and until the copyright holder explicitly and -finally terminates your license, and (b) permanently, if the copyright -holder fails to notify you of the violation by some reasonable means -prior to 60 days after the cessation. - - Moreover, your license from a particular copyright holder is -reinstated permanently if the copyright holder notifies you of the -violation by some reasonable means, this is the first time you have -received notice of violation of this License (for any work) from that -copyright holder, and you cure the violation prior to 30 days after -your receipt of the notice. - - Termination of your rights under this section does not terminate the -licenses of parties who have received copies or rights from you under -this License. If your rights have been terminated and not permanently -reinstated, you do not qualify to receive new licenses for the same -material under section 10. - - 9. Acceptance Not Required for Having Copies. - - You are not required to accept this License in order to receive or -run a copy of the Program. Ancillary propagation of a covered work -occurring solely as a consequence of using peer-to-peer transmission -to receive a copy likewise does not require acceptance. However, -nothing other than this License grants you permission to propagate or -modify any covered work. These actions infringe copyright if you do -not accept this License. Therefore, by modifying or propagating a -covered work, you indicate your acceptance of this License to do so. - - 10. Automatic Licensing of Downstream Recipients. - - Each time you convey a covered work, the recipient automatically -receives a license from the original licensors, to run, modify and -propagate that work, subject to this License. You are not responsible -for enforcing compliance by third parties with this License. - - An "entity transaction" is a transaction transferring control of an -organization, or substantially all assets of one, or subdividing an -organization, or merging organizations. If propagation of a covered -work results from an entity transaction, each party to that -transaction who receives a copy of the work also receives whatever -licenses to the work the party's predecessor in interest had or could -give under the previous paragraph, plus a right to possession of the -Corresponding Source of the work from the predecessor in interest, if -the predecessor has it or can get it with reasonable efforts. - - You may not impose any further restrictions on the exercise of the -rights granted or affirmed under this License. For example, you may -not impose a license fee, royalty, or other charge for exercise of -rights granted under this License, and you may not initiate litigation -(including a cross-claim or counterclaim in a lawsuit) alleging that -any patent claim is infringed by making, using, selling, offering for -sale, or importing the Program or any portion of it. - - 11. Patents. - - A "contributor" is a copyright holder who authorizes use under this -License of the Program or a work on which the Program is based. The -work thus licensed is called the contributor's "contributor version". - - A contributor's "essential patent claims" are all patent claims -owned or controlled by the contributor, whether already acquired or -hereafter acquired, that would be infringed by some manner, permitted -by this License, of making, using, or selling its contributor version, -but do not include claims that would be infringed only as a -consequence of further modification of the contributor version. For -purposes of this definition, "control" includes the right to grant -patent sublicenses in a manner consistent with the requirements of -this License. - - Each contributor grants you a non-exclusive, worldwide, royalty-free -patent license under the contributor's essential patent claims, to -make, use, sell, offer for sale, import and otherwise run, modify and -propagate the contents of its contributor version. - - In the following three paragraphs, a "patent license" is any express -agreement or commitment, however denominated, not to enforce a patent -(such as an express permission to practice a patent or covenant not to -sue for patent infringement). To "grant" such a patent license to a -party means to make such an agreement or commitment not to enforce a -patent against the party. - - If you convey a covered work, knowingly relying on a patent license, -and the Corresponding Source of the work is not available for anyone -to copy, free of charge and under the terms of this License, through a -publicly available network server or other readily accessible means, -then you must either (1) cause the Corresponding Source to be so -available, or (2) arrange to deprive yourself of the benefit of the -patent license for this particular work, or (3) arrange, in a manner -consistent with the requirements of this License, to extend the patent -license to downstream recipients. "Knowingly relying" means you have -actual knowledge that, but for the patent license, your conveying the -covered work in a country, or your recipient's use of the covered work -in a country, would infringe one or more identifiable patents in that -country that you have reason to believe are valid. - - If, pursuant to or in connection with a single transaction or -arrangement, you convey, or propagate by procuring conveyance of, a -covered work, and grant a patent license to some of the parties -receiving the covered work authorizing them to use, propagate, modify -or convey a specific copy of the covered work, then the patent license -you grant is automatically extended to all recipients of the covered -work and works based on it. - - A patent license is "discriminatory" if it does not include within -the scope of its coverage, prohibits the exercise of, or is -conditioned on the non-exercise of one or more of the rights that are -specifically granted under this License. You may not convey a covered -work if you are a party to an arrangement with a third party that is -in the business of distributing software, under which you make payment -to the third party based on the extent of your activity of conveying -the work, and under which the third party grants, to any of the -parties who would receive the covered work from you, a discriminatory -patent license (a) in connection with copies of the covered work -conveyed by you (or copies made from those copies), or (b) primarily -for and in connection with specific products or compilations that -contain the covered work, unless you entered into that arrangement, -or that patent license was granted, prior to 28 March 2007. - - Nothing in this License shall be construed as excluding or limiting -any implied license or other defenses to infringement that may -otherwise be available to you under applicable patent law. - - 12. No Surrender of Others' Freedom. - - If conditions are imposed on you (whether by court order, agreement or -otherwise) that contradict the conditions of this License, they do not -excuse you from the conditions of this License. If you cannot convey a -covered work so as to satisfy simultaneously your obligations under this -License and any other pertinent obligations, then as a consequence you may -not convey it at all. For example, if you agree to terms that obligate you -to collect a royalty for further conveying from those to whom you convey -the Program, the only way you could satisfy both those terms and this -License would be to refrain entirely from conveying the Program. - - 13. Use with the GNU Affero General Public License. - - Notwithstanding any other provision of this License, you have -permission to link or combine any covered work with a work licensed -under version 3 of the GNU Affero General Public License into a single -combined work, and to convey the resulting work. The terms of this -License will continue to apply to the part which is the covered work, -but the special requirements of the GNU Affero General Public License, -section 13, concerning interaction through a network will apply to the -combination as such. - - 14. Revised Versions of this License. - - The Free Software Foundation may publish revised and/or new versions of -the GNU General Public License from time to time. Such new versions will -be similar in spirit to the present version, but may differ in detail to -address new problems or concerns. - - Each version is given a distinguishing version number. If the -Program specifies that a certain numbered version of the GNU General -Public License "or any later version" applies to it, you have the -option of following the terms and conditions either of that numbered -version or of any later version published by the Free Software -Foundation. If the Program does not specify a version number of the -GNU General Public License, you may choose any version ever published -by the Free Software Foundation. - - If the Program specifies that a proxy can decide which future -versions of the GNU General Public License can be used, that proxy's -public statement of acceptance of a version permanently authorizes you -to choose that version for the Program. - - Later license versions may give you additional or different -permissions. However, no additional obligations are imposed on any -author or copyright holder as a result of your choosing to follow a -later version. - - 15. Disclaimer of Warranty. - - THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY -APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT -HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY -OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, -THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR -PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM -IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF -ALL NECESSARY SERVICING, REPAIR OR CORRECTION. - - 16. Limitation of Liability. - - IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING -WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS -THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY -GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE -USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF -DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD -PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), -EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF -SUCH DAMAGES. - - 17. Interpretation of Sections 15 and 16. - - If the disclaimer of warranty and limitation of liability provided -above cannot be given local legal effect according to their terms, -reviewing courts shall apply local law that most closely approximates -an absolute waiver of all civil liability in connection with the -Program, unless a warranty or assumption of liability accompanies a -copy of the Program in return for a fee. - - END OF TERMS AND CONDITIONS - - How to Apply These Terms to Your New Programs - - If you develop a new program, and you want it to be of the greatest -possible use to the public, the best way to achieve this is to make it -free software which everyone can redistribute and change under these terms. - - To do so, attach the following notices to the program. It is safest -to attach them to the start of each source file to most effectively -state the exclusion of warranty; and each file should have at least -the "copyright" line and a pointer to where the full notice is found. - - - Copyright (C) - - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program. If not, see . - -Also add information on how to contact you by electronic and paper mail. - - If the program does terminal interaction, make it output a short -notice like this when it starts in an interactive mode: - - Copyright (C) - This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. - This is free software, and you are welcome to redistribute it - under certain conditions; type `show c' for details. - -The hypothetical commands `show w' and `show c' should show the appropriate -parts of the General Public License. Of course, your program's commands -might be different; for a GUI interface, you would use an "about box". - - You should also get your employer (if you work as a programmer) or school, -if any, to sign a "copyright disclaimer" for the program, if necessary. -For more information on this, and how to apply and follow the GNU GPL, see -. - - The GNU General Public License does not permit incorporating your program -into proprietary programs. If your program is a subroutine library, you -may consider it more useful to permit linking proprietary applications with -the library. If this is what you want to do, use the GNU Lesser General -Public License instead of this License. But first, please read -. diff --git a/PXDACMEMORY.m b/PXDACMEMORY.m new file mode 100644 index 0000000..e69de29 diff --git a/PXDACPULSE.m b/PXDACPULSE.m new file mode 100644 index 0000000..5b0c4b2 --- /dev/null +++ b/PXDACPULSE.m @@ -0,0 +1,72 @@ +% +% This class represents one waveform of which +% the output during one scanline is composed +% +classdef PXDACPULSE < handle + properties (GetAccess = public, SetAccess = private) + channelMask; + channelCount; + + %number of samples in one channel + samplesPerChannel; + + %byte + byteSize; + + rawData; + + lastEdit; + end + + methods + function obj = PXDACPULSE(channelMask,samplesPerChannel) + if ~isa(channelMask,'uint16') + error('ChannelMask must be of type uint16'); + end + + if sum(bitget(channelMask,5:16)) ~= 0 + maskString = sprintf('%i',bitget(channelMask,1:16)); + error('Only 4 channels available. Recieved channel mask %s',maskString); + end + + + obj.channelCount = sum(bitget(channelMask,1:16)); + obj.channelMask = channelMask; + + if obj.channelCount == 3 + error('Playing on 3 channels not possible'); + elseif sum(bitget(channelMask,1:2)) == 1 && sum(bitget(channelMask,3:4)) == 1 + error('May not play channel (1 XOR 2) AND (3 XOR 4).'); + end + + obj.samplesPerChannel = samplesPerChannel; + bytesPerSample = 2; %sizeof(uint16) + obj.byteSize = obj.channelCount*obj.samplesPerChannel*bytesPerSample; + + %hardcoded resolution + obj.rawData = libpointer('uint16Ptr',zeros(1,obj.samplesPerChannel*obj.channelCount,'uint16')); + + + obj.lastEdit = now; + + end + + function writeToChannel(self,channel,data) + if length(data)~=self.samplesPerChannel + error('Dimensions do not fit. Expected %i points but got %i.',self.samplesPerChannel,length(data)); + end + + if bitand(channel,self.channelMask) == 0 + error('The channel %i is not included in channel mask %i%i%i%i',channel,bitget(self.channelMask,5-(1:4))); + end + + channelsBefore = sum(bitget(self.channelMask,1:channel)-1); + + self.rawData.Value(... + channelsBefore+... %offset + 1:self.channelCount:self.samplesPerChannel*self.channelCount)... % every channelCount datapoint + = data; + self.lastEdit=now; + end + end +end \ No newline at end of file diff --git a/PXDACPULSEGROUP.m b/PXDACPULSEGROUP.m new file mode 100644 index 0000000..d56979d --- /dev/null +++ b/PXDACPULSEGROUP.m @@ -0,0 +1,22 @@ +classdef PXDACPULSEGROUP < AWGPULSEGROUP + properties + start = []; + totalByteSize = []; + lastMemoryUpdate = -Inf; + + %if not enough onboard memory -> the oldest one will be deleted + lastActivation = -Inf; + + % equivalent to grpdef.pulses + waveformArray = PXDACPULSE.empty(0,0); + + %equivalent to grpdef.pulseind and grpdef.nrep + pulseSequence = repmat(struct('index',[],'nrep',[]),0,0); + end + + methods + function obj = PXDACPULSEGROUP(name) + obj = obj@AWGPULSEGROUP(name); + end + end +end \ No newline at end of file diff --git a/PXDACSEQ.m b/PXDACSEQ.m new file mode 100644 index 0000000..1adcbb8 --- /dev/null +++ b/PXDACSEQ.m @@ -0,0 +1,6 @@ +classdef PXDACSEQ + properties + + end + +end \ No newline at end of file diff --git a/TekPULSEGROUP.m b/TekPULSEGROUP.m new file mode 100644 index 0000000..345d93c --- /dev/null +++ b/TekPULSEGROUP.m @@ -0,0 +1,21 @@ +classdef TekPULSEGROUP < AWGPULSEGROUP + properties + %index of sequence start + seqind = []; + + %number of pulses and usetrig + npulse = [0 0]; + + % + nline = []; + + zerolen = []; + + end + + methods + function obj = TekPULSEGROUP(name) + obj = obj@AWGPULSEGROUP(name); + end + end +end \ No newline at end of file diff --git a/Testing/DSTestPulseBuilder.m b/Testing/DSTestPulseBuilder.m new file mode 100644 index 0000000..d94a049 --- /dev/null +++ b/Testing/DSTestPulseBuilder.m @@ -0,0 +1,72 @@ +classdef DSTestPulseBuilder < TestPulseBuilder + + properties (Constant, GetAccess = public) + meanErrorThreshold = 1e-4; + singleErrorThreshold = 1e-3; + dacOperation = 'ds'; + end + + properties (Constant, GetAccess = protected) + pulseGroupPrototype = struct( ... + 'pulses', [], ... + 'nrep', [], ... + 'name', 'DownsamplingTestPulseGroup', ... + 'chan', 1, ... + 'ctrl', 'notrig' ... + ); + + voltageHoldDuration = 100; + end + + methods (Access = public) + + function self = DSTestPulseBuilder() + self = self@TestPulseBuilder(); + rng(42); + end + + end + + methods (Access = protected) + + function createPulse(self, mask, repetitions) + dt = 1; % us + + readoutDuration = mask.end - mask.begin; + + assert(readoutDuration >= 0, 'Duration of readout window in mask was less than zero!'); + assert(mask.begin == 0 || mask.begin >= dt, 'Could not compute pulse because begin of readout window was to close to zero.'); + assert(mask.end == mask.period || mask.end <= mask.period - dt, 'Could not compute pulse because end of readout windows was to close to period.'); + + randomVoltage = rand(1, 1) * 2 - 1; + + readoutVoltages = [ mask.begin, mask.end; ... + randomVoltage, randomVoltage ]; + + preReadoutVoltages = []; + postReadoutVoltages = []; + if (mask.begin > 0) + preReadoutVoltages = [ 0, mask.begin - dt; ... + -randomVoltage, -randomVoltage ]; + end + if (mask.end < mask.period) + postReadoutVoltages = [ mask.end + dt, mask.period; ... + -randomVoltage, -randomVoltage ]; + end + + pulse.data.pulsetab = [preReadoutVoltages readoutVoltages postReadoutVoltages]; + pulse.name = sprintf('DSIOTestPulse%i', self.pulseCount); + + self.pulseGroup.pulses(end + 1) = plsreg(pulse); + self.pulseGroup.nrep(end + 1) = repetitions; + + for i = 1:repetitions + self.expectedData = [self.expectedData, randomVoltage]; + end + + end + + end + +end + diff --git a/Testing/IOTestDriver.m b/Testing/IOTestDriver.m new file mode 100644 index 0000000..bbd0ceb --- /dev/null +++ b/Testing/IOTestDriver.m @@ -0,0 +1,81 @@ +classdef IOTestDriver + + properties (SetAccess = private, GetAccess = private) + vawg; + configurationProvider; + dac; + end + + properties (SetAccess = private, GetAccess = public) + expectedData = []; + measuredData = []; + end + + methods (Access = public) + + function obj = IOTestDriver(configurationProvider) + if ~isa(configurationProvider, 'TestConfigurationProvider') + error('IOTestDriver requires an instance of TestConfigurationProvider for parameter configurationProvider!'); + end + obj.configurationProvider = configurationProvider; + end + + function success = run(self) + self.init(); + + self.dac.issueTrigger(); + + while self.vawg.playbackInProgress() + pause(1); + fprintf('Waiting for playback to finish...\n'); + end + + self.measuredData = self.dac.getResult(self.configurationProvider.getInputChannel()); + + success = self.evaluate(); + end + + end + + methods (Access = private) + + function init(self) + + % obtain pulse group and expected data from test configuration + self.configurationProvider.createPulseGroup(); + pulseGroup = self.configurationProvider.getPulseGroup(); + self.expectedData = self.configurationProvider.getExpectedData(); + + % obtain DAC instance from test configuration + self.dac = self.configurationProvider.createDAC(); + self.dac.useAsTriggerSource(); + + % setup and arm awg + self.initVAWG(); + self.vawg.add(pulseGroup); + self.vawg.setActivePulseGroup(pulseGroup); + self.vawg.arm(); + end + + function initVAWG(self) + self.vawg = VAWG(); + awg = PXDAC_DC('messrechnerDC', 1); + awg.setOutputVoltage(1, 1); + + self.vawg.addAWG(awg); + self.vawg.createVirtualChannel(awg, 1, 1); + end + + function success = evaluate(self) + err = self.measuredData - self.expectedData; % error signal + rms = std(err,0); % average error per sample + maxerr = max(abs(err)); % maximum single error + satisfiesMeanThreshold = rms < self.configurationProvider.getMeanErrorThreshold(); + satisfiesSingleThreshold = maxerr < self.configurationProvider.getSingleErrorThreshold(); + success = satisfiesMeanThreshold && satisfiesSingleThreshold; + end + + end + +end + diff --git a/Testing/InitializePulseData.m b/Testing/InitializePulseData.m new file mode 100644 index 0000000..fee695a --- /dev/null +++ b/Testing/InitializePulseData.m @@ -0,0 +1,11 @@ +global plsdata; +plsdata = []; +plsdata.datafile = [tempdir 'hardwaretest\plsdata_hw']; +plsdata.grpdir = [tempdir 'hardwaretest\plsdef\plsgrp']; +try + rmdir([tempdir 'hardwaretest'],'s'); +end +mkdir(plsdata.grpdir); + +plsdata.pulses = struct('data', {}, 'name', {}, 'xval',{}, 'taurc',{}, 'pardef',{},'trafofn',{},'format',{}); +plsdata.tbase = 1000; \ No newline at end of file diff --git a/Testing/PeriodicMaskTestConfigurationProvider.m b/Testing/PeriodicMaskTestConfigurationProvider.m new file mode 100644 index 0000000..65bbc52 --- /dev/null +++ b/Testing/PeriodicMaskTestConfigurationProvider.m @@ -0,0 +1,39 @@ +classdef PeriodicMaskTestConfigurationProvider < TestConfigurationProvider + + properties (GetAccess = protected) + mask = struct( ... + 'begin', 400, ... + 'end', 600, ... + 'period', 1000 ... + ); + iterations = 100; + end + + methods (Access = protected) + + function computePulseGroup(self) + for i = 1:self.iterations + self.pulseBuilder.addPulse(self.mask, 1); + end + end + + end + + methods (Access = public) + + function self = PeriodicMaskTestConfigurationProvider(inputChannel, pulseBuilder, mask, iterations) + self = self@TestConfigurationProvider(inputChannel, pulseBuilder); + if (nargin >= 3) + assert(~isfield(mask, 'type') || ~strcmp(mask.type, 'Table Mask'), 'mask must not be a table mask!'); + self.mask = mask; + end + if (nargin == 4) + assert(isnumeric(iterations) && iterations > 0, 'iterations must be a positive integer value!'); + self.iterations = iterations; + end + end + + end + +end + diff --git a/Testing/RSATestPulseBuilder.m b/Testing/RSATestPulseBuilder.m new file mode 100644 index 0000000..7060b77 --- /dev/null +++ b/Testing/RSATestPulseBuilder.m @@ -0,0 +1,96 @@ +classdef RSATestPulseBuilder < TestPulseBuilder + + properties (Constant, GetAccess = public) + meanErrorThreshold = 1e-4; + singleErrorThreshold = 1e-3; + dacOperation = 'rsa'; + end + + properties (Constant, GetAccess = protected) + pulseGroupPrototype = struct( ... + 'pulses', [], ... + 'nrep', [], ... + 'name', 'RSATestPulseGroup', ... + 'chan', 1, ... + 'ctrl', 'notrig' ... + ); + + voltageHoldDuration = 20; + end + + properties (SetAccess = private, GetAccess = private) + readoutDuration = 0; + readoutVoltages = []; + period = 0; + end + + methods (Access = public) + + function self = RSATestPulseBuilder() + self = self@TestPulseBuilder(); + rng(42); + end + + function reset(self) + reset@TestPulseBuilder(self); + self.readoutDuration = 0; + self.readoutVoltages = []; + self.period = 0; + end + + end + + methods (Access = protected) + + function createPulse(self, mask, repetitions) + dt = 1; % us + + if (self.readoutDuration == 0) + self.readoutDuration = mask.end - mask.begin; + end + + if (self.period == 0) + self.period = mask.period; + end + + assert(self.period == mask.period, 'Period of mask deviates from previous values.'); + assert(self.readoutDuration == mask.end - mask.begin, 'Duration of readout window in mask deviates from previous values!'); + assert(self.readoutDuration >= 0, 'Duration of readout window in mask was less than zero!'); + assert(mask.begin == 0 || mask.begin >= dt, 'Could not compute pulse because begin of readout window was to close to zero.'); + assert(mask.end == mask.period || mask.end <= mask.period - dt, 'Could not compute pulse because end of readout windows was to close to period.'); + + if (isempty(self.readoutVoltages)) + readoutVoltageCount = floor(self.readoutDuration / self.voltageHoldDuration) + 1; + self.readoutVoltages = rand(1, readoutVoltageCount) * 2 - 1; + + readoutPulse.data.pulsetab = [linspace(0, self.readoutDuration, readoutVoltageCount); self.readoutVoltages]; + + readoutPulse = plstowf(readoutPulse); + self.expectedData = readoutPulse.data.wf; + end + + readoutVoltageTable = [linspace(mask.begin, mask.end, size(self.readoutVoltages, 2)); self.readoutVoltages]; + + preReadoutVoltages = []; + postReadoutVoltages = []; + if (mask.begin > 0) + preReadoutVoltages = [ 0, mask.begin - dt; ... + -self.readoutVoltages(1), -self.readoutVoltages(1) ]; + end + if (mask.end < mask.period) + postReadoutVoltages = [ mask.end + dt, mask.period; ... + -self.readoutVoltages(end), -self.readoutVoltages(end) ]; + end + + pulse.data.pulsetab = [preReadoutVoltages readoutVoltageTable postReadoutVoltages]; + pulse.name = sprintf('RSAIOTestPulse%i', self.pulseCount); + + self.pulseGroup.pulses(end + 1) = plsreg(pulse); + self.pulseGroup.nrep(end + 1) = repetitions; + + end + + end + +end + diff --git a/Testing/RandomTestPulseBuilder.m b/Testing/RandomTestPulseBuilder.m new file mode 100644 index 0000000..57647d0 --- /dev/null +++ b/Testing/RandomTestPulseBuilder.m @@ -0,0 +1,55 @@ +classdef RandomTestPulseBuilder < TestPulseBuilder + + properties (Constant, GetAccess = public) + meanErrorThreshold = 1e-3; + singleErrorThreshold = 0.5e-2; + dacOperation = 'raw'; + end + + properties (Constant, GetAccess = protected) + pulseGroupPrototype = struct( ... + 'pulses', [], ... + 'nrep', [], ... + 'name', 'RandomTestPulseGroup', ... + 'chan', 1, ... + 'ctrl', 'notrig' ... + ); + + voltageHoldDuration = 100; + end + + methods (Access = public) + + function self = RandomTestPulseBuilder() + self = self@TestPulseBuilder(); + rng(42); + end + + end + + methods (Access = protected) + + function createPulse(self, mask, repetitions) + readoutDuration = mask.period; + voltagesAmount = ceil(readoutDuration / self.voltageHoldDuration); + + randomVoltages = rand(1, voltagesAmount) * 2 - 1; + pulse.data.pulsetab = zeros(2, voltagesAmount); + pulse.data.pulsetab(1, :) = linspace(mask.begin, mask.end, voltagesAmount); + pulse.data.pulsetab(2, :) = randomVoltages; + + pulse.name = sprintf('RandomTestPulse%i', self.pulseCount); + + self.pulseGroup.pulses(end + 1) = plsreg(pulse); + self.pulseGroup.nrep(end + 1) = repetitions; + + p = plstowf(pulse); + for i = 1:repetitions + self.expectedData = [self.expectedData, p.data.wf]; + end + end + + end + +end + diff --git a/Testing/RawIOTestConfigurationProvider.m b/Testing/RawIOTestConfigurationProvider.m new file mode 100644 index 0000000..f9992b0 --- /dev/null +++ b/Testing/RawIOTestConfigurationProvider.m @@ -0,0 +1,39 @@ +classdef RawIOTestConfigurationProvider < TestConfigurationProvider + + properties (GetAccess = protected) + mask = struct( ... + 'begin', 0, ... + 'end', 10000, ... + 'period', 10000 ... + ); + iterations = 1; + end + + methods (Access = protected) + + function computePulseGroup(self) + for i = 1:self.iterations + self.pulseBuilder.addPulse(self.mask, 1); + end + end + + end + + methods (Access = public) + + function self = RawIOTestConfigurationProvider(inputChannel, pulseBuilder, mask, iterations) + self = self@TestConfigurationProvider(inputChannel, pulseBuilder); + if (nargin >= 3) + assert(~isfield(mask, 'type') || ~strcmp(mask.type, 'Table Mask'), 'mask must not be a table mask!'); + assert(mask.begin == 0 && mask.end == mask.period, 'Readout window of mask must be equal to its period!'); + self.mask = mask; + end + if (nargin == 4) + assert(isnumeric(iterations) && iterations > 0, 'iterations must be a positive integer value!'); + self.iterations = iterations; + end + end + end + +end + diff --git a/Testing/TableMaskTestConfigurationProvider.m b/Testing/TableMaskTestConfigurationProvider.m new file mode 100644 index 0000000..dc77589 --- /dev/null +++ b/Testing/TableMaskTestConfigurationProvider.m @@ -0,0 +1,40 @@ +classdef TableMaskTestConfigurationProvider < TestConfigurationProvider + + properties (GetAccess = protected) + mask = struct( ... + 'type', 'Table Mask', ... + 'begin', 400 + floor(linspace(0, 200, 100)), ... + 'end', 600 + floor(linspace(0, 200, 100)), ... + 'period', 1000 ... + ); + end + + methods (Access = protected) + + function computePulseGroup(self) + currentMask = self.mask; + for i = 1:numel(self.mask.begin) + currentMask.begin = self.mask.begin(i); + currentMask.end = self.mask.end(i); + self.pulseBuilder.addPulse(currentMask, 1); + end + end + + end + + methods (Access = public) + + function self = TableMaskTestConfigurationProvider(inputChannel, pulseBuilder, mask) + self = self@TestConfigurationProvider(inputChannel, pulseBuilder); + if (nargin >= 3) + assert(isfield(mask, 'type'), 'mask must be a table mask!'); + assert(strcmp(mask.type, 'Table Mask'), 'mask must be a table mask!'); + assert(numel(mask.begin) == numel(mask.end), 'begin and end table dimensions of mask are not equal!'); + self.mask = mask; + end + end + + end + +end + diff --git a/Testing/TestConfigurationProvider.m b/Testing/TestConfigurationProvider.m new file mode 100644 index 0000000..905289f --- /dev/null +++ b/Testing/TestConfigurationProvider.m @@ -0,0 +1,69 @@ +classdef TestConfigurationProvider < handle + + properties (SetAccess = private, GetAccess = public) + inputChannel = 1; + meanErrorThreshold; + singleErrorThreshold; + end + + properties (SetAccess = private, GetAccess = protected) + pulseBuilder; + end + + properties (Abstract, GetAccess = protected) + mask; + end + + methods (Abstract, Access = protected) + computePulseGroup(self); + end + + methods (Access = public) + + function self = TestConfigurationProvider(inputChannel, pulseBuilder) + assert(isa(pulseBuilder, 'TestPulseBuilder'), 'pulseBuilder must be an instance of TestPulseBuilder!'); + assert(isnumeric(inputChannel) && inputChannel > 0, 'inputChannel must be a positive integer value!'); + + self.pulseBuilder = pulseBuilder; + self.inputChannel = inputChannel; + self.meanErrorThreshold = self.pulseBuilder.meanErrorThreshold; + self.singleErrorThreshold = self.pulseBuilder.singleErrorThreshold; + end + + function createPulseGroup(self) + self.pulseBuilder.reset(); + self.computePulseGroup(); + plsdefgrp(self.pulseBuilder.pulseGroup); + end + + function pulseGroup = getPulseGroup(self) + pulseGroup = self.pulseBuilder.pulseGroup; + end + + function expectedData = getExpectedData(self) + expectedData = self.pulseBuilder.expectedData; + end + + function dac = createDAC(self) + switch (self.pulseBuilder.dacOperation) + case 'raw' + operation = self.inputChannel; + case 'ds' + operation = self.inputChannel + 4; + case 'rsa' + operation = self.inputChannel + 8; + otherwise + error('DAC operation %s unknown', self.pulseBuilder.dacOperation); + end + + dac = ATS9440(1); + dac.samprate = 100e6; + samplesInPeriod = self.mask.period * dac.samprate / 1e6; + dac.masks = { self.mask }; + dac.configureMeasurement(samplesInPeriod, self.pulseBuilder.pulseCount, 1, operation); + end + + end + +end + diff --git a/Testing/TestPulseBuilder.m b/Testing/TestPulseBuilder.m new file mode 100644 index 0000000..013c179 --- /dev/null +++ b/Testing/TestPulseBuilder.m @@ -0,0 +1,49 @@ +classdef TestPulseBuilder < handle + + properties (Constant, Abstract, GetAccess = public) + meanErrorThreshold; + singleErrorThreshold; + dacOperation; + end + + properties (Constant, Abstract, GetAccess = protected) + pulseGroupPrototype; + end + + properties (SetAccess = private, GetAccess = public) + pulseCount = 0; + end + + properties (SetAccess = protected, GetAccess = public) + pulseGroup; + expectedData; + end + + methods (Abstract, Access = protected) + createPulse(self, mask, repetitions); + end + + methods (Access = protected) + + function self = TestPulseBuilder() + self.reset(); + end + + end + + methods (Access = public) + + function addPulse(self, mask, repetitions) + self.pulseCount = self.pulseCount + repetitions * mask.period; + self.createPulse(mask, repetitions); + end + + function reset(self) + self.pulseGroup = self.pulseGroupPrototype; + self.pulseCount = 0; + self.expectedData = []; + end + end + +end + diff --git a/Testing/hardwarePulseTest.m b/Testing/hardwarePulseTest.m new file mode 100644 index 0000000..0dabe90 --- /dev/null +++ b/Testing/hardwarePulseTest.m @@ -0,0 +1,10 @@ +function hardwarePulseTest() + InitializePulseData; + + + pulseBuilder = RandomTestPulseBuilder(); + configurationProvider = RawIOTestConfigurationProvider(1, pulseBuilder); + testSetup = IOTestDriver(configurationProvider); + + testSetup.run(); +end \ No newline at end of file diff --git a/awgadd.m b/awgadd.m deleted file mode 100755 index 0ce5a7b..0000000 --- a/awgadd.m +++ /dev/null @@ -1,279 +0,0 @@ -function awgadd(groups) -% awgadd(groups) -% Add groups to end of sequence. Store group name and target index in -% awgdata.pulsgroups.name, seqind. - -% Group control 'seq' creates sequence combined groups -% ------------------------------------------------------------------------- -% Add groups like: pg.pulses.groups = {'group_1', 'group_2', 'group_4'}; -% At least one of the subgroup names should be the same name as the group -% name itself followed by an underscore and a number, e.g. -% pg.name = 'groups' for the above example of pg.pulses.groups. -% Set group control: pg.ctrl = 'loop seq'; -% Set order of pulses froms groups with pg.pulseind. Subgroups are indexed -% by row, pulses of the group are indexed by column. The value gives the -% number of pulse to use from original subgroup. A zero indicates not to -% use a pulse from the corresponding group in the respective position. For -% 8 pulses per group this might look like: -% pg.pulseind(1,:) = [1:8 zeros(1,16)]; -% pg.pulseind(2,:) = [zeros(1,8) 1:8 zeros(1,8)]; -% pg.pulseind(3,:) = [zeros(1,16) 1:8]; -% ------------------------------------------------------------------------- -% -% (c) 2010 Hendrik Bluhm. Please see LICENSE and COPYRIGHT information in plssetup.m. - -global plsdata; -global awgdata; -astart=toc; -awgcntrl('clr'); -awgcntrl('stop'); - -if ~iscell(groups) - groups = {groups}; -end - -dosave = false; % keeps track of whether awgdata changed. -gstart=toc; - - -for k = 1:length(groups) - load([plsdata.grpdir, 'pg_', groups{k}]); - - stayInWhileLoop = true; - while plsinfo('stale',groups{k}) && stayInWhileLoop - fprintf('Latest pulses of group %s not loaded; %s > %s.\n', groups{k}, ... - datestr(lastupdate), datestr(plslog(end).time(end))); - tstart=toc; - plsmakegrp(groups{k},'upload'); - ts2 = toc; - awgcntrl('wait'); - % fprintf('Load time=%f secs, wait time=%f\n',toc-tstart,toc-ts2); - load([plsdata.grpdir, 'pg_', groups{k}]); - - if ~isempty(strfind(grpdef.ctrl, 'seqhack')) % Added by Pascal on 2014_08_01 to fix behaviour of sequence joined groups - stayInWhileLoop = false; - warning('Not checking whether pulses stale since grpdef.ctrl = ''seqhack''.'); - end; - end - - - if strcmp(grpdef.ctrl(1:min([end find(grpdef.ctrl == ' ', 1)-1])), 'grp')... - && ~isempty(strfind(grpdef.ctrl, 'seq')) % combine groups at sequence level. - - % retrieve channels of component groups - clear chan; - for m = 1:length(grpdef.pulses.groups) - gd=plsinfo('gd', grpdef.pulses.groups{m}); - rf={'varpar', 'pulseind', 'time'}; % Required fields that may be missing - for qq=1:length(rf) - if ~isfield(gd,rf{qq}) - gd=setfield(gd,rf{qq},[]); - end - end - chan(m) = orderfields(gd); - end - chan = {chan.chan}; - seqmerge = true; - else - if ~isfield(grpdef, 'pulseind') - zerolen = plsinfo('zl', grpdef.name); % hack TB - grpdef.pulseind = 1:size(zerolen, 1); - end - - seqmerge = false; - end - - if ~isfield(grpdef, 'nrep') - grpdef.nrep = 1; - end - - for a=1:length(awgdata) - npls = size(grpdef.pulseind, 2); - nchan = length(awgdata(a).chans); % alternatively use awgdata or data size - usetrig = (grpdef.nrep(1) ~= Inf) && isempty(strfind(grpdef.ctrl, 'notrig')); - - if isempty(awgdata(a).pulsegroups) - startline = 1; - gind = []; - else - gind = strmatch(grpdef.name, {awgdata(a).pulsegroups.name}, 'exact'); - if ~isempty(gind) - startline = awgdata(a).pulsegroups(gind(1)).seqind; - if npls + usetrig ~= sum(awgdata(a).pulsegroups(gind(1)).npulse); - error('Number of pulses changed in group %s. Use awgrm first!', grpdef.name); - end - else - startline = awgdata(a).pulsegroups(end).seqind + sum(awgdata(a).pulsegroups(end).nline); - end - end - - if isempty(gind) % group not loaded yet, extend sequence - - gind = length(awgdata(a).pulsegroups)+1; - awgdata(a).pulsegroups(gind).name = grpdef.name; - awgdata(a).pulsegroups(gind).seqind = startline; - awgdata(a).pulsegroups(gind).lastupdate = lastupdate; - awgdata(a).pulsegroups(gind).npulse = [npls usetrig]; - if strfind(grpdef.ctrl,'pack') - awgdata(a).pulsegroups(gind).nline = 1+usetrig; - % Hack alert; way too much code assumes zl == pulselen. For - % packed groups, we ignore it and work out the correct length - % ourselves. - zlmult=npls; - npls=1; - else - zlmult=1; - awgdata(a).pulsegroups(gind).nline = npls+usetrig; - end - fprintf(awgdata(a).awg, sprintf('SEQ:LENG %d', startline + awgdata(a).pulsegroups(gind).nline-1)); - dosave = 1; - else - if strfind(grpdef.ctrl,'pack') - zlmult = npls; - npls=1; - else - zlmult=1; - end - if isfield(plslog, 'readout') && exist('zerolen', 'var') - if any(awgdata(a).pulsegroups(gind).nrep ~= grpdef.nrep) || ... - any(any(awgdata(a).pulsegroups(gind).readout ~= plslog(end).readout)) || ... - any(any(awgdata(a).pulsegroups(gind).zerolen ~= zerolen)) % nrep or similar changed - dosave = 1; - end - else - if any(awgdata(a).pulsegroups(gind).nrep ~= grpdef.nrep) % nrep changed - dosave = 1; - end - end; - end - - if ~isfield(grpdef, 'jump') - if strfind(grpdef.ctrl, 'loop') - grpdef.jump = [npls; 1]; - else - grpdef.jump = []; - end - end - - - awgdata(a).pulsegroups(gind).nrep = grpdef.nrep; - awgdata(a).pulsegroups(gind).lastload = plslog(end).time(1); - - % Added block below to fix 'seq', where zerolen and plslog(end).readout - % are not available 02.05.2014 PC - if ~exist('zerolen', 'var') - zerolen = []; - for groupInd = 1:length(grpdef.pulses.groups) - tempGrp = load([plsdata.grpdir, 'pg_', grpdef.pulses.groups{groupInd}]); - zerolen = vertcat(zerolen, tempGrp.zerolen); - end - plslog(end).readout = tempGrp.plslog(end).readout; - awgdata(a).pulsegroups(gind).lastload = now; - clear tempGrp groupInd - end; - - awgdata(a).pulsegroups(gind).zerolen = zerolen; % Cache some handy stuff here. - awgdata(a).pulsegroups(gind).readout=plslog(end).readout; - - if usetrig - fprintf(awgdata(a).awg, sprintf('SEQ:ELEM%d:WAV1 "trig_%08d"', startline, awgdata(a).triglen)); - for j = 2:nchan - fprintf(awgdata(a).awg, sprintf('SEQ:ELEM%d:WAV%d "zero_%08d_%d"', startline, j, awgdata(a).triglen, awgdata(a).zerochan(j))); - end - if isfield(awgdata(a),'slave') && ~isempty(awgdata(a).slave) && (awgdata(a).slave) - fprintf(awgdata(a).awg, sprintf('SEQ:ELEM%d:TWAIT 1\n', startline)); - end - end - - - for i = 1:npls - ind = i-1 + startline + usetrig; - if ~seqmerge % pulses combined here. - for j = 1:nchan - ch = find(awgdata(a).chans(j) == grpdef.chan); - if ~isempty(ch) && zerolen(grpdef.pulseind(i), ch) < 0 - % channel in group and not zero - fprintf(awgdata(a).awg, sprintf('SEQ:ELEM%d:WAV%d "%s_%05d_%d"', ind, j, ... - grpdef.name, grpdef.pulseind(i), ch)); - else - % hack alert. We should really make zerolen a cell array. fixme. - fprintf(awgdata(a).awg, sprintf('SEQ:ELEM%d:WAV%d "zero_%08d_%d"', ind, j, ... - zlmult*abs(zerolen(grpdef.pulseind(i), 1))*awgdata(a).clk/awgdata(1).clk,awgdata(a).zerochan(j))); - end - end - else % completely overhauled 02.05.2014 PC - for m = 1:length(grpdef.pulses.groups) - for j = 1:nchan % channels of component groups - ch = find(awgdata(a).chans(j) == chan{m}); - if grpdef.pulseind(m, i) > 0 % Do not add if pulseind == 0 - if ~isempty(ch) && zerolen(grpdef.pulseind(m, i), ch) < 0 - % channel in group and not zero - fprintf(awgdata(a).awg, sprintf('SEQ:ELEM%d:WAV%d "%s_%05d_%d"', ind, j, ... - grpdef.pulses.groups{m}, grpdef.pulseind(m, i), ch)); - else - fprintf(awgdata.awg, sprintf('SEQ:ELEM%d:WAV%d "zero_%08d_%d"', ind, j, ... - zlmult*abs(zerolen(grpdef.pulseind(m, i), 1))*awgdata(a).clk/awgdata(1).clk,awgdata(a).zerochan(j))); - end - end; - end - end - end - if grpdef.nrep(min(i, end)) == Inf || grpdef.nrep(min(i, end)) == 0 ... - || (i == npls && isempty(strfind(grpdef.ctrl, 'loop')) && (isempty(grpdef.jump) || all(grpdef.jump(1, :) ~= i))) - fprintf(awgdata(a).awg, sprintf('SEQ:ELEM%d:LOOP:INF 1', ind)); - else - fprintf(awgdata(a).awg, 'SEQ:ELEM%d:LOOP:INF 0', ind); % default - fprintf(awgdata(a).awg, sprintf('SEQ:ELEM%d:LOOP:COUN %d', ind, grpdef.nrep(min(i, end)))); - end - - fprintf(awgdata(a).awg, sprintf('SEQ:ELEM%d:GOTO:STAT 0', ind)); - - if grpdef.nrep(min(i, end)) == Inf && isreal(grpdef.pulses) && ... - (length(awgdata(a).seqpulses) < ind || awgdata(a).seqpulses(ind) ~= grpdef.pulses(grpdef.pulseind(i))); - dosave = 1; - awgdata(a).seqpulses(ind) = grpdef.pulses(grpdef.pulseind(i)); - end - if ~mod(i, 100) - fprintf('%i/%i pulses added.\n', i, npls); - end - end - %fprintf('Group load time: %g secs\n',toc-gstart); - - jstart=toc; - % event jumps - %SEQ:ELEM%d:JTARget:IND - %SEQ:ELEM%d:JTARget:TYPE - - for j = 1:size(grpdef.jump, 2) - fprintf(awgdata(a).awg, sprintf('SEQ:ELEM%d:GOTO:IND %d', startline+usetrig-1 + grpdef.jump(:, j))); - fprintf(awgdata(a).awg, sprintf('SEQ:ELEM%d:GOTO:STAT 1', startline+usetrig-1 + grpdef.jump(1, j))); - end - end - if ~exist('seqlog','var') - seqlog.time = now; - else - seqlog(end+1).time = now; - end - seqlog(end).nrep = grpdef.nrep; - seqlog(end).jump = grpdef.jump; - - save([plsdata.grpdir, 'pg_', groups{k}], '-append', 'seqlog'); - %fprintf('Jump program time: %f secs\n',toc-jstart); - wstart=toc; - awgcntrl('wait'); - %fprintf('Wait time: %f secs; total time %f secs\n',toc-wstart,toc-astart); - nerr=0; - for a=1:length(awgdata(a)) - err=query(awgdata(a).awg, 'SYST:ERR?'); - if ~isempty(strfind(err, 'No error')) - nerr=nerr+1; - end - end - if nerr == 0 - fprintf('Added group %s on index %i. %s', grpdef.name, gind, err); - logentry('Added group %s on index %i.', grpdef.name, gind); - end -end -if dosave - awgsavedata; -end diff --git a/awgclear.m b/awgclear.m deleted file mode 100755 index c527442..0000000 --- a/awgclear.m +++ /dev/null @@ -1,97 +0,0 @@ -function awgclear(groups,options) -% awgclear(groups) -% OR -% awgclear('all') -% awgclear('pack') removes all groups, adds back groups loaded in sequences -% awgclear('all','paranoid') removes all waveforms, including those not known to be loaded. -% awgclear('pack','paranoid') similar - -% (c) 2010 Hendrik Bluhm. Please see LICENSE and COPYRIGHT information in plssetup.m. - -if ~exist('options','var') - options=''; -end -global awgdata; -global plsdata; - -if strcmp(groups, 'pack') - grps={awgdata(1).pulsegroups.name}; - awgclear('all',options); - awgadd(grps); - return; -end - -if strcmp(groups, 'all') - % Mark only groups known to be loaded as loaded. - if isempty(strfind(options,'paranoid')) - g=awgwaveforms; - else % Mark all pulse groups as not loaded - g=plsinfo('ls'); - end - for a=1:length(awgdata) - fprintf(awgdata(a).awg,'WLIS:WAV:DEL ALL\n') - awgdata(a).zeropls=[]; - end - - logentry('Cleared all pulses.'); - for i=1:length(g) - load([plsdata.grpdir, 'pg_', g{i}, '.mat'], 'plslog'); - if(plslog(end).time(end) <= 0) - %fprintf('Skipping group ''%s''; already unloaded\n',g{i}); - else - plslog(end).time(end+1) = -now; - save([plsdata.grpdir, 'pg_', g{i}, '.mat'], 'plslog','-append'); - %fprintf('Marking group ''%s'' as unloaded\n',g{i}); - end - end - awgrm('all'); - return; -end - -if strcmp(groups,'unused') - g=awgwaveforms; - g2={awgdata(1).pulsegroups.name}; - groups=setdiff(g,g2); - for i=1:length(groups) - fprintf('Unloading %s\n',groups{i}); - end -end - -if ischar(groups) - groups = {groups}; -end -tic; -for a=1:length(awgdata) - if isreal(groups) - groups = sort(groups, 'descend'); - for i = groups - wf = query(awgdata(a).awg, sprintf('WLIS:NAME? %d', i)); - if ~query(awgdata(a).awg, sprintf('WLIS:WAV:PRED? %s', wf), '%s\n', '%i') - fprintf(awgdata(a).awg, 'WLIS:WAV:DEL %s', wf); - end - if toc > 20 - fprintf('%i/%i\n', i, length(groups)); - tic; - end - end - awgcntrl('wait'); - return; - end -end - -for k = 1:length(groups) - load([plsdata.grpdir, 'pg_', groups{k}], 'plslog'); - for a=1:length(awgdata) - wfms=awgwaveforms(groups{k},a,'delete'); - for i=1:length(wfms) - fprintf(awgdata(a).awg, sprintf('WLIS:WAV:DEL "%s"', wfms{i})); - end - end - -plslog(end).time(end+1) = -now; -save([plsdata.grpdir, 'pg_', groups{k}], '-append', 'plslog'); -logentry('Cleared group %s.', groups{k}); -fprintf('Cleared group %s.\n', groups{k}); - -awgrm(groups{k}); -end diff --git a/awgcntrl.m b/awgcntrl.m deleted file mode 100755 index 635bb80..0000000 --- a/awgcntrl.m +++ /dev/null @@ -1,165 +0,0 @@ -function val = awgcntrl(cntrl, chans) -% awgcntrl(cntrl, chans) -% cntrl: stop, start, on off, wait, raw|amp, israw, extoff|exton, isexton, err, clr -% several commands given are processed in order. -% isamp and isexton return a vector of length chans specifying which are -% amp or in exton mode - -% (c) 2010 Hendrik Bluhm. Please see LICENSE and COPYRIGHT information in plssetup.m. - -global awgdata; -val=[]; -if nargin <2 - chans = []; -end - -breaks = [regexp(cntrl, '\<\w'); regexp(cntrl, '\w\>')]; - -for k = 1:size(breaks, 2); - switch cntrl(breaks(1, k):breaks(2, k)) - case 'stop' - for a=1:length(awgdata) - fprintf(awgdata(a).awg, 'AWGC:STOP'); - end - - case 'start' - for a=1:length(awgdata) - fprintf(awgdata(a).awg, 'AWGC:RUN'); - end - awgcntrl('wait'); - - case 'off' - for a=1:length(awgdata) - for i = ch(awgdata(a), chans) - fprintf(awgdata(a).awg, 'OUTPUT%i:STAT 0', i); - end - end - case 'on' - for a=1:length(awgdata) - for i = ch(awgdata(a), chans) - fprintf(awgdata(a).awg, 'OUTPUT%i:STAT 1', i); - end - end - - - case 'wait' - for a=1:length(awgdata) - to = awgdata(a).awg.timeout; - awgdata(a).awg.timeout = 600; - query(awgdata(a).awg, '*OPC?'); - awgdata(a).awg.timeout = to; - end - - case 'raw' - if any(any(~awgcntrl('israw'))) - for a=1:length(awgdata) - if ~is7k(awgdata(a)) - for i = ch(awgdata(a), chans) - fprintf(awgdata(a).awg, 'AWGC:DOUT%i:STAT 1', i); - end - end - end - else - fprintf('Already raw\n'); - end - - case 'amp' - if any(any(awgcntrl('israw'))) - for a=1:length(awgdata) - if ~is7k(awgdata(a)) - for i = ch(awgdata(a), chans) - fprintf(awgdata(a).awg, 'AWGC:DOUT%i:STAT 0', i); - end - end - end - else - fprintf('Already amp\n'); - end - - case 'israw' - - val=[]; - for a=1:length(awgdata) - if ~is7k(awgdata(a)) - for i = ch(awgdata(a), chans) - fprintf(awgdata(a).awg, 'AWGC:DOUT%i:STAT?',i); - val(end+1) = fscanf(awgdata(a).awg,'%f'); - end - end - end - - case 'exton' %adds external DC to outputs specified in chans - for a=1:length(awgdata) - if ~is7k(awgdata(a)) - for i = ch(awgdata(a),chans) - fprintf(awgdata(a).awg, 'SOUR%i:COMB:FEED "ESIG"', i); - end - end - end - - case 'extoff' %turns off external DC - for a=1:length(awgdata) - if ~is7k(awgdata(a)) - for i = ch(awgdata(a),chans) - fprintf(awgdata(a).awg, 'SOUR%i:COMB:FEED ""', i); - end - end - end - case 'isexton' - val=[]; - for a=1:length(awgdata) - if ~is7k(awgdata(a)) - for i = ch(awgdata(a), chans) - - fprintf(awgdata(a).awg, 'SOUR%i:COMB:FEED?',i); - val(end+1) = strcmp(fscanf(awgdata(a).awg, '%f'), 'ESIG'); - end - end - end - case 'err' - for a=1:length(awgdata) - err=query(awgdata(a).awg, 'SYST:ERR?'); - if strcmp(err(1:end-1), '0,"No error"') - % Supress blank error messages. - else - fprintf('%d: %s\n',a,err); - end - end - - case 'clr' - for a=1:length(awgdata) - i = 0; - err2 = sprintf('n/a.\n'); - while 1 - err = query(awgdata(a).awg, 'SYST:ERR?'); - if strcmp(err(1:end-1), '0,"No error"') - if i > 0 - fprintf('%d: %i errors. Last %s', a, i, err2); - end - break; - end - err2 = err; - i = i + 1; - end - end - case 'norm' - for i = 1:4 - fprintf(awgdata.awg, 'SOUR%i:VOLT:AMPL .6', i); - end - case 'dbl' - for i = 1:4 - fprintf(awgdata.awg, 'SOUR%i:VOLT:AMPL 1.2', i); - end - end - end -end - -function chans=ch(awg, chans) - if isempty(chans) - chans=1:length(awg.chans); - end -end - -function val=is7k(awg) - val=length(awg.chans) <= 2; -end \ No newline at end of file diff --git a/awggetdata.m b/awggetdata.m deleted file mode 100755 index 55b9cf9..0000000 --- a/awggetdata.m +++ /dev/null @@ -1,18 +0,0 @@ -function data = awggetdata(time) -% awgloaddata -% load latest awgdata file saved by awgsavedata. - -% (c) 2010 Hendrik Bluhm. Please see LICENSE and COPYRIGHT information in plssetup.m. - - -global plsdata; - -if nargin < 1 - time = inf; -end - -d = dir(sprintf('%sawgdata_*', plsdata.grpdir)); -mi = find([d.datenum] < time, 1, 'last'); -load([plsdata.grpdir, d(mi).name]); - - diff --git a/awggroups.m b/awggroups.m deleted file mode 100755 index c6ffec1..0000000 --- a/awggroups.m +++ /dev/null @@ -1,15 +0,0 @@ -function awggroups(ind) - -% (c) 2010 Hendrik Bluhm. Please see LICENSE and COPYRIGHT information in plssetup.m. - - -global awgdata; - -if nargin < 1 - ind = 1:length(awgdata(1).pulsegroups); -end - -for i = ind - zl=plsinfo('zl',awgdata(1).pulsegroups(i).name); - fprintf('%2i: %-15s (%3i pulses, %5.2f us, %d lines)\n', i, awgdata(1).pulsegroups(i).name, awgdata(1).pulsegroups(i).npulse(1), abs(zl(1)*1e-3),awgdata(1).pulsegroups(i).nline); -end diff --git a/awggrpind.m b/awggrpind.m deleted file mode 100755 index d662ccc..0000000 --- a/awggrpind.m +++ /dev/null @@ -1,32 +0,0 @@ -function grp = awggrpind(grp) -% function grpind = grpind(grp) -% Find group index from name of loaded group. - -% (c) 2010 Hendrik Bluhm. Please see LICENSE and COPYRIGHT information in plssetup.m. - - -global awgdata; - -if ischar(grp) - grp = {grp}; -end - -if ~isfield(awgdata(1).pulsegroups,'name') - names={}; -else - names={awgdata(1).pulsegroups.name}; -end - -if iscell(grp) - for i = 1:length(grp) - grp{i} = max(strmatch(grp{i}, names, 'exact')); - if isempty(grp{i}) - grp{i} = nan; - %fprintf('Group not loaded.\n'); - %error('Group not loaded.'); - end - end - grp = cell2mat(grp); -elseif any(grp > length(awgdata(1).pulsegroups)) - awgerror('Group index too large.'); -end diff --git a/awglist.m b/awglist.m deleted file mode 100755 index b72ece5..0000000 --- a/awglist.m +++ /dev/null @@ -1,30 +0,0 @@ -function awglist(ind,awg) -% function awglist([ind],[awg]) -% List the waveforms present on an awg. If ind is absent, list all waveforms. -% If ind is negative, list the last (-ind) waveforms. -% -% Awg specifies which awg in awgdata(i) to use, and defaults to 1. Nominally -% the output should not depend on awg. - -% (c) 2010 Hendrik Bluhm. Please see LICENSE and COPYRIGHT information in plssetup.m. - - -global awgdata; - -if ~exist('awg','var') || isempty(awg) - awg=1; -end - -if nargin < 1 - ind = 1:query(awgdata(awg).awg, 'WLIS:SIZE?', '%s\n', '%i')-1; -elseif ind < 0 - ind = query(awgdata(awg).awg, 'WLIS:SIZE?', '%s\n', '%i')+(ind:-1); -end - -for i = ind - wf = query(awgdata(awg).awg, sprintf('WLIS:NAME? %d', i)); - if ~query(awgdata(awg).awg, sprintf('WLIS:WAV:PRED? %s', wf), '%s\n', '%i') - fprintf('%i: %s', i, wf); - end -end - diff --git a/awgload.m b/awgload.m deleted file mode 100755 index 5d0fccc..0000000 --- a/awgload.m +++ /dev/null @@ -1,147 +0,0 @@ -function zerolen = awgload(grp, ind) -% zerolen = awgload(grp) -% load pulses from group to AWG. - -% (c) 2010 Hendrik Bluhm. Please see LICENSE and COPYRIGHT information in plssetup.m. - - -%global plsdata; -global awgdata; - -%nchan = length(grp.chan); % alternatively use data size -%nchan = size(grp.pulses(1).data, 1); % assumed same for all. - -awgcntrl('stop'); %changing pulses while running is slow. -awgsyncwaveforms(); % make sure the waveform list is up-to-date. -dosave = false; - -for a=1:length(awgdata) - - dind=find([grp.pulses(1).data.clk] == awgdata(a).clk); - - nchan = length(awgdata(a).chans); % alternatively use awgdata or data size - - - % fixme; emit an error if this changes zerochan and wlist.size ~= 25. - % The screwiness here is to get each channel with a unique offset/scale combo - [offsets offsetchan awgdata(a).zerochan] = unique(awgdata(a).offset./awgdata(a).scale); - offsets=awgdata(a).offset(offsetchan); - - % create trig pulse (and corresponding 0) if waveform list empty. - if query(awgdata(a).awg, 'WLIS:SIZE?', '%s\n', '%i') == 25 % nothing loaded (except predefined) - zdata=zeros(1,awgdata(a).triglen); - zmarker=repmat(1,1,awgdata(a).triglen); - awgloadwfm(a,zdata,zmarker,sprintf('trig_%08d',awgdata(a).triglen),1,1); - - for l=1:length(offsets) - awgloadwfm(a,zdata,zmarker,sprintf('zero_%08d_%d',awgdata(a).triglen,l),offsetchan(l),1); - end - dosave = 1; - awgdata(a).zeropls = awgdata(a).triglen; - end - nzpls=0; - for i = 1:length(grp.pulses) - npts = size(grp.pulses(i).data(dind).wf, 2); - if ~any(awgdata(a).zeropls == npts) % create zero if not existing yet - zdata=zeros(1,npts); - for l=1:length(offsets) - zname=sprintf('zero_%08d_%d', npts, l); - awgloadwfm(a,zdata,zdata,zname,offsetchan(l),1); - end - zdata=[]; - awgdata(a).zeropls(end+1) = npts; - dosave = 1; - end - - for j = 1:size(grp.pulses(i).data(dind).wf, 1) - ch=find(grp.chan(j)==awgdata(a).chans); - if isempty(ch) - continue; - end - if any(abs(grp.pulses(i).data(dind).wf(j, :)) > awgdata(a).scale(ch)/(2^awgdata(a).bits)) || any(grp.pulses(i).data(dind).marker(j,:) ~= 0) - name = sprintf('%s_%05d_%d', grp.name, ind(i), j); - - if isempty(strmatch(name,awgdata(a).waveforms)) - fprintf(awgdata(a).awg, sprintf('WLIS:WAV:NEW "%s",%d,INT', name, npts)); - awgdata(a).waveforms{end+1}=name; - err = query(awgdata(a).awg, 'SYST:ERR?'); - if ~isempty(strfind(err,'E11113')) - fprintf(err(1:end-1)); - error('Error loading waveform; AWG is out of memory. Try awgclear(''all''); '); - end - end - awgloadwfm(a,grp.pulses(i).data(dind).wf(j,:), uint16(grp.pulses(i).data(dind).marker(j,:)), name, ch, 0); - zerolen{a}(ind(i), j) = -npts; - nzpls=1; - else - zerolen{a}(ind(i), j) = npts; - end - end - end - % If no non-zero pulses were loaded, make a dummy waveform so awgclear - % knows this group was in memory. - if nzpls == 0 - name=sprintf('%s_1_1',grp.name); - npts=256; - if isempty(strmatch(name,awgdata(a).waveforms)) - fprintf(awgdata(a).awg, sprintf('WLIS:WAV:NEW "%s",%d,INT', name, npts)); - awgdata(a).waveforms{end+1}=name; - end - awgloadwfm(a,zeros(1,npts), zeros(1,npts), name, 1, 0); - end -end - -% if the pulse group is added, update it's load time. -ind=awggrpind(grp.name); -if ~isnan(ind) - for i=1:length(awgdata) - awgdata(i).pulsegroups(ind).lastload=now; - end -end - -if dosave - awgsavedata; -end - -end - - -function zerolen = awgloadwfm(a, data, marker, name, chan,define) -% a is the awg index. -% Send waveform 'data,marker' to the awg with name 'name' intended for channel c. -% data is scaled and offset by awgdata.scale and awgdata.offset *before* sending. -global awgdata; -start = now; -if exist('define','var') && define - fprintf(awgdata(a).awg, sprintf('WLIS:WAV:NEW "%s",%d,INT', name, length(data))); - awgdata(a).waveforms{end+1}=name; -end -chunksize=65536; -if(size(data,1) > size(data,2)) - data=data'; -end - data=(awgdata(a).offset(min(chan,end)) + data)./awgdata(a).scale(chan) + 1; - tb=find(data > 2); - tl=find(data < 0); - if ~isempty(tb) || ~isempty(tl) - % fprintf('Pulse exceeds allowed range: %g - %g\n',min(data),max(data)); - data(tb) = 2; - data(tl) = 0; - end % 14 bit data offset is hard-coded in the AWG. - data = uint16(min(data*(2^(14-1) - 1), 2^(14)-1)) + uint16(marker) * 2^14; - npts = length(data); - for os=0:chunksize:npts - if os + chunksize >= npts - fwrite(awgdata(a).awg, [sprintf('WLIS:WAV:DATA "%s",%d,%d,#7%07d', name, os, npts-os,2 * (npts-os)),... - typecast(data((os+1):end), 'uint8')]); - else - fwrite(awgdata(a).awg, [sprintf('WLIS:WAV:DATA "%s",%d,%d,#7%07d', name, os, chunksize,2 * chunksize),... - typecast(data((os+1):(os+chunksize)), 'uint8')]); - end - fprintf(awgdata(a).awg,''); - end - time=(now-start)*24*60*60; - %fprintf('Load time: %g seconds for %g points (%g bytes/sec)\n',time, npts,npts*2/time); -end - - diff --git a/awgloaddata.m b/awgloaddata.m deleted file mode 100755 index 73c180a..0000000 --- a/awgloaddata.m +++ /dev/null @@ -1,20 +0,0 @@ -function awgloaddata -% awgloaddata -% load latest awgdata file saved by awgsavedata. - -% (c) 2010 Hendrik Bluhm. Please see LICENSE and COPYRIGHT information in plssetup.m. - - -global awgdata; -global plsdata; - -d = dir(sprintf('%sawgdata_*', plsdata.grpdir)); -[mi, mi] = max([d.datenum]); -load([plsdata.grpdir, d(mi).name]); -if exist('awgdata','var') && isfield(awgdata,'awg') - for a=1:length(awgdata) - data(a).awg = awgdata(a).awg; - end -end -awgdata=data; - diff --git a/awgnpulse.m b/awgnpulse.m deleted file mode 100755 index 63e5d47..0000000 --- a/awgnpulse.m +++ /dev/null @@ -1,20 +0,0 @@ -function awgnpulse(groups, npulse) -% awgnpulse(groups, npulse) -% Set npulse for pulsegroups. - -% (c) 2010 Hendrik Bluhm. Please see LICENSE and COPYRIGHT information in plssetup.m. - - -global awgdata; -groups = awggrpind(groups); - -for a=1:length(awgdata) - - for i = 1:length(groups) - pg.name = awgdata(a).pulsegrous(groups(i)).name; - pg.npulse = npulse(min(i, end)); - pulseupdate(pg); - awgadd(groups(i)); - end - -end diff --git a/awgrm.m b/awgrm.m deleted file mode 100755 index 12de2ec..0000000 --- a/awgrm.m +++ /dev/null @@ -1,49 +0,0 @@ -function awgrm(grp, ctrl) -% awgrm(grp, ctrl) -% grp: 'all' or group name -% ctrl: 'after' remove all following groups -% (otherwise, specified group is removed by removing it and following ones -% and then reloading the latter. - -% (c) 2010 Hendrik Bluhm. Please see LICENSE and COPYRIGHT information in plssetup.m. - - -global awgdata; - - -if strcmp(grp, 'all') - awgcntrl('stop'); - for a=1:length(awgdata) - fprintf(awgdata(a).awg, 'SEQ:LENG 0'); - awgdata(a).pulsegroups = []; - awgdata(a).seqpulses = []; - end - awgsavedata; - return; -end - -grp = awggrpind(grp); %strmatch(grp, strvcat(awgdata.pulsegroups.name), 'exact'); -grp(2:end) = []; -if isnan(grp) - return; -end - -awgcntrl('stop'); - -if exist('ctrl','var') && strfind(ctrl, 'after') - for a=1:length(awgdata) - fprintf(awgdata(a).awg, 'SEQ:LENG %d', awgdata(a).pulsegroups(grp).seqind-1 + sum(awgdata(a).pulsegroups(grp).nline)); - awgdata(a).seqpulses(awgdata(a).pulsegroups(grp).seqind + sum(awgdata(a).pulsegroups(grp).npulse):end) = []; - awgdata(a).pulsegroups(grp+1:end) = []; - end - % may miss trigger line. - return; -end -for a=1:length(awgdata) - fprintf(awgdata(a).awg, 'SEQ:LENG %d', awgdata(a).pulsegroups(grp).seqind-1); - awgdata(a).seqpulses(awgdata(a).pulsegroups(grp).seqind:end) = []; - groups = {awgdata(a).pulsegroups(grp+1:end).name}; - awgdata(a).pulsegroups(grp:end) = []; -end -% log unloading here if necessary -awgadd(groups); diff --git a/awgsavedata.m b/awgsavedata.m deleted file mode 100755 index ef23af9..0000000 --- a/awgsavedata.m +++ /dev/null @@ -1,12 +0,0 @@ -function awgsavedata -% awgsavedata -% save awgdata in plsdata.grpdir, with name generated from date and time. - -% (c) 2010 Hendrik Bluhm. Please see LICENSE and COPYRIGHT information in plssetup.m. - -global awgdata; -global plsdata; - -data = rmfield(awgdata, 'awg'); -time = clock; -save(sprintf('%sawgdata_%02d%02d%02d_%02d%02d', plsdata.grpdir, mod(time(1), 100), time(2:5)), 'data','-v6'); diff --git a/awgseqind.m b/awgseqind.m deleted file mode 100755 index 2d963ee..0000000 --- a/awgseqind.m +++ /dev/null @@ -1,43 +0,0 @@ -function seqind = awgseqind(pulses,rep) -% seqind = awgseqind(pulses, rep) -% Find the pulse line associated with a pulse group or pulse index. -% negative for groups, positive for pulse index. - -% (c) 2010 Hendrik Bluhm. Please see LICENSE and COPYRIGHT information in plssetup.m. - - -global awgdata; -if isstruct(pulses) - rep=[pulses.rep]; - pulses=[pulses.pulses]; -elseif ischar(pulses) - pulses = {pulses}; -end - -seqind = nan(1, length(pulses)); -for i = 1:length(pulses) - if iscell(pulses) % could allow ints as well - ind = strmatch(pulses{i}, {awgdata(1).pulsegroups.name}, 'exact'); - if isempty(ind) % no such group - seqind(i) = nan; - else - seqind(i) = awgdata(1).pulsegroups(ind).seqind; - end - elseif pulses(i) > 0 - if(exist('rep', 'var')) - ind = find(pulses(i) == awgdata(1).seqpulses); - ind=ind(rep(i)); - else - ind = find(pulses(i) == awgdata(1).seqpulses, 1); - end - if ~isempty(ind) - seqind(i) = ind; - end - else - seqind(i) = awgdata(1).pulsegroups(-pulses(i)).seqind; - end -end -if any(isnan(seqind)) - fprintf('WARNING: Some pulses not present in sequence.\nHit Ctrl-C to abort, or any key to continue.\n'); - pause; -end diff --git a/awgswap.m b/awgswap.m deleted file mode 100755 index 64013e1..0000000 --- a/awgswap.m +++ /dev/null @@ -1,106 +0,0 @@ -function awgswap(name) -global awgdata; -global smdata; -% strategy: store alternative awgdata sets in awgdata(1).alternates -% store current alternate in awgdata.current -if ~exist('name') || isempty('name') - fprintf('Available sets:\n'); - s=fieldnames(awgdata(1).alternates); - for i=1:length(s) - fprintf('\t%s\n',s{i}); - end - fprintf('Currently selected: %s\n',awgdata(1).current); - return; -end - -% Save the current setting -tmp=nicermfield(awgdata,{'alternates','current'}); -awgdata(1).alternates.(awgdata(1).current) = tmp; - -% Extract the current offsets. -for i=1:length(awgdata) - o(awgdata(i).chans) = awgdata(i).offset; -end - -% Check if new setting exists -if ~isfield(awgdata(1).alternates,name) - error('No AWG alternate named %s\n',name); -end - -if ~strcmp(name,'current') - fprintf('WARNING: Changing awg configuration. Some\n awgclear(''pack'',''paranoid'')\n is probably in order\n'); -end - -% Load the new setting. -tmp=awgdata(1).alternates; -awgdata=awgdata(1).alternates.(name); -awgdata(1).current=name; -awgdata(1).alternates = tmp; - -% Restore the offsets -for i=1:length(awgdata) - awgdata(i).offset = o(awgdata(i).chans); -end - -% Figure out new master/slave relationships -master=nan; -for i=1:length(awgdata) - if ~isfield(awgdata(i),'slave') || isempty(awgdata(i).slave) || ~awgdata(i).slave - master = i; - break; - end -end -if isnan(master) - error('No master AWG defined!\n'); -end -slaves = setdiff(1:length(awgdata), master); - -% Figure out what instruments different AWG's correspond to. -for i=1:length(awgdata) - insts(i) = findawg(awgdata(i)); -end - -if ~isempty(slaves) - for i=1:(length(slaves)-1) - smdata.inst(insts(slaves(i))).data.chain = insts(slaves(i+1)); - end - smdata.inst(insts(slaves(end))).data.chain = insts(master); -end - -smdata.inst(insts(master)).data.chain=[]; - -% Rewire pulseline to control the first slave. -plc = smchanlookup('PulseLine'); -if isempty(slaves) - smdata.channels(29).instchan(1) = insts(master); -else - smdata.channels(29).instchan(1) = insts(slaves(1)); -end - -end - -function s = nicermfield(s, fields) - if ~iscell(fields) - fields={fields}; - end - for i=1:length(fields) - if isfield(s,fields{i}) - s=rmfield(s,fields{i}); - end - end -end - -function val = findawg(awgdata) - global smdata; - val=nan; - for i=1:length(smdata.inst) - try - if smdata.inst(i).data.inst == awgdata.awg - val = i; - return; - end - catch - end - end - -end \ No newline at end of file diff --git a/awgsyncwaveforms.m b/awgsyncwaveforms.m deleted file mode 100755 index ea77a7d..0000000 --- a/awgsyncwaveforms.m +++ /dev/null @@ -1,20 +0,0 @@ -function awgsyncwaveforms() -% Make sure the list of pulses in awgdata is consistent with the awg. -% we assume if the number of pulses is right, everything is.\ -global awgdata; - awgcntrl('clr'); - - for a=1:length(awgdata) - npls=str2num(query(awgdata(a).awg,'WLIS:SIZE?')); - if isfield(awgdata(a),'waveforms') && (length(awgdata(a).waveforms) == npls) - return; - end - fprintf('AWG waveform list out of date. Syncing.'); - awgdata(a).waveforms=cell(npls,1); - for l=1:npls - r=query(awgdata(a).awg,sprintf('WLIS:NAME? %d',l-1)); - awgdata(a).waveforms{l}=r(2:end-2); %-2 since communication adds newline at end - end - fprintf('.. Done.\n'); - end -end \ No newline at end of file diff --git a/awgupdate.m b/awgupdate.m deleted file mode 100755 index f058562..0000000 --- a/awgupdate.m +++ /dev/null @@ -1,72 +0,0 @@ -function awgupdate(groups) -% Obsolete! awgadd now also updates groups already loaded. -% awgupdate(groups) -% Change nrep or jump of previously loaded groups -% Nothing done if fields don't exist. - -% (c) 2010 Hendrik Bluhm. Please see LICENSE and COPYRIGHT information in plssetup.m. - - -global plsdata; -global awgdata; - -awgcntrl('stop'); - -groups = awggrpind(groups); - -for k = 1:length(groups) - if isnan(groups) - continue; - end - - load([plsdata.grpdir, 'pg_', awgdata.pulsegroups(groups(k)).name]); - - if isfield(grpdef, 'pulseind') - npls = length(grpdef.pulseind); - else - npls = size(zerolen, 1); - end - %awgdata.pulsegroups(groups(k)).npulse; - startline = awgdata.pulsegroups(groups(k)).seqind + (grpdef.nrep(1) ~= Inf && isempty(strfind(grpdef.ctrl, 'notrig'))); - - if isfield(grpdef, 'nrep') - for i = 1:npls - ind = i-1 + startline; - - if grpdef.nrep(min(i, end)) == Inf || grpdef.nrep(min(i, end)) == 0 ... - || (i == npls && isempty(strfind(grpdef.ctrl, 'loop')) && all(grpdef.jump(1, :) ~= i)) - - fprintf(awgdata.awg, sprintf('SEQ:ELEM%d:LOOP:INF 1', ind)); - else - fprintf(awgdata.awg, 'SEQ:ELEM%d:LOOP:INF 0', ind); % needed? - fprintf(awgdata.awg, sprintf('SEQ:ELEM%d:LOOP:COUN %d', ind, grpdef.nrep(min(i, end)))); - end - - end - end - - if isfield(grpdef, 'jump') - % event jumps - %SEQ:ELEM%d:JTARget:IND - %SEQ:ELEM%d:JTARget:TYPE - - for j = 1:size(grpdef.jump, 2) - fprintf(awgdata.awg, sprintf('SEQ:ELEM%d:GOTO:IND %d', startline-1 + grpdef.jump(:, j))); - fprintf(awgdata.awg, sprintf('SEQ:ELEM%d:GOTO:STAT 1', startline-1 + grpdef.jump(1, j))); - end - end - - if ~exist('seqlog','var') - seqlog=struct(); - end - seqlog(end+1).time = now; - seqlog(end).nrep = grpdef.nrep; - seqlog(end).jump = grpdef.jump; - - save([plsdata.grpdir, 'pg_', awgdata.pulsegroups(groups(k)).name], '-append', 'seqlog'); - err=query(awgdata.awg, 'SYST:ERR?'); - if isempty(strfind(err, 'No error')) - fprintf('Updated group %s on index %i. %s', grpdef.name, groups(k), err)); - end - logentry('Updated group %s on index %i.', grpdef.name, groups(k)); -end diff --git a/awgwaveforms.m b/awgwaveforms.m deleted file mode 100755 index dcf1ace..0000000 --- a/awgwaveforms.m +++ /dev/null @@ -1,34 +0,0 @@ -function waveforms = awgwaveforms(group,awg,opts) -% function waveforms = awgwaveforms(group,awg,opts) -% Give a list of waveforms that are known to be loaded. -% no arguments: return a list of group names -% if group is specified, give the names of the waveforms in that group -% if awg is specified, work on awg awg -% if 'opts' is 'delete' and group is specifed, erase the waveforms from the table. -global awgdata; -awgsyncwaveforms(); -if ~exist('awg','var') || isempty(awg) - awg=1; -end - - -if ~exist('group','var') || isempty(group) - waveforms = regexp(awgdata(awg).waveforms,'(.*)_\d+_\d+','tokens'); - waveforms = [waveforms{:}]; - waveforms = [waveforms{:}]; - waveforms=unique(sort(waveforms)); - i=strmatch('zero',waveforms,'exact'); - waveforms(i)=[]; - i=strmatch('trig',waveforms); - waveforms(i)=[]; -else - waveforms = regexp(awgdata(awg).waveforms,sprintf('(%s)_\\d+_\\d+',group)); - ind = find([cellfun(@(x) ~isempty(x) && x == 1 , waveforms)]); - - waveforms=awgdata(awg).waveforms(ind); - if exist('opts','var') && strcmp(opts,'delete') - awgdata(awg).waveforms(ind)=[]; - end -end - -end diff --git a/awgzero.m b/awgzero.m deleted file mode 100755 index c9136ed..0000000 --- a/awgzero.m +++ /dev/null @@ -1,23 +0,0 @@ -function zerolen = awgzero(grp, ind, zerolen) -% zerolen = awgzero(grp, ind, zerolen,awg) -% determine if pulse is zero (helper function) - -% (c) 2010 Hendrik Bluhm. Please see LICENSE and COPYRIGHT information in plssetup.m. - - -%global plsdata; -global awgdata; -for awg=1:length(awgdata) - scale=min(awgdata(awg).scale/2^(awgdata(awg).bits-1)); - for i = 1:length(grp.pulses) - dind = find([grp.pulses(i).data.clk] == awgdata(awg).clk); - npts = size(grp.pulses(i).data(dind).wf, 2); - for j=1:size(grp.pulses(i).data(dind).wf,1) % FIXME; channel mappings not honored here. - if any(abs(grp.pulses(i).data(dind).wf(j,:) > awgdata(awg).scale(min(j,end))/(2^awgdata(awg).bits))) - zerolen{awg}(ind(i),j) = -npts; - else - zerolen{awg}(ind(i),j) = npts; - end - end - end -end diff --git a/correlation.m b/correlation.m new file mode 100644 index 0000000..2ed1824 --- /dev/null +++ b/correlation.m @@ -0,0 +1,29 @@ +function retval = isCorrelated(expected,measured,maxSingle,maxAll) +retval = true; +% expected = randomList(1000,0); +% measured = disturbList(expected,0,0); +diffvec = abs(expected - measured); +if (max(diffvec) < maxSingle): + Disp('Single value differs to wide') + retval = false; + plot(diffvec,'.') +if (sum(diffvec.^2))/length(diffvec) < maxAll): + Disp('Overall values differs to wide') + retval = false; + plot(diffvec, '.') +else + retval = true; +end +end +end + +function retval = randomList(length,seed) +rng(seed); +retval = rand(1,length)*2-1; +end + +function retval = disturbList(list,disturbance,seed) +rng(seed); +lengthOfTheDisturbance = length(list); +retval = list + (rand(1,lengthOfTheDisturbance)-1)*disturbance; +end \ No newline at end of file diff --git a/doc/awgsetup.m b/doc/awgsetup.m deleted file mode 100644 index 716c1bd..0000000 --- a/doc/awgsetup.m +++ /dev/null @@ -1,32 +0,0 @@ -%% create awg structure from scratch -clear global awgdata; -global awgdata; - -% number of instrument channels -nChan = 4; - - -awgdata.chans = 1:nChan; % determines order of channels, not used -awgdata.scale = ones(1,nChan); % pre-scale waveform valus before upload. - % Used with offset to relate arbitrary - % pulse scale to, e.g. mV -awgdata.pulsegroups = []; % pulsegroups synchroniced with the AWG -awgdata.zeropls = []; % available placeholder zero-pulse lengths -awgdata.triglen = 1200; % length of optional trigger pulse preceding a group -awgdata.clk = 1.2e9; % number of samples per second of instrument -awgdata.seqpulses = []; % !!! functinoality not clear -awgdata.waveforms = {''}; % waveforms present on instrument -awgdata.offset = zeros(1,nChan); % pre-offset waveform valus before upload -awgdata.zerochan = ones(1,nChan); % !!! functinoality not clear -awgdata.bits = 14; % bit-resolution of instrument. - % in case of AWG7000 12bit may be useable, check -awgdata.slave = []; % !!! elaborate -awgdata.current = 'awg5k'; % name of the current instrument -awgdata.awg=[]; % handle to the openend instrument object -awgdata.alternates = awgdata; % alternative configurations of awgdata - -% save current awg -awgsavedata; - -% clean up -clear nChan; \ No newline at end of file diff --git a/doc/figures.odp b/doc/figures.odp deleted file mode 100644 index c07310c..0000000 Binary files a/doc/figures.odp and /dev/null differ diff --git a/doc/group_struct.png b/doc/group_struct.png deleted file mode 100644 index 6c3977d..0000000 Binary files a/doc/group_struct.png and /dev/null differ diff --git a/doc/helptoc.xml b/doc/helptoc.xml deleted file mode 100644 index 71f6f95..0000000 --- a/doc/helptoc.xml +++ /dev/null @@ -1,54 +0,0 @@ - - - - - - Special Measure Pulsecontrol - - - - - - - - - - - - About Special Measure Pulsecontrol - System Requirements - - Features - - - - User Guide - - - - Function Reference - - AWG Control - awgadd - - Pulse Database Management - plstotab - - - - - - - - - - - Special Measure Pulsecontrol on Google Code - - - \ No newline at end of file diff --git a/doc/mxdom2simplehtml.xsl b/doc/mxdom2simplehtml.xsl deleted file mode 100755 index da90782..0000000 --- a/doc/mxdom2simplehtml.xsl +++ /dev/null @@ -1,360 +0,0 @@ - - - - - ]> - - - - - - - - - - - - - - - - - - - -This HTML was auto-generated from MATLAB code. -To make changes, update the MATLAB code and republish this document. - - - <xsl:value-of select="$title"/> - - - MATLAB - - - - - - - .m - - - - - - - - - - -
- - - - - - -

- introduction - - /introduction -
- - - - - - - - - - - - - - - - - h1 - h2 - - - - - - - - - - - - - - - - - - - - - - - -
- - - - - -
- - - - - - - - - - - - - - - - - -

Contents

-
    - - -
  • #
  • -
    -
    -
-
- - - - -

-
- -
-
- -
-
- -
  • -
    - - - -
    -
    - -
    -
    -
    -
    - - - - - - - - - - - - - - - - - - - - - - - -
    
    -
    -
    - - - - - - - -
    -
    -
    -
    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -##### SOURCE BEGIN ##### - -##### SOURCE END ##### - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    diff --git a/doc/namespace.png b/doc/namespace.png deleted file mode 100644 index e534256..0000000 Binary files a/doc/namespace.png and /dev/null differ diff --git a/doc/plssetup.m b/doc/plssetup.m deleted file mode 100644 index f892c3b..0000000 --- a/doc/plssetup.m +++ /dev/null @@ -1,32 +0,0 @@ -%% create pulse database from scratch for iq mixer calibration -clear global plsdata; -global plsdata; - -% name of database -dbName = 'iqmx'; - -% get the path to this script -[scriptPath, ~, ~] = fileparts(mfilename('fullpath')); - -% save pathes in database -plsdata.datafile = [scriptPath '/awg_pulses/plsdata_' dbName '.mat']; -plsdata.grpdir = [scriptPath '/awg_pulses/pulsegroups_' dbName '/']; - -% create pulse structure -% plsdata.pulses = struct('data', {}, 'format', {}, ... -% 'xval',{}, 'taurc',{}, 'name',{},'trafofn',{},'pardef',{}); -plsdata.pulses = struct('name', {}, 'data', {}, ... - 'xval',{}, 'taurc',{}, 'pardef',{},'trafofn',{},'format',{}); - -% set timebase to microseconds -plsdata.tbase = 1000; - -% create folders -mkdir 'awg_pulses'; -mkdir(plsdata.grpdir); - -% save database -plssync('save'); - -% clean up -clear dbName scriptPath; \ No newline at end of file diff --git a/doc/pulse_struct.png b/doc/pulse_struct.png deleted file mode 100644 index 05daaf8..0000000 Binary files a/doc/pulse_struct.png and /dev/null differ diff --git a/doc/pulsecontrol_features.html b/doc/pulsecontrol_features.html deleted file mode 100644 index d4b113d..0000000 --- a/doc/pulsecontrol_features.html +++ /dev/null @@ -1,83 +0,0 @@ - - - - - Special Measure Pulsecontrol Features

    Special Measure Pulsecontrol Features

    Special Measure Pulsecontrol can be used to:

    • create pulses for Arbitrary Waveform Generator (AWG) instruments on different levels of sophistication.
    • mange pulses as groups in a sequence on one or several AWGs at once.
    • mirror AWG states inside MATLAB®, adjust global offset and other calibration parameters for optimized pulse generation.
    • backup AWG content changes for automatic documentation purposes.
    • mange pulses in a global pulse database (plsdata).
    • examin pulses from plsdata graphically before upload to an AWG.
    \ No newline at end of file diff --git a/doc/pulsecontrol_features.m b/doc/pulsecontrol_features.m deleted file mode 100644 index 169f386..0000000 --- a/doc/pulsecontrol_features.m +++ /dev/null @@ -1,14 +0,0 @@ -%% Special Measure Pulsecontrol Features -% -% Special Measure Pulsecontrol can be used to: -% -% * create pulses for Arbitrary Waveform Generator (AWG) instruments on -% different levels of sophistication. -% * mange pulses as groups in a sequence on one or several AWGs at once. -% * mirror AWG states inside MATLAB(R), adjust global offset and other -% calibration parameters for optimized pulse generation. -% * backup AWG content changes for automatic documentation purposes. -% * mange pulses in a global pulse database (|plsdata|). -% * examin pulses from |plsdata| graphically before upload to an AWG. -% -% Copyright 2009 Not The MathWorks, Inc. \ No newline at end of file diff --git a/doc/pulsecontrol_functions_by_cat.html b/doc/pulsecontrol_functions_by_cat.html deleted file mode 100644 index e634648..0000000 --- a/doc/pulsecontrol_functions_by_cat.html +++ /dev/null @@ -1,425 +0,0 @@ - - - - - Functions by Category

    Functions by Category

    Special Measure Pulsecontrol

    Requires Instrument Control Toolbox™.

    Contents

    AWG Control

    awgadd(groups)

    Uploads one or more pulsegroups to the connected AWG.

    -
    -

    awgclear(groups,options)

    Used to delete pulses from the AWG or clear them up.

    -
    -

    val = awgcntrl(cntrl, chans))

    Control the AWG to start/stop, etc.

    -
    -

    data = awggetdata(time)

    Load latest awgdata.

    -
    -

    awggroups(ind)

    Prints a list of the pulsegroups in the awgdata struct.

    -
    -

    grp = awggrpind(grp)

    Find group index in awgdata.pulsegroups based on provided group name.

    -
    -

    awglist(ind,awg)

    List the waveforms present on an AWG.

    -
    -

    zerolen = awgload(grp, ind)

    Transmit pulses from group to AWG.

    -
    -

    awgloaddata()

    Load latest awgdata file saved by awgsavedata().

    -
    -

    awgnpulse(groups, npulse)

    Set npulse for pulsegroups.

    -
    -

    awgrm(grp, ctrl)

    Used to delete pulses from the AWG.

    -
    -

    awgsavedata()

    Save awgdata in plsdata.grpdir, with name generated from date and time.

    -
    -

    seqind = awgseqind(pulses,rep)

    Find the pulse line associated with a pulse group or pulse index.

    -
    -

    awgswap(name)

    Swap the current active AWG with an alternative.

    -
    -

    awgsyncwaveforms()

    Make sure the list of pulses is awgdata is consistent with the AWG.

    -
    -

    awgupdate(groups)

    Obsolete!

    -
    -

    waveforms = awgwaveforms(group,awg,opts)

    Give a list of waveforms that are known to be loaded.

    -
    -

    zerolen = awgzero(grp, ind, zerolen)

    Helper function to determine if a pulse is zero.

    -
    -

    Pulse Databse Management

    pulse = plsdefault(pulse)

    Enshures correct pulse-formating and updates the format-string pulse.format depending on the content of pulse.data.

    -
    -

    inds = plslist(rng, name)

    Tabular representation of specified pulses.

    -
    -

    plsplot(pulse, dict, ctrl)

    Plots pulse using plstowf() as time vs. channelA and channelB, channelA vs. channelB, time vs. markerA and markerB.

    -
    -

    plsreadxval()

    Extracts xval from pulses inside plsdata.pulses database and collecting them in plsdata.xval.

    -
    -

    plsnum = plsreg(pulse, plsnum)

    Adds pulse to plsdata.pulses database, while enshuring correct elements and correct order of elements.

    -
    -

    plssync(ctrl)

    Used to save and load pulse databese plsdata, while enshuring correct path-handling.

    -
    -

    pulse = plstotab(pulse)

    Downconverts pulses of format 'elem' to format 'tab'.

    -
    -

    [I Q] = plsIqFnCreate(coeff, ...)

    Auxiliary function for plstotab(), to process pulse elements of type 'rfpulse'.

    -
    -

    pulse = plstowf(pulse, dict)

    Downconverts pulses of format 'tab' to format 'wf'.

    -
    -

    plsdefgrp(grpdef)

    Enshures correct group-formating. Saves a pulse group to the path specified in plsdata.grpdir.

    -
    -

    plslint(pg)

    Some preliminary checks for a pulsegroups.

    -
    -

    grpdef = plsmakegrp(name, ctrl, ind, opts)

    Processes pulse group to 'wf' format and saves to disk. Used by awgadd(). Can be controlled by a string ctrl containing swiches:

    • 'plot': plot pulse group
    • 'check': check if any voltage value is out of awgdata.scale bounds
    • 'upload': uploads pulse group to the AWG

    Changes to the pulsegroup are logged inside plslog (stored with pulse).

    -
    -

    plsupdate(newdef)

    Updates the fields of a pulsegroup. The fields name, chan, markmap, offset, pulseind, pulses cannot be updated. Formatting of the other fields must remain the same.

    -
    -

    val = plsinfo(ctrl, group, ind, time)

    Used to retrieve fileds of a pulsegroup at a given time or check if the provided group is stale ( previously not existing, old timestamp, ...). Variable at a given time can be reclaimed using the ctrl strings:

    • 'ro': returns readout
    • 'zl': returns zerolen
    • 'gd': returns grpdef
    • 'sl': returns seqlog

    Ctrl strings not honoring time:

    • 'ls': list/returns pulsegroup(s) of provided name(mask) in grpdir. E.g. 'dBz*'
    • 'params': returns params
    • 'pl' or 'log': returns plslog
    • 'rev': prints avalible revisions of the pulse in plslog
    • 'stale': returns, whether the pulse is stale or not
    \ No newline at end of file diff --git a/doc/pulsecontrol_functions_by_cat.m b/doc/pulsecontrol_functions_by_cat.m deleted file mode 100644 index 68b630b..0000000 --- a/doc/pulsecontrol_functions_by_cat.m +++ /dev/null @@ -1,293 +0,0 @@ -%% Functions by Category -% Special Measure Pulsecontrol -% -% Requires Instrument Control Toolbox(TM). -% -%% AWG Control -% -% -% Uploads one or more pulsegroups to the connected AWG. -% -% -%
    -% -% -% -% -% Used to delete pulses from the AWG or clear them up. -% -% -%
    -% -% -% -% -% Control the AWG to start/stop, etc. -% -% -%
    -% -% -% -% -% Load latest awgdata. -% -% -%
    -% -% -% -% -% Prints a list of the pulsegroups in the awgdata struct. -% -% -%
    -% -% -% -% -% Find group index in awgdata.pulsegroups based on provided group name. -% -% -%
    -% -% -% -% -% List the waveforms present on an AWG. -% -% -%
    -% -% -% -% -% Transmit pulses from group to AWG. -% -% -%
    -% -% -% -% -% Load latest awgdata file saved by awgsavedata(). -% -% -%
    -% -% -% -% -% Set npulse for pulsegroups. -% -% -%
    -% -% -% -% -% Used to delete pulses from the AWG. -% -% -%
    -% -% -% -% -% Save awgdata in plsdata.grpdir, with name generated from date and time. -% -% -%
    -% -% -% -% -% Find the pulse line associated with a pulse group or pulse index. -% -% -%
    -% -% -% -% -% Swap the current active AWG with an alternative. -% -% -%
    -% -% -% -% -% Make sure the list of pulses is awgdata is consistent with the AWG. -% -% -%
    -% -% -% -% -% Obsolete! -% -% -%
    -% -% -% -% -% Give a list of waveforms that are known to be loaded. -% -% -%
    -% -% -% -% -% Helper function to determine if a pulse is zero. -% -% -%
    -% -% -%% Pulse Databse Management -% -% -% -% Enshures correct pulse-formating and updates the format-string pulse.format depending on -% the content of pulse.data. -% -% -%
    -% -% -% -% -% Tabular representation of specified pulses. -% -% -%
    -% -% -% -% -% Plots pulse using plstowf() as time vs. channelA and channelB, channelA -% vs. channelB, time vs. markerA and markerB. -% -% -%
    -% -% -% -% -% Extracts xval from pulses inside plsdata.pulses database and collecting -% them in plsdata.xval. -% -% -%
    -% -% -% -% -% Adds pulse to plsdata.pulses database, while enshuring correct elements -% and correct order of elements. -% -% -%
    -% -% -% -% -% Used to save and load pulse databese plsdata, while enshuring correct -% path-handling. -% -% -%
    -% -% -% -% -% Downconverts pulses of format 'elem' to format 'tab'. -% -% -%
    -% -% -% -% -% Auxiliary function for plstotab(), to process pulse elements of type -% 'rfpulse'. -% -% -%
    -% -% -% -% -% Downconverts pulses of format 'tab' to format 'wf'. -% -% -%
    -% -% -% -% -% Enshures correct group-formating. Saves a pulse group to the path -% specified in plsdata.grpdir. -% -% -%
    -% -% -% -% -% Some preliminary checks for a pulsegroups. -% -% -%
    -% -% -% -% -% Processes pulse group to 'wf' format and saves to disk. Used by -% awgadd(). Can be controlled by a string ctrl containing swiches: -% -% * 'plot': plot pulse group -% * 'check': check if any voltage value is out of awgdata.scale bounds -% * 'upload': uploads pulse group to the AWG -% -% Changes to the pulsegroup are logged inside plslog (stored with pulse). -% -% -%
    -% -% -% -% -% Updates the fields of a pulsegroup. The fields name, chan, markmap, offset, pulseind, -% pulses cannot be updated. Formatting of the other fields must remain the -% same. -% -% -%
    -% -% -% -% -% Used to retrieve fileds of a pulsegroup at a given time or -% check if the provided group is stale ( previously not existing, old -% timestamp, ...). -% Variable at a given time can be reclaimed using the ctrl strings: -% -% * 'ro': returns readout -% * 'zl': returns zerolen -% * 'gd': returns grpdef -% * 'sl': returns seqlog -% -% Ctrl strings not honoring time: -% -% * 'ls': list/returns pulsegroup(s) of provided name(mask) in grpdir. E.g. 'dBz*' -% * 'params': returns params -% * 'pl' or 'log': returns plslog -% * 'rev': prints avalible revisions of the pulse in plslog -% * 'stale': returns, whether the pulse is stale or not -% -% Copyright 2007-2009 Not The MathWorks, Inc. diff --git a/doc/pulsecontrol_getting_started.html b/doc/pulsecontrol_getting_started.html deleted file mode 100644 index 7cd5002..0000000 --- a/doc/pulsecontrol_getting_started.html +++ /dev/null @@ -1,261 +0,0 @@ - - - - - Special Measure Pulsecontrol: Getting Started

    Special Measure Pulsecontrol: Getting Started

    Contents

    What Is Pulsecontrol?

    Pulsecontrol is a script bundle with the aim to standardise the operation of Arbitrary Waveform Generators (AWGs) in a MATLAB® context. Pulses for AWGs can be created using a flexible syntax. They are stored in a global database (plsdata). Groups of pulses from plsdata can be transfered to an AWG for execution in sequence.

    An AWG is represented in the data structure awgdata. Changes to pulses loaded to an AWG are documented, by autosaving the current AWG state.

    What Is A Pulse?

    An AWG runs so called waveforms. Waveforms consist of a number of values stepped through with a certain clock rate.

    The values can range from -1 to 1 and are mapped according to the maximum peak-to-peak amplitude on the device to a voltage:

    - - - - -
    voltagenormalized value
    offset - amplitude(p-p)/2-1
    offset + amplitude(p-p)/2+1
    -

    Exceding values are clipped.

    Besides the analog part of the data, a waveform inclueds two digital marker parts, which can be set for every clock individually to 'on' or 'off' on additional output channels. This is useful for triggering other instruments based on the index position within the waveform. More Information on the 14-bit integer encoding used for transmission and storage of pulses on the AWG can be found in awgload.

    Waveforms are produced by a more general data type in Pulsecontrol called a pulse.

    In Pulsecontrol a pulse can be created in several ways defined by the following formats:

    • 'wf' is the most basic format. The pulse is described via data values for every clock of the AWG. This is the format transmitted to the instrument and corresponds to a waveform.
    • 'tab' defines pulses in a tabular manner. Data values are defined at distinct times of the pulse. The intermediate values are ramped.
    • 'elem' defines pulses out of several pulse elements. A pulse element describes distinct shapes controlled by parameters, e.g. 'wait' will stay at at a specific voltage for a given time.

    The format of a pulse is determinded by the string pinf.format where pinf is a data structure defining the pulse.

    Pulses of the format 'elem' and 'tab' are converted into 'wf' when transmitted to the AWG.

    What Setup Do I Need?

    To use Pulsecontrol the AWG must be connected and opened for communication via the Instrument Control Toolbox™. Two global data structures plsdata and awgdata must be present in the MATLAB® workspace. awgdata contains the instrument object, a representation of uploaded pulses and other important poroperties. plsdata contains pulsedefenitions and defines paths where to store groups of pulses, and backup awgdata. The next section describes the creation and upload of a pulse plus the necessery setup.

    Example: Creation Of A Simple Pulse Using Example plsdata, awgdata

    The example data structures plsdata and awgdata can be created by executing the scripts awgsetup.m and plssetup.m Copy them to your own location, for modification purposes.

    plssetup
    -awgsetup
    -

    They define necessary properties in the structures and create a folder structure to store pulses and AWG states for Pulsecontrol. Now a pulse can be defined.

    clear pinf;
    -pinf.name = 'pulse1';
    -pinf.data.pulsetab = [0 1; 0.1 0.3; 0.2 0.4];
    -

    The above code creates a pulse of the format 'tab' with the name 'pulse1'. The format is specified automatically depending on the content of pinf.data. In this case a pulsetab array creates a 'tab' pulse. The first row in the array defines the time. While the subsequent two rows define values at this times. Since start and end value are not equal, the intermediate values are linear interpolated. The length of the rows is arbitrary.

    The atomar unit of time is 1ns. A scaling factor plsdata.tbase is used when evaluating time in Pulsecontrol. The default value is 1000. Thus pinf is a pulse of 1ns* plsdata.tbase *1 = 1us, which reaches 1 starting from 0 in 1200 steps for a clock speed of 1.2GHz of the connected AWG. The used speed can be found in awgdata.clk.

    - - - - -
    timefirst chan.second chan.
    00.10.3
    10.20.4
    -

    Since the values have an arbitrary unit and the resulting voltage is dependent on the actual settings of the AWG peak-to-peak voltage, it is commune to normalize the values to the unit 'mV' using awgdata.scale and when requiered awgdata.offset.

    The created pulse can be plotted using the function plsplot().

    plsplot(pinf);
    -

    pinf is then added into the plsdata.pulses database at the index plsnum.

    plsnum = 1;
    -plsreg(pinf, plsnum);
    -

    Next a pulse group needs to be defined. Only groups can be uploaded to the AWG. In this case the group consist of only one pulse.

    clear pg;
    -pg.name='pulse1_loop';
    -pg.ctrl = 'notrig'; % no trigger signal
    -pg.pulses = plsnum; % index of pulse1
    -pg.nrep = Inf;
    -pg.chan = [1 2];
    -

    Pulse 'pulse1' is selected by the index plsnum of the database. For several pulses an 1xm index array can be specified in pg.pulses, then the pulses are concatenated together.

    pg.nrep can be used to specify the number of repetitions of the group. pg.chan defines which physical channels are used to output the pulse. More advanced options are possible. Further information can be found in the User Guide.

    The function plsdefgrp() is used to save the group as a mat-file to disk inside plsdata.grpdir with the name 'gr_pulse1_loop'.

    plsdefgrp(pg);
    -

    All supported features of a group can be found in the comment of plsdefgrp.

    Finally the group is added to end of the sequence of the connected AWG with:

    awgadd('pulse1_loop')
    -
    \ No newline at end of file diff --git a/doc/pulsecontrol_getting_started.m b/doc/pulsecontrol_getting_started.m deleted file mode 100644 index a409234..0000000 --- a/doc/pulsecontrol_getting_started.m +++ /dev/null @@ -1,163 +0,0 @@ -%% Special Measure Pulsecontrol: Getting Started -% -%% What Is Pulsecontrol? -% Pulsecontrol is a script bundle with the aim to standardise the operation -% of Arbitrary Waveform Generators (AWGs) in a MATLAB(R) context. Pulses -% for AWGs can be created using a flexible syntax. They are stored in a global -% database (|plsdata|). Groups of pulses from |plsdata| can be transfered -% to an AWG for execution in sequence. -% -% An AWG is represented in the data structure |awgdata|. Changes to pulses -% loaded to an AWG are documented, by autosaving the current AWG state. - -%% What Is A Pulse? -% An AWG runs so called waveforms. Waveforms consist of a number of -% values stepped through with a certain clock rate. -% -% The values can range from -1 to 1 and are mapped according to the maximum -% peak-to-peak amplitude on the device to a voltage: -% -% -% -% -% -% -%
    voltagenormalized value
    offset - amplitude(p-p)/2-1
    offset + amplitude(p-p)/2+1
    -% -% -% Exceding values are clipped. -% -% Besides the analog part of the data, a waveform inclueds two digital marker -% parts, which can be set for every clock individually to 'on' or 'off' on -% additional output channels. -% This is useful for triggering other instruments based on the index -% position within the waveform. -% More Information on the 14-bit integer encoding used for -% transmission and storage of pulses on the AWG can be found in -% -% -% Waveforms are produced by a more general data type in Pulsecontrol called -% a pulse. -% -% In Pulsecontrol a pulse can be created in several ways defined by the -% following formats: -% -% * |'wf'| is the most basic format. The pulse is described via -% data values for every clock of the AWG. This is the format transmitted to -% the instrument and corresponds to a waveform. -% * |'tab'| defines pulses in a tabular manner. Data values are defined at -% distinct times of the pulse. The intermediate values are ramped. -% * |'elem'| defines pulses out of several pulse elements. A pulse element -% describes distinct shapes controlled by parameters, e.g. 'wait' will stay -% at at a specific voltage for a given time. -% -% The format of a pulse is determinded by the string |pinf.format| where -% |pinf| is a data structure defining the pulse. -% -% Pulses of the format |'elem'| and |'tab'| are converted into |'wf'| when -% transmitted to the AWG. -% -%% What Setup Do I Need? -% To use Pulsecontrol the AWG must be connected and opened for -% communication via the Instrument Control Toolbox(TM). Two global data -% structures |plsdata| and |awgdata| must be present in the MATLAB(R) workspace. -% |awgdata| contains the instrument object, a representation of uploaded -% pulses and other important poroperties. |plsdata| contains -% pulsedefenitions and defines paths where to store groups of pulses, and -% backup |awgdata|. The next section -% describes the creation and upload of a pulse plus the necessery setup. -% -%% Example: Creation Of A Simple Pulse Using Example |plsdata|, |awgdata| -% The example data structures |plsdata| and |awgdata| can be created by -% executing the scripts and -% -% Copy them to your own location, for modification purposes. - -plssetup -awgsetup - - -%% -% They define necessary properties in the structures and create a folder -% structure to store pulses and AWG states for Pulsecontrol. Now a pulse -% can be defined. - -clear pinf; -pinf.name = 'pulse1'; -pinf.data.pulsetab = [0 1; 0.1 0.3; 0.2 0.4]; - -%% -% The above code creates a pulse of the format |'tab'| with the name -% 'pulse1'. The format is specified automatically depending on the content of -% |pinf.data|. In this case a |pulsetab| array creates a |'tab'| pulse. The -% first row in the array defines the time. While the subsequent two rows -% define values at this times. Since start and end value are not -% equal, the intermediate values are linear interpolated. The length of the -% rows is arbitrary. -% -% The atomar unit of time is 1ns. A scaling factor |plsdata.tbase| is used -% when evaluating time in Pulsecontrol. The default -% value is 1000. Thus |pinf| is a pulse of 1ns* |plsdata.tbase| *1 = 1us, -% which reaches 1 starting from 0 in 1200 steps for a clock speed of 1.2GHz -% of the connected AWG. The used speed can be found in awgdata.clk. -% -% -% -% -% -% -%
    timefirst chan.second chan.
    00.10.3
    10.20.4
    -% -% -% Since the values have an arbitrary unit and the resulting voltage is -% dependent on the actual settings of the AWG peak-to-peak voltage, it is -% commune to normalize the values to the unit 'mV' using awgdata.scale and -% when requiered awgdata.offset. -% -% The created pulse can be plotted using the function |plsplot()|. - -plsplot(pinf); - -%% -% |pinf| is then added into the |plsdata.pulses| database at the index |plsnum|. - -plsnum = 1; -plsreg(pinf, plsnum); - -%% -% Next a pulse group needs to be defined. Only groups can be -% uploaded to the AWG. In this case the group consist of only one pulse. - -clear pg; -pg.name='pulse1_loop'; -pg.ctrl = 'notrig'; % no trigger signal -pg.pulses = plsnum; % index of pulse1 -pg.nrep = Inf; -pg.chan = [1 2]; - -%% -% Pulse 'pulse1' is selected by the index |plsnum| of the database. For several -% pulses an 1xm index array can be specified in |pg.pulses|, then the pulses are -% concatenated together. -% -% |pg.nrep| can be used to specify the number of repetitions of the group. -% |pg.chan| defines which physical channels are used to output the pulse. -% More advanced options are possible. Further information can be found in the -% . -% -% The function |plsdefgrp()| is used to save the group as a mat-file to -% disk inside |plsdata.grpdir| with the name 'gr_pulse1_loop'. - -plsdefgrp(pg); - -%% -% All supported features of a group can be found in the comment of -% -% -% Finally the group is added to end of the sequence of the connected AWG -% with: - -awgadd('pulse1_loop') - -%% -% Copyright 2009 Not The MathWorks, Inc. diff --git a/doc/pulsecontrol_how_to_doc.html b/doc/pulsecontrol_how_to_doc.html deleted file mode 100644 index f849d7a..0000000 --- a/doc/pulsecontrol_how_to_doc.html +++ /dev/null @@ -1,104 +0,0 @@ - - - - - How to extend and modify this documentation using publish()

    How to extend and modify this documentation using publish()

    This documentation follows MATLAB® guidelines defined in Custom Documentation.

    Contents

    Short summary of the documentation structure

    • The content uses html-markup for styling
    • The topics are seperated into m-files, which are parsed with publish() to generate html-files
    • The html-files are linked together in the html/..._product_page.html
    • html/helptoc.xml provides links to main topics in the documentation browser tree-view
    • info.xml directs MATLAB® to the html/ folder containing all documentation files

    The m-files of the existing topics are provided as guidiance to write new topics. Existing spelling mistakes or contentual errors should also be corrected inside the m-files. The files must then be re-published as discussed below.

    How to publish an m-file

    The publish() function is used with the following options:

    opts = struct('outputDir', fileparts(which('pulsecontrol_features.m')),
    -			  'evalCode', false,
    -			  'stylesheet', [fileparts(which('pulsecontrol_features.m')) '/mxdom2simplehtml.xsl']);
    -
    -publish('pulsecontrol_how_to_doc.m', opts);
    -

    This would re-publish the document you are reading right now, if the current folder is the html/ directory containing this documentation.

    The opts structure contains important options for the publish function. A commun stylesheet mxdom2simplehtml.xsl is used throughout all created html-files to enshure a homogenous layout.

    \ No newline at end of file diff --git a/doc/pulsecontrol_how_to_doc.m b/doc/pulsecontrol_how_to_doc.m deleted file mode 100644 index 1ae7a81..0000000 --- a/doc/pulsecontrol_how_to_doc.m +++ /dev/null @@ -1,30 +0,0 @@ -%% How to extend and modify this documentation using |publish()| -% This documentation follows MATLAB(R) guidelines defined in -% . -% -%% Short summary of the documentation structure -% * The content uses for styling -% * The topics are seperated into m-files, which are parsed with |publish()| to generate html-files -% * The html-files are linked together in the html/..._product_page.html -% * html/helptoc.xml provides links to main topics in the documentation browser tree-view -% * info.xml directs MATLAB(R) to the html/ folder containing all documentation files -% -% The m-files of the existing topics are provided as guidiance to write new topics. -% Existing spelling mistakes or contentual errors should also be corrected inside the m-files. -% The files must then be re-published as discussed below. -% -%% How to publish an m-file -% The |publish()| function is used with the following options: -% -opts = struct('outputDir', fileparts(which('pulsecontrol_features.m')),... - 'evalCode', false,... - 'stylesheet', [fileparts(which('pulsecontrol_features.m')) '/mxdom2simplehtml.xsl']); - -publish('pulsecontrol_how_to_doc.m', opts); - -%% -% This would re-publish the document you are reading right now, -% if the current folder is the html/ directory containing this documentation. -%% -% The opts structure contains important options for the publish function. -% A commun stylesheet mxdom2simplehtml.xsl is used throughout all created html-files to enshure a homogenous layout throughout MATLAB(R) versions. \ No newline at end of file diff --git a/doc/pulsecontrol_product_page.html b/doc/pulsecontrol_product_page.html deleted file mode 100644 index cdf2c14..0000000 --- a/doc/pulsecontrol_product_page.html +++ /dev/null @@ -1,86 +0,0 @@ - - - - - Special Measure Pulsecontrol

    Special Measure Pulsecontrol

    Available Documentation

    Also see the pulsecontrol repository on the Google Code Special Measure page.

    \ No newline at end of file diff --git a/doc/pulsecontrol_product_page.m b/doc/pulsecontrol_product_page.m deleted file mode 100644 index 458a91d..0000000 --- a/doc/pulsecontrol_product_page.m +++ /dev/null @@ -1,16 +0,0 @@ -%% Special Measure Pulsecontrol -% -% *Available Documentation* -% -% * -% * -% * -% * -% * -% * -% * -% -% Also see the on the Google Code -% -% -% Copyright 2009 Not The MathWorks, Inc. diff --git a/doc/pulsecontrol_recipes.html b/doc/pulsecontrol_recipes.html deleted file mode 100644 index 3f316bf..0000000 --- a/doc/pulsecontrol_recipes.html +++ /dev/null @@ -1,80 +0,0 @@ - - - - - Recipes

    Recipes

    This is a selection of common code-snippets related to pulsecontrol.

    Contents

    Tomato soup

    • Some fresh tomatos
    • Hot Water
    • Tomato Pasta
    • salt, pepper
    • chopped, fryied onions
    \ No newline at end of file diff --git a/doc/pulsecontrol_recipes.m b/doc/pulsecontrol_recipes.m deleted file mode 100644 index 81c0007..0000000 --- a/doc/pulsecontrol_recipes.m +++ /dev/null @@ -1,11 +0,0 @@ -%% Recipes -% -% This is a selection of common code-snippets related to pulsecontrol. -% -%% Tomato soup -% -% * Some fresh tomatos -% * Hot Water -% * Tomato Pasta -% * salt, pepper -% * chopped, fryied onions \ No newline at end of file diff --git a/doc/pulsecontrol_system_requirements.html b/doc/pulsecontrol_system_requirements.html deleted file mode 100644 index c4d096c..0000000 --- a/doc/pulsecontrol_system_requirements.html +++ /dev/null @@ -1,77 +0,0 @@ - - - - - Special Measure Pulsecontrol System Requirements

    Special Measure Pulsecontrol System Requirements

    Special Measure Pulsecontrol requires MATLAB® and Instrument Control Toolbox™.

    Special Measure is recommended for control of the instrument itself.

    \ No newline at end of file diff --git a/doc/pulsecontrol_system_requirements.m b/doc/pulsecontrol_system_requirements.m deleted file mode 100644 index 2933aa5..0000000 --- a/doc/pulsecontrol_system_requirements.m +++ /dev/null @@ -1,7 +0,0 @@ -%% Special Measure Pulsecontrol System Requirements -% Special Measure Pulsecontrol requires MATLAB(R) and Instrument Control Toolbox(TM). -% -% is -% recommended for control of the instrument itself. -% -% Copyright 2009 Not The MathWorks, Inc. diff --git a/doc/pulsecontrol_troubleshooting.html b/doc/pulsecontrol_troubleshooting.html deleted file mode 100644 index b12da90..0000000 --- a/doc/pulsecontrol_troubleshooting.html +++ /dev/null @@ -1,72 +0,0 @@ - - - - - Troubleshooting

    Troubleshooting

    no trouble if used correctly.

    \ No newline at end of file diff --git a/doc/pulsecontrol_troubleshooting.m b/doc/pulsecontrol_troubleshooting.m deleted file mode 100644 index b289ea7..0000000 --- a/doc/pulsecontrol_troubleshooting.m +++ /dev/null @@ -1,3 +0,0 @@ -%% Troubleshooting -% -% no trouble if used correctly. \ No newline at end of file diff --git a/doc/pulsecontrol_user_guide.html b/doc/pulsecontrol_user_guide.html deleted file mode 100644 index 468271e..0000000 --- a/doc/pulsecontrol_user_guide.html +++ /dev/null @@ -1,664 +0,0 @@ - - - - - Special Measure Pulsecontrol User Guide

    Special Measure Pulsecontrol User Guide

    Contents

    General Structure

    The figure below depicts the general structure of the pulsecontrol package. The functionality is divided up into two main parts. This is indicated with a common prefix awg* or pls*.

    The namespace awg* contains functions that are directly associeted with the transfer of pulses to the physical instrument. Not all functions are intended for direct user interaction. The common used functions are awgadd(), awglist() and awgcntrl().

    The namespace pls* contains functions associeted with the management, grouping and conversion of pulses into different formats. This part of pulsecontrol is not instrument specific. The common used functions are plsreg(), plsdefgrp(), plsplot() and plslist().

    awgdata Structure

    awgdata is a global struct, which makes the instrument object, uploaded pulses, pulsegroups and other important properties availible in a central place. It is a virtual representation of the current state of the connected AWG(s).

    See the comments of the example file for how to setup the awgdata struct: awgsetup example

    Every time the functions awgadd() or awgrm() are called, the updated awgdata is saved inside the director provided in plsdata.grpdir. The format is:

    awgdata_yyMMdd_hhmm

    This provides a history of the awgstate and a backup method. The functions to save and load the latest awgdata manually are awgloaddata() and awgsavedata(). An awgdata struct at a specific time can be accessed with awggetdata(time) with time a serial date number.

    plsdata Structure

    plsdata is a global struct, which contains pulse definitions in a database, provides paths where to store groups of pulses and backup awgdata. Additionally, plsdata.tbase is a common factor to control the unit of time for the whole package.

    See the comments of the example file for how to setup the plsdata struct: plssetup example

    plssync(ctrl) with the ctrl string 'save' or 'load' is used to save or load plsdata from/to the location in plsdata.datafile.

    The pulse types wf, tab, elem

    'wf'

    'wf' is the most basic format. The pulse is described via data values for every clock of the AWG. This is the format transmitted to the instrument and corresponds to a waveform.

    Below is an example of this type:

    clear wf_p;
    -wf_p.name = 'myname1';
    -wf_p.data.wf = ones(2,1200) .* .5;
    -wf_p.data.marker = zeros(2,1200, 'uint8');
    -wf_p.data.clk = 1.2e9;
    -

    wf_p.data contains the raw waveform information. wf and marker contain an entry per clock (here 1200) per channel (here 2). clk is the number of clocks per second. The above pulse will generate a 1 us long pulse of 0.5 and no markers for a clock frequency of 1.2GHz on two channels.

    wf_p is not in the correct format yet. A call of plsdefault() generates additional fields and set the correct wf_p.format string. This function is called automatically when converting from one pulse to another.

    wf_p = plsdefault(wf_p);
    -

    This is equivalent of setting the following fields manually.

    wf_p.format = 'wf';
    -wf_p.taurc = Inf;
    -wf_p.xval = [];
    -wf_p.pardef = [];
    -wf_p.trafofn = [];
    -

    See the following chapter for how to use trafofn, pardef and taurc. The field xval is used by an external data evaluation package dataview to define the scaling of the x-axis. Look into dataview for more information about xval.

    As seen in this example it is enough to define the wf_p.data part of the pulse. The other fields are not necessary to define manually and are generated automatically. They are shown to give an overview of the complete pulse in this and the following example pulses.

    -
    -
    -

    'tab'

    'tab' is used for time-value pairs in a tabular format.

    Below is an example of this pulse type:

    clear tb_p;
    -tb_p.name = 'myname2';
    -tb_p.data.pulsetab = [0 1; 0 0.3; 0.0 0.4];
    -

    The first row in the pulsetab defines the time. While the subsequent two rows define values at this times for channel 1, channel 2, ... Since start and end value are not equal, the intermediate values are linear interpolated (from 0.0 to 0.3 on channel 1 and from 0.0 to 0.4 on channel 2). The length of the rows is arbitrary.

    The atomar unit of time is 1ns. A scaling factor plsdata.tbase is used when evaluating time in Pulsecontrol. The default value is 1000. Thus tb_p is a pulse of 1ns* plsdata.tbase *1 = 1us.

    When downconverted to the 'wf' format the voltage values are linear interpolated. For our example of a 1.2GHz clock frequency the voltage of of channel 1 would rise from 0.1 to 0.3 in 1200 stepps.

    It is also possible to set the field tb_p.data.pulsefn. This replaceses the default linear interpolation with a custume function handle, for arbitrary pulse shapes. Here is an example using two sinusoids:

    tb_p.data.pulsefn.t = [0, 1];
    -tb_p.data.pulsefn.fn = {@(x)sin(2*pi*x), @(x)0.5*sin(2*pi*x)};
    -

    After the pulse table is converted to the 'wf' format, the time segment specified in pulsefn.t is overwritten with pulsefn.fn evaluated at this points. pulsefn can be an array to define several custome funcitons to replace several parts of a pulse with arbitrary functions.

    Again tb_p is not in the correct format yet. A call of plsdefault() generates additional fields and set the correct tb_p.format string

    tb_p = plsdefault(tb_p);
    -

    This is equivalent of setting the following fields manually.

    tb_p.format = 'tab';
    -tb_p.taurc = Inf;
    -tb_p.xval = [];
    -tb_p.pardef = [];
    -tb_p.trafofn = [];
    -

    -
    -
    -

    'elem'

    'elem' is a flexible format to construct a pusle from prefedined building blocks. This pulse elements are defined in plstotab().

    Below is an example of this pulse type:

    clear pdstart pdwait pdramp;
    -
    -pdstart.type = 'raw';
    -pdstart.time = 0;
    -pdstart.val= [0; 0];
    -
    -pdramp.type = 'ramp';
    -pdramp.time = 1;
    -pdramp.val = [0.2, 0.3];
    -
    -pdwait.type = 'wait';
    -pdwait.time = 2;
    -pdwait.val = [1, 1];
    -
    -clear el_p;
    -
    -el_p.name = 'myname3';
    -el_p.data = [pdstart pdramp pdwait];
    -

    The pulse el_p uses three pulse elements pdstart, pdramp and pdwait to define a shape, which starts from zero than rises to 0.2 for channel 1 and 0.3 for channel 2 in 1us. It than jumps to 1.0 for both channels and stays there for 2us.

    Again el_p is not in the correct format yet. A call of plsdefault() generates additional fields and set the correct el_p.format string

    el_p = plsdefault(el_p);
    -

    This is equivalent of setting the following fields manually.

    el_p.format = 'elem';
    -el_p.taurc = Inf;
    -el_p.xval = [];
    -el_p.pardef = [];
    -el_p.trafofn = [];
    -

    Each element is defined by a type and two fields time and val. Dependind on the type the fields provide different parameters to define a pulse element. When downconverted to the 'tab' format correct pulse table entries are derived, to represent the shapes defined by this elements.

    -
    -
    -

    Here is an overview of the currently existing elements:

    raw insert [time; val] into pulse table.

    mark add time' to marktab

    fill stretch this element to make the total pulse duration equal to time. Idea for future development: allow several fills, each spreading a subset. Would need a second element to flush previous fill, could be fill without time.

    wait stay at val (row vector, one entry for each channel) for duration time. If val has 3 entries, third is a scaling factor for the first two.

    reload relaod pulse at val (row vector, one entry for each channel). time: [ramp time, wait time at load point, wait time at (0, 0) after load]

    meas measurement stage at [0, 0] for time(1), RF marker delayed by time(2) and off time(3) before end of the stage. [time(2) is lead delay, time(3) is negative tail delay. Optional val(1) is the readout tag. If it is given and not nan, time 4 and 5 set its delays with the sae convention as for the marker. Optional val(2,3) moves the measurement point away from 0,0. Makes meas_o obsolete.

    meas_o as meas, but measure at current voltages, not 0,0.

    ramp ramp to val (row vector, one entry for each channel) in time. opt val(3) is multiplier

    comp measurement compensation at val(1:2) (one for each channel) for duration time(1). Ramps voltage to target and back over time(2) and time(3) at the beginning and end of the stage, respectively. If length(val)>=4, val(3:4) are used as final value. The compensation value could be determined automatically, but this feature is not implemented yet.

    adprep adiabatic ramp along second diagonal (epsilon) from val(1) to val(2), ramp duration time.

    adread same, going the other way.

    rfpulse RF pulse using an IQ mixer with a mixer calibration present in caldata. Signals for I and Q are generated to produce a sinusoidal controllable in amplitude, phase and frequency. val(1:5)=[freqStart, freqStop, freqLO, amplitude, phase]. Using different values for freqStart and freqStop produces a chirp. val(6:7)=[calnumber, runind] are optional and are used as indices in caldata for a specific calibration, e.g. for multiple mixers. Otherwise the calibration with the name 'iqmx' is used. A starting element can be produced by setting the time to 0. Note: Vp amplitude expected (peak-to-zero amplitude). Specific: Tektroniks AWG expects Vpp on default, so Vpp=2*Vp is used inside rfpulse.

    • how to use dict related functionality

    -
    -
    -

    The figure below is a summary of the discussed pulse formats. It also shows how the pulse information is kept when converting from 'elem' to 'tab' to 'wf'. The original fields are not deletet. They are rather left in the pulse when it is converted. The data array of 'elem' for example is moved into the data.elem field when converting to 'tab'.

    trafofn, pardef, varpar, params, taurc

    implimentation of trafofn, pardef, ... plsmakegrp()

    basic documentation of trafofn, pardef, ... plsdefgrp()

    • pardef working on individual pulse (n x 2)-array: (i,1) pulse element to target, (i,2) if neg. time index/ if pos. val index
    • create variations of pulse based on varpar/parm in one group
    • replacing targeted value generated by trafofn(params(i)) or if trafofn empty params directly
    • works for 'elem' and 'tab'
    • different purpose of trafofn in group struct opposed to pulse struct: transform all final wf-values based on custome function + args
    • taurc is for compensation of physical setup: combination of dc + rf signal from AWG channel, see picture

    This section describes the process of parametrisation for 'elem' or 'tab' type pulses. This are currently the only types of pulses to support this feature.

    Using parametrisation enables the creation of a series of pulses, where an arbitrary parameters is varied.

    Markers

    Besides the analog part of the data, a waveform inclueds two digital marker parts, which can be set for every clock individually to 'on' or 'off' on additional output channels. This is useful for triggering other instruments based on the index position within the waveform.

    The creation of markers is supported on all levels of the pulse format:

    In an 'wf' the marker is set using the data.marker field. The field has to be the same length as the data.wf field. It must contain 0-3 of the type unsigned char. Zero if no marker should be set. One for the first marker on channel one two for the second and three for both markers. For example:

    wf_p.data.marker = zeros(2,1200, 'uint8');
    -wf_p.data.marker(1,1:120) = 1;
    -

    This generates a short marker pulse on the first channel for one tenth of the total pulselength of 1200 for the first marker.

    It is more convinient to use data.marktab when creating a 'tab' pulse. Analogous to the pulsetab the marktab contains the start times in the first rows. The subsequent rows contain the widths of the marker pulses. For two channels one two marker per channel this follows the schema:

    [time; channel1_marker1_duration; channel1_marker2_duration; channel2_marker1_duration; channel2_marker2_duration;]

    Here is an example:

    tb_p.data.marktab = [ 2 ; 1 ; 0 ; 0 ; 1 ];
    -

    This fires marker1 of channel one and marker2 of channel two for 1us starting at 2us.

    When using 'elem' the pulse element 'mark' is used to write to the marktab.

    An advanced feature is the routing of marker channels. It is for example possible to define markers for one channel and have them executed in parallel on additional channels. Or exchange the markers of several channels. This is realised using the markmap field in a pulse group. Consult the section 'groups' below for group specific questions.

    Note that you can only route both markers of a channel at the same time. This is not a problem when using just one marker per channel.

    The usage is defind in plsdefgrp() as follows:

    markmap = [marker_source_channel1 marker_source_channel2, ...; marker_output_channel1 marker_output_channel2, ...]

    An example would be to route the markers from channel one to all channels for four channels in total:

    group.markmap = [1 1 1 1; 1 2 3 4];
    -

    or to exchange the markers of the first two channels:

    group.markmap = [1 2 3 4; 2 1 3 4];
    -

    Groups

    Only groups can be uploaded to the AWG. They consist of a number of pulses in various forms, or just one pulse in the most simple case. The group struct is similar to the struct of the pulse itself and is depicted in the following figure. Refer to the comments in plsdefgrp() for a quick reference.

    The group pg with the name pg.name has a number of fields. The field pg.markmap is discussed in the above section about markers. It is used to map markers from different channels to different marker outputs. The field pg.xval is a merging of the xval fields of the individual pulses. It is used by an external data evaluation package dataview to define the scaling of the x-axis. Look into dataview for more information about xval.

    The fields pg.varpar and pg.params are used to parametrize certain aspects of an 'elem' or 'tab' pulse. Refer to the section above discussing it in connection with pardef and trafofn of the individual pulses inside the group.

    -
    -

    ctrl, nrep, jump, pulseind

    This fields control how the group behaves.

    • explain single fields with example

    -
    -

    chan, matrix, offset, trafofn

    This fields modify the group in several ways and set its output destination.

    • explain single fields with example

    -
    -

    dict

    This is a dictionary of predifined pulse elements used to create 'elem' pulses.

    • ask harvard nicely to write about it and provide example uses

    -
    -

    pulses

    This field contains information about the pulses used in the group.

    The above figure about the pulsegroup struct shows three possible cases of content of the pg.pulses field (starting from the bottom):

    1. The pulses are part of the database in plsdata.pulses and are referenced via their index, e.g., pg.pulses = [1, 2, 3] will use plsdata.pulses(1:3).
    2. An array of pulse structs is provided directly, without the step of retrieving the pulses from the database.
    3. Instead of single pulses use whole groups. This creates a group of groups in a recursive manner. To use this feature pg.pulses.groups has to be an array of already existing groups adressed by their group names, e.g., pg.pulses.groups = {'pg1', 'pg2'} will use pulse groups with the names 'pg1' and 'pg2', which were created with plsdefgrp() earlier to create a new group

    Cases 1 and 2 create groups of the type 'pls', since pulses are used. The third case creates groups of the type 'grp'. The type of group is specified inside the ctrl string pg.ctrl by plsdefgrp() automatically or it can be set manually when the group is defined.

    Besides pg.pulses.groups for type 'grp' the field pg.pulses.chan and pg.pulses.markchan exist to overwrite the output of the inner groups indiviually oposed to pg.chan and pg.markmap for all groups at once. The field pg.pulses.chan has as many rows as inner groups. The columns specify the output channels. The same is true for pg.pulses.markchan. If the output channel is set to 0 the specific output is ignored.

    For the example of using the two inner groups 'pg1' and 'pg2' as in case 3 above:

    pg.pulses.chan = [1 2; 2 0];
    -

    The inner group 'pg1' is routet to channel 1 und 2. The first channel of 'pg2' is routed to the physical channel 2 and the second channel of the group is omitted.

    Workflow

    At first a working configuration of pulsecontrol has to be enshured. Refer to the sections awgdata, plsdata above and the section "What Setup Do I Need?" in the Getting Started guide.

    A typical procedure how to use pulsecontrol is depicted in the figure below.

    1. at first the desiered pulses are defined
    2. the pulses can be plotted by plsplot() to enshure the correct shape visually
    3. using plsreg() the pulses are 'registered' in the plsdata database. Since the database is backad up on disk, this enshures consistency and safty
    4. a pulse group is defined using the pulses created in the first step. It is convinient to use the index of the pulse in the plsdata database. An alternative is to write the pulse struct(s) directly in the .pulses field of the group
    5. the function plsdefgrp() saves the group to disk inside the directory plsdata.grpdir. Only groups saved in this directory can be send to the AWG. This enshures that no information is lost.
    6. finally awgadd() is used to transmit the group or several groups to the AWG
    7. the function plsupdate is used to update some parameters of the group. The fields name, chan, markmap, offset, pulseind, pulses cannot be updated. The modified group can be retransmitted to the AWG to reflect the changes

    Refer to the section "Example: Creation Of A Simple Pulse..." in the Getting Started guide.

    • explain when to use plsinfo()
    • explain how to use plsmakegrp()
    • explain how pulse is documented in plslog and how to retrieve states

    Multiple AWGs

    • remains ToDo
    \ No newline at end of file diff --git a/doc/pulsecontrol_user_guide.m b/doc/pulsecontrol_user_guide.m deleted file mode 100644 index 9b802d8..0000000 --- a/doc/pulsecontrol_user_guide.m +++ /dev/null @@ -1,536 +0,0 @@ -%% Special Measure Pulsecontrol User Guide -% -% -%% General Structure -% The figure below depicts the general structure of the pulsecontrol -% package. The functionality is divided up into two main parts. This is -% indicated with a common prefix awg* or pls*. -% -% The namespace awg* contains -% functions that are directly associeted with the transfer of pulses to the -% physical instrument. Not all functions are intended for direct user -% interaction. The common used functions are awgadd(), awglist() and -% awgcntrl(). -% -% The namespace pls* contains functions associeted with the management, -% grouping and conversion of pulses into different formats. This part of -% pulsecontrol is not instrument specific. The common used functions are -% plsreg(), plsdefgrp(), plsplot() and plslist(). -% -% <> -% -%% awgdata Structure -% awgdata is a global struct, which makes the instrument object, uploaded -% pulses, pulsegroups and other important properties availible in a central -% place. It is a virtual representation of the current state of the -% connected AWG(s). -% -% See the comments of the example file for how to setup the awgdata struct: -% -% -% Every time the functions awgadd() or awgrm() are called, the updated -% awgdata is saved inside the director provided in plsdata.grpdir. The -% format is: -% -% |awgdata_yyMMdd_hhmm| -% -% This provides a history of the awgstate and a backup method. The -% functions to save and load the latest awgdata manually are awgloaddata() -% and awgsavedata(). An awgdata struct at a specific time can be accessed -% with awggetdata(time) with time a serial date number. -% -%% plsdata Structure -% plsdata is a global struct, which contains pulse definitions in a -% database, provides paths where to store groups of pulses and backup -% awgdata. Additionally, plsdata.tbase is a common factor to control the unit -% of time for the whole package. -% -% See the comments of the example file for how to setup the plsdata struct: -% -% -% plssync(ctrl) with the ctrl string 'save' or 'load' is used to save or -% load plsdata from/to the location in plsdata.datafile. -% -%% The pulse types wf, tab, elem -% -% *|'wf'|* -% -% |'wf'| is the most basic format. The pulse is described via data values -% for every clock of the AWG. This is the format transmitted to the -% instrument and corresponds to a waveform. -% -% Below is an example of this type: - -clear wf_p; -wf_p.name = 'myname1'; -wf_p.data.wf = ones(2,1200) .* .5; -wf_p.data.marker = zeros(2,1200, 'uint8'); -wf_p.data.clk = 1.2e9; - -%% -% wf_p.data contains the raw waveform information. wf and marker contain an -% entry per clock (here 1200) per channel (here 2). clk is the number of clocks per -% second. The above pulse will generate a 1 us long pulse of 0.5 and no markers -% for a clock frequency of 1.2GHz on two channels. -% -% wf_p is not in the correct format yet. A call of plsdefault() generates -% additional fields and set the correct wf_p.format string. This function -% is called automatically when converting from one pulse to another. - -wf_p = plsdefault(wf_p); - -%% -% This is equivalent of setting the following fields manually. - -wf_p.format = 'wf'; -wf_p.taurc = Inf; -wf_p.xval = []; -wf_p.pardef = []; -wf_p.trafofn = []; - -%% -% See the following chapter for how to use trafofn, pardef and taurc. The -% field xval is used by an external data evaluation package -% -% to define the scaling of the x-axis. Look into dataview for more -% information about xval. -% -% As seen in this example it is enough to define the wf_p.data part of the -% pulse. The other fields are not necessary to define manually and are generated -% automatically. They are shown to give an overview of the complete -% pulse in this and the following example pulses. -% -% -%
    -%
    -% -% -% *|'tab'|* -% -% |'tab'| is used for time-value pairs in a tabular format. -% -% Below is an example of this pulse type: - -clear tb_p; -tb_p.name = 'myname2'; -tb_p.data.pulsetab = [0 1; 0 0.3; 0.0 0.4]; - -%% -% The first row in the pulsetab defines the time. While the subsequent two -% rows define values at this times for channel 1, channel 2, ... -% Since start and end value are not -% equal, the intermediate values are linear interpolated (from 0.0 -% to 0.3 on channel 1 and from 0.0 to 0.4 on channel 2). The length of -% the rows is arbitrary. -% -% The atomar unit of time is 1ns. A scaling factor plsdata.tbase is used -% when evaluating time in Pulsecontrol. The default value is 1000. Thus -% tb_p is a pulse of 1ns* plsdata.tbase *1 = 1us. -% -% When downconverted to the -% |'wf'| format the voltage values are linear interpolated. For our example -% of a 1.2GHz clock frequency the voltage of of channel 1 would rise from -% 0.1 to 0.3 in 1200 stepps. -% -% It is also possible to set the field tb_p.data.pulsefn. This replaceses the -% default linear interpolation with a custume function handle, for -% arbitrary pulse shapes. Here is an example using two sinusoids: - -tb_p.data.pulsefn.t = [0, 1]; -tb_p.data.pulsefn.fn = {@(x)sin(2*pi*x), @(x)0.5*sin(2*pi*x)}; - -%% -% After the pulse table is converted to the |'wf'| format, the time segment -% specified in pulsefn.t is overwritten with pulsefn.fn evaluated at this -% points. pulsefn can be an array to define several custome funcitons to -% replace several parts of a pulse with arbitrary functions. -% -% Again tb_p is not in the correct format yet. A call of plsdefault() generates -% additional fields and set the correct tb_p.format string - -tb_p = plsdefault(tb_p); - -%% -% This is equivalent of setting the following fields manually. - -tb_p.format = 'tab'; -tb_p.taurc = Inf; -tb_p.xval = []; -tb_p.pardef = []; -tb_p.trafofn = []; - -%% -% -% -%
    -%
    -% -% -% *|'elem'|* -% -% |'elem'| is a flexible format to construct a pusle from prefedined -% building blocks. This pulse elements are defined in -% . -% -% Below is an example of this pulse type: - -clear pdstart pdwait pdramp; - -pdstart.type = 'raw'; -pdstart.time = 0; -pdstart.val= [0; 0]; - -pdramp.type = 'ramp'; -pdramp.time = 1; -pdramp.val = [0.2, 0.3]; - -pdwait.type = 'wait'; -pdwait.time = 2; -pdwait.val = [1, 1]; - -clear el_p; - -el_p.name = 'myname3'; -el_p.data = [pdstart pdramp pdwait]; - -%% -% The pulse el_p uses three pulse elements pdstart, pdramp and pdwait to -% define a shape, which starts from zero than rises to 0.2 for channel 1 -% and 0.3 for channel 2 in 1us. It than jumps to 1.0 for both channels and -% stays there for 2us. -% -% Again el_p is not in the correct format yet. A call of plsdefault() generates -% additional fields and set the correct el_p.format string - -el_p = plsdefault(el_p); - -%% -% This is equivalent of setting the following fields manually. - -el_p.format = 'elem'; -el_p.taurc = Inf; -el_p.xval = []; -el_p.pardef = []; -el_p.trafofn = []; - -%% -% Each element is defined by a type and two fields time and val. Dependind -% on the type the fields provide different parameters to define a pulse -% element. When downconverted to the |'tab'| format correct pulse table -% entries are derived, to represent the shapes defined by this elements. -% -% -% -%
    -%
    -% -% -% Here is an overview of the currently existing elements: -% -% *raw* insert [time; val] into pulse table. -% -% *mark* add time' to marktab -% -% *fill* stretch this element to make the total pulse duration equal to time. -% Idea for future development: allow several fills, each spreading a subset. -% Would need a second element to flush previous fill, could be fill without time. -% -% *wait* stay at val (row vector, one entry for each channel) for duration time. -% If val has 3 entries, third is a scaling factor for the first two. -% -% *reload* relaod pulse at val (row vector, one entry for each channel). -% time: [ramp time, wait time at load point, wait time at (0, 0) after load] -% -% *meas* measurement stage at [0, 0] for time(1), RF marker delayed by time(2) and -% off time(3) before end of the stage. [time(2) is lead delay, -% time(3) is negative tail delay. -% Optional val(1) is the readout tag. If it is given and not nan, time 4 and 5 set its delays -% with the sae convention as for the marker. -% Optional val(2,3) moves the measurement point away from 0,0. Makes -% meas_o obsolete. -% -% *meas_o* as meas, but measure at current voltages, not 0,0. -% -% *ramp* ramp to val (row vector, one entry for each channel) in time. opt val(3) is multiplier -% -% *comp* measurement compensation at val(1:2) (one for each channel) for duration time(1). -% Ramps voltage to target and back over time(2) and time(3) at the beginning and -% end of the stage, respectively. If length(val)>=4, val(3:4) are used as final value. -% The compensation value could be determined automatically, but this feature is not -% implemented yet. -% -% *adprep* adiabatic ramp along second diagonal (epsilon) from val(1) to val(2), ramp duration time. -% -% *adread* same, going the other way. -% -% *rfpulse* RF pulse using an IQ mixer with a mixer calibration present in caldata. -% Signals for I and Q are generated to produce a sinusoidal controllable in amplitude, -% phase and frequency. val(1:5)=[freqStart, freqStop, freqLO, amplitude, phase]. Using different -% values for freqStart and freqStop produces a chirp. val(6:7)=[calnumber, runind] are optional -% and are used as indices in caldata for a specific calibration, e.g. for multiple mixers. -% Otherwise the calibration with the name 'iqmx' is used. A starting element can be produced -% by setting the time to 0. Note: Vp amplitude expected -% (peak-to-zero amplitude). Specific: Tektroniks AWG expects Vpp on default, so -% Vpp=2*Vp is used inside rfpulse. -% -% * how to use dict related functionality -% -% -%
    -%
    -% -% -% The figure below is a summary of the discussed pulse formats. It also -% shows how the pulse information is kept when converting from |'elem'| to -% |'tab'| to |'wf'|. The original fields are not deletet. They are rather -% left in the pulse when it is converted. The data array of |'elem'| for -% example is moved into the data.elem field when converting to |'tab'|. -% -% <> -% -%% trafofn, pardef, varpar, params, taurc -% implimentation of trafofn, pardef, ... -% -% -% basic documentation of trafofn, pardef, ... -% -% -% * pardef working on individual pulse (n x 2)-array: (i,1) pulse element to -% target, (i,2) if neg. time index/ if pos. val index -% * create variations of pulse based on varpar/parm in one group -% * replacing targeted value generated by trafofn(params(i)) or if trafofn empty -% params directly -% * works for |'elem'| and |'tab'| -% * different purpose of trafofn in group struct opposed to pulse struct: -% transform all final wf-values based on custome function + args -% * taurc is for compensation of physical setup: combination of dc + rf -% signal from AWG channel, see picture -% -% <> -% -% This section describes the process of parametrisation for 'elem' or 'tab' -% type pulses. This are currently the only types of pulses to support this -% feature. -% -% Using parametrisation enables the creation of a series of pulses, where -% an arbitrary parameter is varied, e.g the value of a pulse element -% for 'elem': - -pdstart.type = 'raw'; -pdstart.time = 0; -pdstart.val= [0; 0]; - -pdramp.type = 'ramp'; -pdramp.time = 1; -pdramp.val = [0.5, 0.6]; - -el_p.name = 'myname3'; -el_p.data = [pdstart pdramp]; -el_p.pardef = [2 1]; -el_p.trafofn = @(x) x; - -%% Markers -% Besides the analog part of the data, a waveform inclueds two -% digital marker parts, which can be set for every clock individually -% to 'on' or 'off' on additional output channels. This is useful for -% triggering other instruments based on the index position within -% the waveform. -% -% The creation of markers is supported on all levels of the pulse format: -% -% In an 'wf' the marker is set using the data.marker field. The field has -% to be the same length as the data.wf field. It must contain 0-3 of the -% type unsigned char. Zero if no marker should be set. One for the first -% marker on channel one two for the second and three for both markers. -% For example: - -wf_p.data.marker = zeros(2,1200, 'uint8'); -wf_p.data.marker(1,1:120) = 1; - -%% -% This generates a short marker pulse on the first channel for one tenth of -% the total pulselength of 1200 for the first marker. -% -% It is more convinient to use data.marktab when creating a |'tab'| pulse. -% Analogous to the pulsetab the marktab contains the start times in the -% first rows. The subsequent rows contain the widths of the marker pulses. -% For two channels one two marker per channel this follows the schema: -% -% [time; channel1_marker1_duration; channel1_marker2_duration; -% channel2_marker1_duration; channel2_marker2_duration;] -% -% Here is an example: - -tb_p.data.marktab = [ 2 ; 1 ; 0 ; 0 ; 1 ]; - -%% -% This fires marker1 of channel one and marker2 of channel two for 1us -% starting at 2us. -% -% When using |'elem'| the pulse element 'mark' is used to write to the -% marktab. -% -% An advanced feature is the routing of marker channels. It is for example -% possible to define markers for one channel and have them executed in -% parallel on additional channels. Or exchange the markers of several -% channels. This is realised using the markmap field in a pulse group. -% Consult the section 'groups' below for group specific questions. -% -% Note that you can only route both markers of a channel at the same time. -% This is not a problem when using just one marker per channel. -% -% The usage is defind in as follows: -% -% markmap = [marker_source_channel1 marker_source_channel2, ...; -% marker_output_channel1 marker_output_channel2, ...] -% -% An example would be to route the markers from channel one to all channels -% for four channels in total: - -group.markmap = [1 1 1 1; 1 2 3 4]; - -%% -% or to exchange the markers of the first two channels: - -group.markmap = [1 2 3 4; 2 1 3 4]; - -%% Groups -% Only groups can be uploaded to the AWG. They consist of a number of -% pulses in various forms, or just one pulse in the most simple case. The -% group struct is similar to the struct of the pulse itself and is depicted -% in the following figure. Refer to the comments in -% -% for a quick reference. -% -% <> -% -% The group pg with the name pg.name has a number of fields. The field -% pg.markmap is discussed in the above section about markers. It is used to -% map markers from different channels to different marker outputs. The field -% pg.xval is a merging of the xval fields of the individual pulses. It is -% used by an external data evaluation package -% -% to define the scaling of the x-axis. Look into dataview for more -% information about xval. -% -% The fields pg.varpar and pg.params are used to parametrize certain -% aspects of an |'elem'| or |'tab'| pulse. Refer to the section above discussing it in -% connection with pardef and trafofn of the individual pulses inside the -% group. -% -% -%
    -% -% -% *ctrl, nrep, jump, pulseind* -% -% This fields control how the group behaves. -% -% * explain single fields with example -% -% -%
    -% -% -% *chan, matrix, offset, trafofn* -% -% This fields modify the group in several ways and set its output destination. -% -% * explain single fields with example -% -% -%
    -% -% -% *dict* -% -% This is a dictionary of predifined pulse elements used to create |'elem'| -% pulses. -% -% * ask harvard nicely to write about it and provide example uses -% -% -%
    -% -% -% *pulses* -% -% This field contains information about the pulses used in the group. -% -% The above figure about the pulsegroup struct shows three possible cases -% of content of the pg.pulses field (starting from the bottom): -% -% # The pulses are part of the database in plsdata.pulses and are -% referenced via their index, e.g., pg.pulses = [1, 2, 3] will use -% plsdata.pulses(1:3). -% # An array of pulse structs is provided directly, without the step of -% retrieving the pulses from the database. -% # Instead of single pulses use whole groups. This creates a group of groups -% in a recursive manner. To use this feature pg.pulses.groups has to be an -% array of already existing groups adressed by their group names, e.g., -% pg.pulses.groups = {'pg1', 'pg2'} will use pulse groups with the names 'pg1' and -% 'pg2', which were created with plsdefgrp() earlier to create a new group -% -% Cases 1 and 2 create groups of the type 'pls', since pulses are used. The -% third case creates groups of the type 'grp'. The type of group is -% specified inside the ctrl string pg.ctrl by plsdefgrp() automatically or -% it can be set manually when the group is defined. -% -% Besides pg.pulses.groups for type 'grp' the field pg.pulses.chan and -% pg.pulses.markchan exist to overwrite the output of the inner groups -% indiviually oposed to pg.chan and pg.markmap for all groups at once. The field pg.pulses.chan -% has as many rows as inner groups. The columns specify the output -% channels. The same is true for pg.pulses.markchan. If the output channel -% is set to 0 the specific output is ignored. -% -% For the example of using the two inner groups 'pg1' and 'pg2' as in case -% 3 above: - -pg.pulses.chan = [1 2; 2 0]; - -%% -% The inner group 'pg1' is routet to channel 1 und 2. The first channel of -% 'pg2' is routed to the physical channel 2 and the second channel of the -% group is omitted. - -%% Workflow -% At first a working configuration of pulsecontrol has to be enshured. -% Refer to the sections awgdata, plsdata above and the section "What Setup -% Do I Need?" in the Getting Started guide. -% -% A typical procedure how to use pulsecontrol is depicted in the figure -% below. -% -% <> -% -% # at first the desiered pulses are defined -% # the pulses can be plotted by plsplot() to enshure the correct shape -% visually -% # using plsreg() the pulses are 'registered' in the plsdata database. -% Since the database is backad up on disk, this enshures consistency and -% safty -% # a pulse group is defined using the pulses created in the first step. It -% is convinient to use the index of the pulse in the plsdata database. An -% alternative is to write the pulse struct(s) directly in the .pulses field -% of the group -% # the function plsdefgrp() saves the group to disk inside the directory -% plsdata.grpdir. Only groups saved in this directory can be send to the AWG. -% This enshures that no information is lost. -% # finally awgadd() is used to transmit the group or several groups to the AWG -% # the function plsupdate is used to update some parameters of the group. -% The fields name, chan, markmap, offset, pulseind, pulses cannot be -% updated. The modified group can be retransmitted to the AWG to reflect -% the changes -% -% Refer to the section "Example: Creation Of A Simple Pulse..." in the -% Getting Started guide. -% -% * explain when to use plsinfo() -% * explain how to use plsmakegrp() -% * explain how pulse is documented in plslog and how to retrieve states -% -%% Multiple AWGs -% -% * remains ToDo -% -% Copyright 2009 Not The MathWorks, Inc. diff --git a/doc/taurc.png b/doc/taurc.png deleted file mode 100644 index 03e2f5d..0000000 Binary files a/doc/taurc.png and /dev/null differ diff --git a/doc/workflow.png b/doc/workflow.png deleted file mode 100644 index 1a63d2c..0000000 Binary files a/doc/workflow.png and /dev/null differ diff --git a/makeGroupDef.m b/makeGroupDef.m new file mode 100644 index 0000000..748573b --- /dev/null +++ b/makeGroupDef.m @@ -0,0 +1,418 @@ +function grpdef = makeGroupDef(name, ctrl, ind, opts) +% grpdef = plsmakegrp(name, ctrl, ind, opts) +% Covert pulses in pulsegroup to wf format. +% name: group name. +% ctrl: 'plot', 'plot chrg', 'check', 'upload' +% for maintenance/debugging: 'clrzero', 'local'. +% These may mess with the upload logging, so use with care. +% ind: optional pulse index. The default is pulseind or all pulses. +% opts is an option struct +% opts.time ; time to recreate group at. +% time: optional, make grp as it was at time.. +% (c) 2010 Hendrik Bluhm. Please see LICENSE and COPYRIGHT information in plssetup.m. + +global plsdata; + +global vawg; +if ~isa(vawg,'VAWG') + error('vawg has to be of type VAWG'); +end + +if ~exist('ctrl','var') + ctrl=''; +end +if ~exist('ind','var') + ind=[]; +end +if ~exist('opts','var') + opts=struct(); +end + +opts=def(opts,'time',[]); + +if ~iscell(name) + name = {name}; +end + +for k = 1:length(name) + if(~isstruct(name{k})) + zerolen = []; % avoid using zerolen from previous group. + plslog=[]; + load([plsdata.grpdir, 'pg_', name{k}]); + else + grpdef=name{k}; + end + + if exist('plslog','var') && length(plslog) > 100 + fprintf('Group %s has %d log entries.\n',name{k},length(plslog)); + end + if exist('plslog','var') && ~isempty(plslog) && ~isempty(opts.time) + le=plsinfo_logentry(plslog,opts.time); + grpdef.params=plslog(le).params; + grpdef.matrix=plslog(le).matrix; + grpdef.varpar=plslog(le).varpar; + grpdef.offset=plslog(le).offset; + grpdef.dict=plslog(le).dict; + grpdef.readout=plslog(le).readout; + end + + if strfind(grpdef.ctrl, 'seq') + fprintf('Sequence joined groups: %s\n',sprintf('%s ',grpdef.pulses.groups{:})); + for m = 1:length(grpdef.pulses.groups) + plsmakegrp(grpdef.pulses.groups{m},ctrl,ind,opts); + end + return; + end + + pack = ~isempty(strfind(grpdef.ctrl,'pack')); + + if ~ isfield(grpdef, 'varpar') + grpdef.varpar = []; + end + if ~ isfield(grpdef, 'params') + grpdef.params = []; + end + if isfield(grpdef, 'time')&& ~isempty(grpdef.time) + fprintf('Ignoring opts.time '); + opts.time=grpdef.time; + end + + switch grpdef.ctrl(1:min([end find(grpdef.ctrl == ' ', 1)-1])) + + case 'pls' + + grpdef.pulses = plsdefault(grpdef.pulses); + + npar = max(1, size(grpdef.varpar, 1)); + + plsdef = grpdef.pulses;%(ind); + + if nargin < 3 || isempty(ind) + if isfield(grpdef, 'pulseind') + ind = unique(grpdef.pulseind); + else + ind = 1:length(plsdef)*npar; + end + + end + + grpdef.pulses(length(ind)+1:end) = []; + if isfield(grpdef,'dict') && ~isempty(grpdef.dict) + grpdef.dict=pdpreload(grpdef.dict,opts.time); + end + for m = 1:length(ind) + + i = floor((ind(m)-1)/npar)+1; + j = mod(ind(m)-1, npar)+1; + + % transfer valid pulse dependent parameters. Interpretation of nan may not be so useful here, + % but should not hurt. + params = grpdef.params; + if ~isempty(grpdef.varpar) + mask = ~isnan(grpdef.varpar(j, :)); + params(end-size(grpdef.varpar, 2) + find(mask)) = grpdef.varpar(j, mask); + end + + if ~isempty(plsdef(i).trafofn) + params = plsdef(i).trafofn(params); + end + + % Apply dictionary before varpars; avoids many random bugs. + if isfield(grpdef,'dict') && ~isempty(grpdef.dict) && strcmp(plsdef(i).format,'elem') + plsdef(i)=pdapply(grpdef.dict, plsdef(i),opts.time); + end + mask = ~isnan(params); + % update parameters - could move to plstowf + if ~isempty(plsdef(i).pardef) + switch plsdef(i).format + case 'elem' + pardef = plsdef(i).pardef; + for n = 1:size(pardef, 1) + if isnan(params(n)) + continue; + end + if pardef(n, 2) < 0 + plsdef(i).data(pardef(n, 1)).time(-pardef(n, 2)) = params(n); + else + plsdef(i).data(pardef(n, 1)).val(pardef(n, 2)) = params(n); + end + end + + case 'tab' + pardef = plsdef(i).pardef; + for n = 1:size(pardef, 1) + if isnan(params(n)) + continue; + end + if pardef(n, 1) < 0 + plsdef(i).data.marktab(pardef(n, 2), -pardef(n, 1)) = params(n); + else + plsdef(i).data.pulsetab(pardef(n, 2), pardef(n, 1)) = params(n); + end + end + + otherwise + error('Parametrization of pulse format ''%s'' not implemented yet.', plsdef(i).format); + end + end + + grpdef.pulses(m) = plstowf(plsdef(i)); + + + if ~isempty(grpdef.varpar) + grpdef.pulses(m).xval = [grpdef.varpar(j, end:-1:1), grpdef.pulses(m).xval]; + end + end + + case 'grp' + + groupdef = grpdef.pulses; + grpdef.pulses = struct([]); + + nchan = size(grpdef.matrix, 2); % # input channels to matrix + + % if ~isfield(groupdef, 'chan') + % [groupdef.chan] = deal(length(groupdef.groups), nan(nchan)); + % end + % + % if ~isfield(groupdef, 'markchan') + % groupdef.markchan = groupdef.chan; + % end + + + for j = 1:length(groupdef.groups) + pg = plsmakegrp(groupdef.groups{j},'upload local'); + + if ~isfield(pg, 'pulseind') %|| some flag set to apply pulseind after adding, same for all groups + pg.pulseind = 1:length(pg.pulses); + end + + % probably pointless code: + % if j == 1 % set defaults from pg(1) + % if nargin < 3 + % ind = 1:length(pg.pulses); + % end + % end + % pg.pulses = pg.pulses(ind); + + + + % target channels for j-th group + if isfield(groupdef, 'chan') + chan = groupdef.chan(j, :); + else + chan = pg.chan; + end + mask = chan > 0; + chan(~mask) = []; + + % target channels for markers + if ~isfield(groupdef, 'markchan') + markchan = chan; + else + markchan = groupdef.markchan(j, :); + end + markmask = markchan > 0; + markchan(~markmask) = []; + + %ind not given to recursive call above, so plsmagegrp makes all pulses, specified by pulseind or default + % of source group. Need to reconstruct indices as used for file names by inverting unique + [pind, pind, pind] = unique(pg.pulseind(min(j,end),:)); + + for c = 1:length(pg.pulses(1).data) + for i = 1:length(pg.pulseind) + if j == 1 % first pf determines size + grpdef.pulses(i).data(c).wf = zeros(nchan, size(pg.pulses(pind(i)).data(c).wf, 2)); + grpdef.pulses(i).data(c).marker = zeros(nchan, size(pg.pulses(pind(i)).data(c).wf, 2), 'uint8'); + grpdef.pulses(i).data(c).readout = pg.pulses(pind(i)).data(c).readout; % a bit of a hack. + grpdef.pulses(i).data(c).clk = pg.pulses(pind(i)).data(c).clk; + grpdef.pulses(i).xval = []; + else + for ii=1:size(pg.pulses(pind(i)).data(c).readout,1) + roi=find(grpdef.pulses(pind(i)).data(c).readout(:,1) == pg.pulses(pind(i)).data(c).readout(ii,1)); + if ~isempty(roi) + fprintf('Overwriting readout window\n'); + grpdef.pulses(pind(i)).data(c).readout(roi(1),2:3) = pg.pulses(pind(i)).data(c).readout(ii,2:3); + else + grpdef.pulses(pind(i)).data(c).readout(end+1,:) = pg.pulses(pind(i)).data(c).readout(ii,1:3); + end + end + end + + grpdef.pulses(i).data(c).wf(chan, :) = grpdef.pulses(i).data(c).wf(chan, :) + pg.pulses(pind(i)).data(c).wf(mask, :); + grpdef.pulses(i).data(c).marker(markchan, :) = bitor(grpdef.pulses(i).data(c).marker(markchan, :), ... + pg.pulses(pind(i)).data(c).marker(markmask, :)); + grpdef.pulses(i).xval = [grpdef.pulses(i).xval, pg.pulses(pind(i)).xval]; + end + end + end + + if nargin < 3 || isempty(ind) + ind = 1:length(grpdef.pulses); + else + grpdef.pulses = grpdef.pulses(ind); + end + + [grpdef.pulses.format] = deal('wf'); + + %grpdef = rmfield(grpdef, 'groups', 'matrix', 'offset'); + + otherwise + error('Group control %s not understood.\n',grpdef.ctrl); + end + + if isfield(grpdef, 'xval') && ~isempty(grpdef.xval) + for i=1:length(grpdef.pulses) + grpdef.pulses(i).xval = [grpdef.xval grpdef.pulses(i).xval]; + end + end + + for i = 1:length(ind) + for c=1:length(grpdef.pulses(i).data) + grpdef.pulses(i).data(c).wf = grpdef.matrix * (grpdef.pulses(i).data(c).wf + ... + repmat(grpdef.offset, 1, size(grpdef.pulses(i).data(c).wf, 2))); + if isfield(grpdef, 'trafofn') && ~isempty(grpdef.trafofn) + wf=grpdef.pulses(i).data(c).wf; + for qq=1:length(grpdef.trafofn) + fn=grpdef.trafofn(qq).func; + args=grpdef.trafofn(qq).args; + if ~iscell(args) + args={args}; + end + for q=1:size(wf,1) + wf(q,:) = ... + fn(wf(q,:),q,args{:}); + end + end + grpdef.pulses(i).data(c).wf=wf; + end + if isfield(grpdef, 'markmap') + md = grpdef.pulses(i).data(c).marker; + grpdef.pulses(i).data(c).marker = zeros(size(grpdef.matrix, 1), size(md, 2), 'uint8'); + grpdef.pulses(i).data(c).marker(grpdef.markmap(2, :), :) = md(grpdef.markmap(1, :), :); + end + end + end + + grpdef.ctrl = ['pls', grpdef.ctrl(find(grpdef.ctrl == ' ', 1):end)]; + + + switch ctrl(1:min([end find(ctrl == ' ', 1)-1])) + case 'plot' + if isfield(grpdef,'dict') && ~isempty(grpdef.dict) + plsplot(grpdef.pulses,grpdef.dict,ctrl); + else + plsplot(grpdef.pulses,[],ctrl); + end + + + case 'uploadsimulation' + if ~isempty(strfind(ctrl, 'force')) || plsinfo('stale',grpdef.name) + % modified since last upload (or upload forced) + + % A little naughty; secretly pack all the pulse waveforms together for load... + if pack + if any(~strcmp('wf',{grpdef.pulses.format})) + error('Pack can only deal with waveforms.'); + end + packdef = grpdef; + packdef.pulses=[]; + packdef.pulses(1).format='wf'; + for c=1:length(grpdef.pulses(1).data) + data=vertcat(grpdef.pulses.data); + data=data(:,c); + packdef.pulses(1).data(c).marker = [data.marker]; + packdef.pulses(1).data(c).wf = [data.wf]; + packdef.pulses(1).data(c).clk = data(1).clk; + % awgload/zero doesn't use anything else. + end + else + packdef = grpdef; + end + + if isempty(zerolen) || ~isempty(strfind(ctrl, 'clrzero')) + zerolen = zeros(length(packdef.pulses), length(packdef.chan)); + end + + zerolen = vawg.zero(packdef, ind,[]); + + + if pack + zerolen=repmat(zerolen(1,:)/length(grpdef.pulses),length(grpdef.pulses),1); + end + + % save update time in log. + plslog(end+1).time = now; + plslog(end).params = grpdef.params; + plslog(end).matrix = grpdef.matrix; + plslog(end).varpar = grpdef.varpar; + plslog(end).offset = grpdef.offset; + + if isfield(grpdef.pulses(1).data,'readout') + readout=[]; + for ll=1:length(grpdef.pulses) + if ~isempty(grpdef.pulses(ll).data(1).readout) + readout(:,:,ll) = grpdef.pulses(ll).data(1).readout; + end + end + if any(abs(diff(readout,[],3)) > 1e-10) + warning('Readout changes between pulses in %s\n',name{k}); + end + if(size(readout,1) > 0) + plslog(end).readout = readout(:,:,1); + else + plslog(end).readout=[]; + end + end + + if isfield(grpdef, 'dict') + plslog(end).dict = grpdef.dict; + end + + if isfield(grpdef, 'trafofn') + plslog(end).trafofn = grpdef.trafofn; + end + + if length(plslog) > 2 % copy in case not all pulses updated. First plslog has no xval + plslog(end).xval = plslog(end-1).xval; + end + %plslog(end).xval(:, ind) = vertcat(grpdef.pulses.xval)'; + plslog(end).xval = vertcat(grpdef.pulses.xval)'; % temporary bug fix + plslog(end).ind = ind; + + save([plsdata.grpdir, 'pg_', name{k}], '-append','-v6', 'plslog', 'zerolen'); + logentry('Uploaded group %s, revisions %i.', grpdef.name, length(plslog)); + % fprintf(' in upload of group %s.\n', grpdef.name); + else + fprintf('Skipping group %s.\n', grpdef.name); + end + + end +end + +% Apply a default. +function s=def(s,f,v) + if(~isfield(s,f)) + s=setfield(s,f,v); + end +return; + +% Find an appropriate log entry. +function l=plsinfo_logentry(plslog, time) + +l = length(plslog); + +if ~isempty(time) + while plslog(l).time(1) > time + l = l - 1; + end + + if l == 0 + error('Time travellers beware!'); + end + + if length(plslog(l).time) > 1 && -plslog(l).time(2) < time + error('Group not loaded at requested time!'); + end +end +return \ No newline at end of file diff --git a/plsdefgrp_v1.m b/plsdefgrp_v1.m deleted file mode 100755 index c75b3bc..0000000 --- a/plsdefgrp_v1.m +++ /dev/null @@ -1,104 +0,0 @@ -function plsdefgrp(grpdef) -% pldefsgrp(grpdef) -% ctrl: ctrl string for useful default options. -% Starts with group type, then switches: -% noloop -% notrig -% cat -% -% nrep: rep counts, defaults to 1 -% jump: overrides default jumps -% name: group (file) name -% -% chan: physical output channel list (or channels going into matrix) -% matrix: Compensation/linear combination matrix -% one col per input channel, one row per output channel. -% Default = Identity. -% markmap: optional map for marker channels, first row is input channel, second -% row the corresponding oputput channel. -% offset: added to pulse voltages before applying matrix. Default = 0; -% -% varpar: length(pulses) x m matrix with parameters varying between pulses, -% nan entries are ignored, used for last m parameters. -% params: vector with default and start parameters, length determines number of parameters. -% -% type = 'grp' -% pulses: struct with fields -% groups: Cell array with group names -% chan: Matrix with target channels for each group, 0 = ignore. -% Row index is group index -% Indices refer to columns of matrix. Default chan for source group. -% -% type = 'par' -% pulses: struct array with pulses in std format. -% Only comp or (after database index substitution) implemented for parametrization. -% pardef: n x 2 matrix with pulsedef and data entry indices, -% -ve second indices refer to time, +ve ones val. -% trafofn (optional): transforms given parameters into pulse parameters. (Can be a function or matrix.) -% - -% (c) 2010 Hendrik Bluhm. Please see LICENSE and COPYRIGHT information in plssetup.m. - - -global plsdata; - -file = [plsdata.grpdir, 'pg_', grpdef.name]; - -if exist(file, 'file') || exist([file, '.mat'], 'file') - fprintf('File %s exists. Overwrite? (yes/no)', file); - while 1 - str = input('', 's'); - switch str - case 'yes' - break; - case 'no' - return; - end - end -end - -% set defaults -if ~isfield(grpdef, 'ctrl') - grpdef.ctrl = []; -end -if isempty(grpdef.ctrl) || isempty(strmatch(grpdef.ctrl(1:min([end find(grpdef.ctrl == ' ', 1)-1])), {'pls', 'grp', 'par'})) - % format not given - if ~isstruct(grpdef.pulses) || isfield(grpdef.pulses, 'data') - grpdef.ctrl = ['pls ' grpdef.ctrl]; - elseif isfield(grpdef.pulses, 'groups') - grpdef.ctrl = ['grp ' grpdef.ctrl]; - else - error('Invalid group format.'); - end -end - - - -if ~isfield(grpdef, 'matrix') || isempty(grpdef.matrix) - grpdef.matrix = eye(length(grpdef.chan)); -else - if isfield(grpdef, 'chan') - grpdef.matrix = grpdef.matrix(:, grpdef.chan); - if ~isfield(grpdef, 'markmap') - grpdef.markmap = [1:length(grpdef.chan); grpdef.chan]; - end - end - - if isfield(grpdef, 'outchan') - grpdef.chan = grpdef.outchan; - else - grpdef.chan = 1:size(grpdef.matrix, 1); - end -end - - -if ~isfield(grpdef, 'offset') || isempty(grpdef.offset) - grpdef.offset = zeros(size(grpdef.matrix, 2), 1); -end - - -logdata.time = 0; % not loaded yet -lastupdate = now; - -save(file, 'grpdef', 'logdata', 'lastupdate'); -logentry('Created group %s.', grpdef.name); diff --git a/plsinfo.m b/plsinfo.m index d05d288..7f01702 100755 --- a/plsinfo.m +++ b/plsinfo.m @@ -10,10 +10,11 @@ global plsdata; -global awgdata; +global vawg; if nargin >= 2 && ~ischar(group) - group = awgdata(1).pulsegroups(group).name; + %assume group = [awg group] + group = vawg.awg(group(1)).pulsegroups(group(2)).name; end if ~exist('time','var') time=[]; @@ -95,9 +96,10 @@ end case 'ro' - ind=awggrpind(group); - if ~isnan(ind) && isfield(awgdata(1).pulsegroups(ind),'readout') && ~isempty(awgdata(1).pulsegroups(ind).readout) - val=awgdata(1).pulsegroups(ind).readout; + error('assure ind is awg index'); + grpIndex = vawg.awgs(ind).grpind(group); + if ~isnan(grpIndex) && isfield(vawg.awgs(ind).pulsegroups(grpIndex),'readout') && ~isempty(vawg.awgs(ind).pulsegroups(grpIndex).readout) + val=vawg.pulsegroups(grpIndex).readout; else warning('off', 'MATLAB:load:variableNotFound'); load([plsdata.grpdir, 'pg_', group], 'grpdef', 'zerolen','plslog'); @@ -126,10 +128,9 @@ end end case 'zl' - ind=awggrpind(group); - if ~isnan(ind) && isfield(awgdata(1).pulsegroups(ind),'zerolen') && ~isempty(awgdata(1).pulsegroups(ind).zerolen) - val=awgdata(1).pulsegroups(ind).zerolen; - else +% error('pulseinfo(zl) is deprecated'); + % vawg.awgs.getPulsegroupField(group,'zerolen'); + warning('off', 'MATLAB:load:variableNotFound'); load([plsdata.grpdir, 'pg_', group], 'zerolen'); warning('on', 'MATLAB:load:variableNotFound'); @@ -140,7 +141,23 @@ end % hack as a dirty bug fix. Size of zerolen does not match group format. % would have to read and merge all component groups. val = zerolen; - end + + +% ind=awggrpind(group); +% if ~isnan(ind) && isfield(vawg.pulsegroups(ind),'zerolen') && ~isempty(vawg.pulsegroups(ind).zerolen) +% val=vawg.pulsegroups(ind).zerolen; +% else +% warning('off', 'MATLAB:load:variableNotFound'); +% load([plsdata.grpdir, 'pg_', group], 'zerolen'); +% warning('on', 'MATLAB:load:variableNotFound'); +% +% if ~exist('zerolen', 'var') +% load([plsdata.grpdir, 'pg_', group], 'grpdef'); +% load([plsdata.grpdir, 'pg_', grpdef.pulses.groups{1}], 'zerolen'); +% end % hack as a dirty bug fix. Size of zerolen does not match group format. +% % would have to read and merge all component groups. +% val = zerolen; +% end case 'gd' load([plsdata.grpdir, 'pg_', group], 'grpdef'); val = grpdef; @@ -151,26 +168,34 @@ if(~isempty(time)) val=val(logentry(val,time)); end - case 'stale' - ind=awggrpind(group); - if isempty(awgwaveforms(group)) - val=1; - elseif ~isnan(ind) && isfield(awgdata(1).pulsegroups(ind),'lastupdate') && isfield(awgdata(1).pulsegroups(ind),'lastload') && ... - ~isempty(awgdata(1).pulsegroups(ind).lastupdate) && ~isempty(awgdata(1).pulsegroups(ind).lastload) - val = awgdata(1).pulsegroups(ind).lastload < awgdata(1).pulsegroups(ind).lastupdate; - else - load([plsdata.grpdir, 'pg_', group], 'lastupdate','plslog','grpdef'); - if(isempty(strfind(grpdef.ctrl,'seq'))) - val = lastupdate > plslog(end).time(end); - if val && nargout == 0 - fprintf('Pulse group ''%s'' is stale.\n',group); - end + case 'stale' + val = false; + for awg = vawg.awgs + + % pulsegroup is unknown to awg + % assure that awg.load creates a pulsegroup in + % storedPulsegroups even if the awg is not affected + if ~isKey(awg.storedPulsegroups,group) + val = true; else - val = 0; - for i=1:length(grpdef.pulses.groups) - val = val || plsinfo('stale',grpdef.pulses.groups{i}); + load([plsdata.grpdir, 'pg_', group], 'lastupdate','plslog','grpdef'); + + if(isempty(strfind(grpdef.ctrl,'seq'))) + + val = val || lastupdate > awg.storedPulsegroups(group).lastload; + if val && nargout == 0 + fprintf('Pulse group ''%s'' is stale.\n',group); + end + else + for i=1:length(grpdef.pulses.groups) + val = val || plsinfo('stale',grpdef.pulses.groups{i}); + end end end + + if val + return; + end end case 'sl' @@ -212,6 +237,8 @@ fprintf('%i: %s - % + further entries\n', i, datestr(plslog(i).time(1)), datestr(plslog(i).time(2))); end end + otherwise + warning('plsinfo: unknown command %s',ctrl); end return diff --git a/plsmakegrp.m b/plsmakegrp.m index be6b030..c9e9e4d 100755 --- a/plsmakegrp.m +++ b/plsmakegrp.m @@ -2,7 +2,7 @@ % grpdef = plsmakegrp(name, ctrl, ind, opts) % Covert pulses in pulsegroup to wf format. % name: group name. -% ctrl: 'plot', 'plot chrg', 'check', 'upload' +% ctrl: 'plot', 'check', 'upload' % for maintenance/debugging: 'clrzero', 'local'. % These may mess with the upload logging, so use with care. % ind: optional pulse index. The default is pulseind or all pulses. @@ -12,7 +12,7 @@ % (c) 2010 Hendrik Bluhm. Please see LICENSE and COPYRIGHT information in plssetup.m. global plsdata; -global awgdata; +global vawg; if ~exist('ctrl','var') ctrl=''; @@ -69,8 +69,9 @@ grpdef.params = []; end if isfield(grpdef, 'time')&& ~isempty(grpdef.time) - fprintf('Ignoring opts.time '); - opts.time=grpdef.time; + fprintf('Ignoring opts.time\n'); + opts.time=grpdef.time; + end switch grpdef.ctrl(1:min([end find(grpdef.ctrl == ' ', 1)-1])) @@ -301,8 +302,9 @@ plsplot(grpdef.pulses,[],ctrl); end - case 'check' + case 'check' for i = 1:length(ind) + error('scale implementatoin for vawg is missing.'); over=0; for a=1:length(awgdata) for c=1:length(grpdef.pulses(i).data) @@ -345,11 +347,14 @@ % Actually handle the upload... if isempty(strfind(ctrl, 'local')) - zerolen = awgload(packdef, ind); - zerolen=zerolen{1}; + for awg = vawg.awgs + awg.load(packdef, ind); + zerolen = awg.zeroLength(packdef,ind,zerolen); + end else - zerolen = awgzero(packdef, ind); - zerolen=zerolen{1}; + error('local not supported in multiple awg upload yet') +% zerolen = awgzero(packdef, ind); +% zerolen = zerolen{1}; end if pack @@ -401,7 +406,6 @@ else fprintf('Skipping group %s.\n', grpdef.name); end - end end diff --git a/plsmakegrp_v1.m b/plsmakegrp_v1.m deleted file mode 100755 index b0912d4..0000000 --- a/plsmakegrp_v1.m +++ /dev/null @@ -1,231 +0,0 @@ -function grpdef = plsmakegrp(name, ctrl, ind) -% grpdef = plsmakegrp(name, ctrl, ind) -% Covert pulses in pulsegroup to wf format. -% name: group name. -% ctrl: 'plot', 'check', 'upload' -% for maintenance/debugging: 'clrzero', 'local'. -% These may mess with the upload logging, so use with care. -% ind: optional pulse index - -% (c) 2010 Hendrik Bluhm. Please see LICENSE and COPYRIGHT information in plssetup.m. - - -global plsdata; -global awgdata; - -if nargin < 2 - ctrl = ''; -end - -if ~iscell(name) - name = {name}; -end - -for k = 1:length(name) - - zerolen = []; % avoid using zerolen from previous group. - load([plsdata.grpdir, 'pg_', name{k}]); - - switch grpdef.ctrl(1:min([end find(grpdef.ctrl == ' ', 1)-1])) - - case 'par' - if nargin < 3 - ind = 1:size(grpdef.pulses.varpar, 1); - end - - % get template from database - if ~isstruct(grpdef.pulses.template) - grpdef.pulses.template = plsdata(grpdef.pulses.template); - end - %while strcmp(grpdef.pulses.pulse.format, 'ind') - % grpdef.pulses.pulse = plsdata(grpdef.pulses.pulse.data); - %end - plsdef = grpdef.pulses; - plsdef.template = plsdefault(plsdef.template); - - %grpdef.pulses = struct; - - for i = 1:length(ind) - - % transfer valid pulse dependent parameters. Interpretation of nan may not be so useful here, - % but should not hurt. - mask = ~isnan(plsdef.varpar(i, :)); - params = plsdef.params; - params(end-size(plsdef.varpar, 2)+find(mask)) = plsdef.varpar(i, mask); - - if isfield(plsdef.template, 'trafofn') && ~isempty(plsdef.template.trafofn) - params = plsdef.template.trafofn(params); - end - - % update parameters - switch plsdef.template.format - case 'elem' - pardef = plsdef.template.pardef; - for j = 1:size(pardef, 1) - if plsdef.template.pardef(j, 2) < 0 - plsdef.template.data(pardef(j, 1)).time(-pardef(j, 2)) = params(j); - else - plsdef.template.data(pardef(j, 1)).val(pardef(j, 2)) = params(j); - end - end - - otherwise - error('Parametrization of pulse format ''%s'' not implemented yet.', params.pulses.format); - end - - if i == 1 - grpdef.pulses = plstowf(plsdef.template); - else - grpdef.pulses(i) = plstowf(plsdef.template); - end - grpdef.pulses(i).xval = [plsdef.varpar(i, :), plsdef.template.xval]; - end - - case 'pls' - - if nargin < 3 - ind = 1:length(grpdef.pulses); - end - - grpdef.pulses = plsdefault(grpdef.pulses(ind)); - for i = 1:length(ind) - grpdef.pulses(i) = plstowf(grpdef.pulses(i)); - end - - - case 'grp' - - groupdef = grpdef.pulses; - grpdef.pulses = struct([]); - - nchan = size(grpdef.matrix, 2); % # input channels to matrix - - % if ~isfield(groupdef, 'chan') - % [groupdef.chan] = deal(length(groupdef.groups), nan(nchan)); - % end - % - % if ~isfield(groupdef, 'markchan') - % groupdef.markchan = groupdef.chan; - % end - - for j = 1:length(groupdef.groups) - pg = plsmakegrp(groupdef.groups{j}); - - if j == 1 % set defaults from pg(1) - if nargin < 3 - ind = 1:length(pg.pulses); - end - end - - pg.pulses = pg.pulses(ind); - - % target channels for j-th group - if isfield(groupdef, 'chan') - chan = groupdef.chan(j, :); - else - chan = pg.chan; - end - mask = chan > 0; - chan(~mask) = []; - - % target channels for markers - if ~isfield(groupdef, 'markchan') - markchan = chan; - else - markchan = groupdef.markchan(j, :); - end - markmask = markchan > 0; - markchan(~markmask) = []; - - for i = 1:length(ind) - if j == 1 % first pf determines size - grpdef.pulses(i).data.wf = zeros(nchan, size(pg.pulses(i).data.wf, 2)); - grpdef.pulses(i).data.marker = zeros(nchan, size(pg.pulses(i).data.wf, 2), 'uint8'); - grpdef.pulses(i).xval = []; - end - - grpdef.pulses(i).data.wf(chan, :) = grpdef.pulses(i).data.wf(chan, :) + pg.pulses(i).data.wf(mask, :); - grpdef.pulses(i).data.marker(markchan, :) = bitor(grpdef.pulses(i).data.marker(markchan, :), pg.pulses(i).data.marker(markmask, :)); - grpdef.pulses(i).xval = [grpdef.pulses(i).xval, pg.pulses(i).xval]; - end - end - - [grpdef.pulses.format] = deal('wf'); - - %grpdef = rmfield(grpdef, 'groups', 'matrix', 'offset'); - end - - - for i = 1:length(ind) - grpdef.pulses(i).data.wf = grpdef.matrix * (grpdef.pulses(i).data.wf + ... - repmat(grpdef.offset, 1, size(grpdef.pulses(i).data.wf, 2))); - - if isfield(grpdef, 'markmap') - md = grpdef.pulses(i).data.marker; - grpdef.pulses(i).data.marker = zeros(size(grpdef.matrix, 1), size(md, 2), 'uint8'); - grpdef.pulses(i).data.marker(grpdef.markmap(2, :), :) = md(grpdef.markmap(1, :), :); - end - end - - grpdef.ctrl = ['pls', grpdef.ctrl(find(grpdef.ctrl == ' ', 1):end)]; - - - switch ctrl(1:min([end find(ctrl == ' ', 1)-1])) - case 'plot' - plsplot(grpdef.pulses); - - case 'check' - - for i = 1:length(ind) - if any(abs(grpdef.pulses(i).data.wf) > awgdata.scale) - fprintf('Pulse %i exceeds range.\n', i); - end - end - - case 'upload' - if logdata(end).time < lastupdate || ~isempty(strfind(ctrl, 'force')) - % modified since last upload (or upload forced) - - if isempty(zerolen) || ~isempty(strfind(ctrl, 'clrzero')) - zerolen = zeros(length(grpdef.pulses), length(grpdef.chan)); - end - - if isempty(strfind(ctrl, 'local')) - zerolen = awgload(grpdef, ind, zerolen); - else - zerolen = awgzero(grpdef, ind, zerolen); - end - - switch grpdef.ctrl(1:min([end find(grpdef.ctrl == ' ', 1)-1])); - case 'par' - lp = plsdef.params; - - case 'pls' - lp = []; - - case 'grp' - if isfield(groupdef, 'matrix'); - lp.matrix = groupdef.matrix; - end - if isfield(groupdef, 'offset'); - lp.offset = groupdef.offset; - end - end - % save update time in log. - logdata(end+1).time = now; - logdata(end).params = lp; - if length(logdata) > 2 % copy in case not all pulses updated. First logdata has no xval - logdata(end).xval = logdata(end-1).xval; - end - logdata(end).xval(:, ind) = vertcat(grpdef.pulses.xval)'; - logdata(end).ind = ind; - - save([plsdata.grpdir, 'pg_', name{k}], '-append', 'logdata', 'zerolen'); - logentry('Uploaded group %s.', grpdef.name); - fprintf('Uploaded group %s.\n', grpdef.name); - else - fprintf('Skipping group %s.\n', grpdef.name); - end - - end -end diff --git a/plsplot.m b/plsplot.m index d9e56d1..0a4db0a 100755 --- a/plsplot.m +++ b/plsplot.m @@ -13,7 +13,6 @@ function plsplot(pulse, dict, ctrl) styles={'b-','r-','g-'}; for i = 1:length(pulse) figure(30); - if isempty(strfind(ctrl,'hold')) clf; else @@ -22,15 +21,6 @@ function plsplot(pulse, dict, ctrl) subplot(222); hold on; subplot(223); hold on; end - - % Added by Pascal on 13.06.2014 to plot in a current charge diagram - if ~isempty(strfind(ctrl, 'chrg')) - if ishandle(1) - ax = findall(1,'type','axes'); - sp2 = subplot(222); hold on; - copyobj(allchild(ax(end)), sp2); - end; - end; pls = plstowf(pulse(i),dict); @@ -38,20 +28,11 @@ function plsplot(pulse, dict, ctrl) for c=1:length(pls.data) subplot(221) x=linspace(0,(size(pls.data(c).wf,2)-1)*1e9/pls.data(c).clk,size(pls.data(c).wf,2)); - plot(repmat(x,size(pls.data(c).wf,1),1)',pls.data(c).wf');%, time(1:end-1), round((data + vc)*2^13)./2^13); hold on; subplot(222) hold on; - % Added by Pascal on 13.06.2014 to plot in a current charge diagram - % This changes the units of subplot(222) so that charge diagram - % fits. Therefore everything multiplied by 1e-3. - if ~isempty(strfind(ctrl, 'chrg')) - pls.data(c).wf = pls.data(c).wf.*1e-3; - title('Multiplied by 1e-3'); - end; - for j = 1:2:size(pls.data(c).wf, 1)-1 plot(pls.data(c).wf(j, :), pls.data(c).wf(j+1, :),styles{(j+1)/2});%, data(1, :) + vc(1, :), data(2, :) + vc(2, :)); end diff --git a/plstotab.m b/plstotab.m index 99ad439..37b3a3d 100755 --- a/plstotab.m +++ b/plstotab.m @@ -232,32 +232,7 @@ % 'rf' has marktab HIGH for M1 and M2 during rf pulse marktab(:, end+1) = [pulsetab(1, end-1)-pulsedef(i).time(2); 0; pulsedef(i).time(1:3)*[1; 1; 1]; 0; 0]; - case 'rf_chirp' ;% linear sweep of freq over edsr burst - global plsdata; % needed for tbase to scale freq - - % val = [freq amplitude phase fm_amp(p-p) tau] - % pulsedef(i).val(1) = pulsedef(i).val(1)./ (1e9/plsdata.tbase); - - freq=pulsedef(i).val(1); %in MHz - amp=pulsedef(i).val(2); % in mV - phase=pulsedef(i).val(3); % in rads - fm_amp=pulsedef(i).val(4); % depth of FM modulation (full min-max range) - tau = pulsedef(i).time(1); % length of edsr burst - - I = @(t) 2*amp*cos(2*pi*((freq-fm_amp/2)*t+((fm_amp/tau)*t.^2)+phase)); % AWG expects Vpp on default, Vpp=2*Vp, I and Q scaled - zero = @(t) 0; - if pulsedef(i).time(1) > 1e-11 - pulsetab(1, end+1) = pulsetab(1, end) + pulsedef(i).time(1); - pulsetab(2:3,end) = [I(pulsedef(i).time(1));0]; - pulsefn(end+1).fn = {I,zero}; - pulsefn(end).t = [pulsetab(1,end+(-1:0))]; - else - pulsetab = [pulsetab, [0; I(0); 0]]; - end - % 'rf_chirp' has marktab HIGH for M1 and M2 during rf pulse - marktab(:, end+1) = [pulsetab(1, end-1)-pulsedef(i).time(2); 0; pulsedef(i).time(1:3)*[1; 1; 1]; 0; 0]; - case 'rfmarkoff' global plsdata; % needed for tbase to scale freq diff --git a/plstowf.m b/plstowf.m index 73b4e97..b04b409 100755 --- a/plstowf.m +++ b/plstowf.m @@ -30,7 +30,8 @@ dt=1e-11; global plsdata; -global awgdata; +global vawg; + pulse = plsdefault(pulse); if strcmp(pulse.format, 'wf') @@ -72,7 +73,7 @@ if ~isfield(pulseinf, 'readout') pulseinf.readout = []; end -clk = unique([awgdata.clk]); +clk = unique([vawg.awgs.clk]); for c=1:length(clk) pulsetab = pulseinf.pulsetab; nchan = size(pulsetab, 1)-1; @@ -92,22 +93,7 @@ end for j = 1:nchan - for i = 2:size(pulsetab, 2) - mask = time >= pulsetab(1, i-1)-dt & time <= pulsetab(1, i)+dt; - % added small shifts to mitigate rounding errors 08/04/09. Never seen to matter. - % below makes writes the pulse into data using lines to connect the - % corners defined in pulstab - if 0 - data(j, mask) = (-pulsetab(j+1, i-1) * (time(mask) - pulsetab(1, i)) ... - + pulsetab(j+1, i) * (time(mask) - pulsetab(1, i-1)))./... - (pulsetab(1, i) - pulsetab(1, i-1)); - else - data(j, mask) = ((-pulsetab(j+1, i-1) + pulsetab(j+1,i)) * time(mask) + ... - pulsetab(j+1,i-1) * pulsetab(1, i) - pulsetab(j+1,i) * pulsetab(1, i-1))./... - (pulsetab(1, i) - pulsetab(1, i-1)); - end - - end + data(j,:) = interp1(pulsetab(1,:),pulsetab(2,:),time); end % lets pulses be defined with functions (eg. sin, cos) instead of % just lines diff --git a/plsupdate.m b/plsupdate.m index 80b14d3..e0c6cbe 100755 --- a/plsupdate.m +++ b/plsupdate.m @@ -13,7 +13,7 @@ function plsupdate(newdef) % Not implmented: Missing or nan entries of params are taken from previous values. global plsdata; -global awgdata; +global vawg; if length(newdef) > 1 if iscell(newdef) @@ -147,13 +147,11 @@ function plsupdate(newdef) lastupdate = now; save(file, '-append', 'grpdef', 'lastupdate'); logentry('Updated group %s.', grpdef.name); - ind = awggrpind(grpdef.name); - if ~isnan(ind) - for i=1:length(awgdata) - awgdata(i).pulsegroups(ind).lastupdate=now; - end + for awg = vawg.awgs + awg.markforupdate(grpdef.name); end + else fprintf('Didn''t update group "%s": nothing changed\n',grpdef.name); end diff --git a/smcPXDAC.m b/smcPXDAC.m new file mode 100644 index 0000000..50f9554 --- /dev/null +++ b/smcPXDAC.m @@ -0,0 +1,51 @@ +function val = smcPXDAC(ico, val, rate) +% 1: none +% 2: clock, +% 3-6: peak to peak range for ch 1-4 +% 7: none +% 8-11 +% +% Extra fields that can go into smdata.inst(x).data +% chain ; setting the frequency or pulseline on this instrument 'chains' to the specified instrument; +% this allows one to seamlessly set the pulseline on many awg's together. +% clockmult; a multiplier to be applied to any clock frequency sets on this device. +% allows 7k and 5k to be mixed. + +global smdata; + +pxdac = smdata.inst(ico(1)).data; + +cmds = {':FREQ', ':FREQ', 'SOUR1:VOLT', 'SOUR2:VOLT', 'SOUR3:VOLT', 'SOUR4:VOLT', 'SEQ:JUMP', ... + 'SOUR1:VOLT:OFFS', 'SOUR2:VOLT:OFFS','SOUR3:VOLT:OFFS','SOUR4:VOLT:OFFS','SOUR1:MARK1:VOLT:LOW',... + 'SOUR1:MARK1:VOLT:HIGH', 'SOUR1:MARK2:VOLT:LOW', 'SOUR1:MARK2:VOLT:HIGH', ... + 'SOUR2:MARK1:VOLT:LOW', 'SOUR2:MARK1:VOLT:HIGH', 'SOUR2:MARK2:VOLT:LOW', 'SOUR2:MARK2:VOLT:HIGH'... + 'SOUR3:MARK1:VOLT:LOW', 'SOUR3:MARK1:VOLT:HIGH', 'SOUR3:MARK2:VOLT:LOW', 'SOUR3:MARK2:VOLT:HIGH'... + 'SOUR4:MARK1:VOLT:LOW', 'SOUR4:MARK1:VOLT:HIGH', 'SOUR4:MARK2:VOLT:LOW', 'SOUR4:MARK2:VOLT:HIGH'}; + +switch ico(2) + case 1; + error('PXDAC provides no frequency generator mode.'); + case 2; + switch ico(3) + case 1 + pxdac.setOutputVoltage(ico(2)-2,val); + case 0 + val = pxdac.getOutputVoltage(ico(2)-2); + otherwise + error('Only supports get and set operations.'); + end + case 3:6; + switch ico(3) + case 1 + pxdac.setOutputVoltage(ico(2)-2,val); + case 0 + val = pxdac.getOutputVoltage(ico(2)-2); + otherwise + error('Only supports get and set operations.'); + end + + case 8:11; + error('PXDAC supports no hardware DC offset.'); + otherwise + error('Operation %d not supported',ico(2)); +end