Hi Andrea,
thanks for your help.
I have extended the approach you suggested to create 3 sinewaves at 120 degree using 2 SMUs. Please see also below script.
Below (in Italic) some answers/comments to your question and suggestions (in Bold):
To accomplish the phase shift, were you thinking to use a different timer for each SMU?
Yes, because I was also trying to adjust the delay that the script's calculations seem to introduce while generating the waveforms.
However, I was not able to do it using several timers as I did not find easy to add a delay for example in the first waveform so that the first and second waveforms remain at 120 degrees out of phase despite the delay that is introduced by the script.
This delay seems to increase as the script calculations become more complex and affects the phase shift.
For example:
- when using a list of source levels for each sine waveform, I had to adjust the sequence of source levels for each sine waveform to compensate for the delay,
- when using the computed source levels I had to define the phase shift for the second sine waveform at -60 degrees and at 250 degrees for the third sine waveform to actually have the 3 sine waveforms with a phase shift of 120 degrees between each other.
However, since in both cases this is not very convenient if I need to change the sine waveform frequency often from 0 to 100 Hz, could you please advise if there is a way to make the timing for the waveform generation independent from the length and complexity of the script?
I have now moved to your approach using one timer for all 3 waveforms and the computed source levels for each sine waveform.
Instead, I would use a single timer to provide the smuX.trigger.source.stimulus to each SMU channel.
Yes, as mentioned above I am going to use this approach, thank you.
Rather than providing a list of source levels, I recommend computing them. in the TSP or Lua code.
You can account for the phase shift in the computed source levels.
I have used a list of source levels because I found it useful when simulating temporary instantaneous voltage failure in one of the 3 phases.
I have also noticed that when using the computed source levels each voltage level is hold a bit longer and this generates a sinewave in which steps are more prevalent than the ones seen when using a list of source levels.
Since I do like the approach of using the computed source levels, could you please advise on what could the reasons for the wider steps and if there is an easy fix?
Thank you
reset()
errorqueue.clear()
-- Reset the TSP-Link if it is offline
if tsplink.state == "offline" then
local nodesFound = tsplink.reset()
if nodesFound ~= 2 then
print(string.format("Error: Found %d Nodes. Expecting 2.", nodesFound))
exit()
end
end
-- Configure the TSPlink Trigger Model
--============================
function ConfigTSPLinkTriggers(nodenum)
node[nodenum].tsplink.trigger[1].clear()
node[nodenum].tsplink.trigger[1].mode = tsplink.TRIG_FALLING
node[nodenum].tsplink.trigger[2].clear()
node[nodenum].tsplink.trigger[2].mode = tsplink.TRIG_FALLING
--node[nodenum].tsplink.trigger[3].clear()
--node[nodenum].tsplink.trigger[3].mode = tsplink.TRIG_FALLING
end
local _startV -- Used by script to track the starting voltage
local _srcRate = 4000 -- Source Update Rate used by the script Sets the source update rate (pts/sec) used by the script.
--Do not set higher than 8,000 if your waveform will have polarity changes 0V crossings)
function SetupAWG(startV, rangeV, limitI, wfrmTbl, remoteSense, trigLineIn)
-- Do some parameter checks
--=========================
if startV == nil then startV = 0 end
if remoteSense ~= true then remoteSense = false end
if type(trigLineIn) == "number" then
trigLineIn = math.floor(trigLineIn)
if trigLineIn < 0 or trigLineIn > 14 then
return true, "Error: Selected trigger line is not valid. trigLineIn must be a number between 0 and 14 or nil."
end
elseif trigLineIn ~= nil then
return true,"Error: Invalid parameter trigLineIn. trigLineIn must be a number between 0 and 14 or nil."
end
_startV = startV
--wfrmTbl = {0.078459,0.156434,0.233445,0.309017,0.382683,0.45399,0.522499,0.587785,0.649448,0.707107,0.760406,0.809017,0.85264,0.891007,0.92388,0.951057,0.97237,0.987688,0.996917,1,0.996917,0.987688,0.97237,0.951057,0.92388,0.891007,0.85264,0.809017,0.760406,0.707107,0.649448,0.587785,0.522499,0.45399,0.382683,0.309017,0.233445,0.156434,0.078459,0,-0.078459,-0.156434,-0.233445,-0.309017,-0.382683,-0.45399,-0.522499,-0.587785,-0.649448,-0.707107,-0.760406,-0.809017,-0.85264,-0.891007,-0.92388,-0.951057,-0.97237,-0.987688,-0.996917,-1,-0.996917,-0.987688,-0.97237,-0.951057,-0.92388,-0.891007,-0.85264,-0.809017,-0.760406,-0.707107,-0.649448,-0.587785,-0.522499,-0.45399,-0.382683,-0.309017,-0.233445,-0.156434,-0.078459,0}
-- compute the sine wave
pts_per_cycle = 40
wfrmTbl = {}
for i=1, pts_per_cycle do
wfrmTbl[i] = 27 * math.sin(i * 2 * math.pi/pts_per_cycle)
end
-- use some phase shift for smub sine wave
wfrmTbl_smub = {}
for i=1, pts_per_cycle do
wfrmTbl_smub[i] = 27 * math.sin(i * 2 * math.pi/pts_per_cycle + math.rad(-60))
end
-- phase shift for node[2].smua sine wave
wfrmTbl_node = {}
for i=1, pts_per_cycle do
wfrmTbl_node[i] = 27 * math.sin(i * 2 * math.pi/pts_per_cycle + math.rad(250))
end
-- Setup the SMU for arb waveform output
--======================================
--reset()
--smua.reset()
--smub.reset()
-- Setup the SMU for arb waveform output
--======================================
reset()
node[1].smua.reset()
node[1].smub.reset()
node[2].smua.reset()
-- digital output trigger for external equipment
-- configure digital IO line 1 to output a active LO/falling edge
-- pulse at start of each current pulse
digio.trigger[1].clear()
digio.trigger[1].mode = digio.TRIG_FALLING
digio.trigger[1].pulsewidth = 10e-6
digio.trigger[1].stimulus = trigger.timer[1].EVENT_ID
node[1].smua.source.func = smua.OUTPUT_DCVOLTS
node[1].smub.source.func = smub.OUTPUT_DCVOLTS
node[2].smua.source.func = smua.OUTPUT_DCVOLTS
if remoteSense == true then
node[1].smua.sense = smua.SENSE_REMOTE
node[1].smub.sense = smub.SENSE_REMOTE
node[2].smua.sense = smua.SENSE_REMOTE
else
node[1].smua.sense = smua.SENSE_LOCAL
node[1].smub.sense = smub.SENSE_LOCAL
node[2].smua.sense = smua.SENSE_LOCAL
end
node[1].smua.source.autorangev = smua.AUTORANGE_OFF
node[1].smua.source.autorangei = smua.AUTORANGE_OFF
node[1].smua.source.rangev = rangeV
node[1].smua.source.levelv = startV
node[1].smua.source.limiti = limitI
node[1].smua.source.delay = 0
node[1].smua.source.settling = smua.SETTLE_FAST_POLARITY
node[1].smub.source.autorangev = smub.AUTORANGE_OFF
node[1].smub.source.autorangei = smub.AUTORANGE_OFF
node[1].smub.source.rangev = rangeV
node[1].smub.source.levelv = startV
node[1].smub.source.limiti = limitI
node[1].smub.source.delay = 0
node[1].smub.source.settling = smub.SETTLE_FAST_POLARITY
node[2].smua.source.autorangev = smua.AUTORANGE_OFF
node[2].smua.source.autorangei = smua.AUTORANGE_OFF
node[2].smua.source.rangev = rangeV
node[2].smua.source.levelv = startV
node[2].smua.source.limiti = limitI
node[2].smua.source.delay = 0
node[2].smua.source.settling = smua.SETTLE_FAST_POLARITY
-- for each tsplink node -- The TSP-Link triggers are used to synchronize the two instruments
ConfigTSPLinkTriggers(1)
ConfigTSPLinkTriggers(2)
--smua.source.func = smua.OUTPUT_DCVOLTS
--smub.source.func = smub.OUTPUT_DCVOLTS
--if remoteSense == true then
--smua.sense = smua.SENSE_REMOTE
--smub.sense = smub.SENSE_REMOTE
--else
--smua.sense = smua.SENSE_LOCAL
--smub.sense = smub.SENSE_LOCAL
--end
--smua.source.autorangev = smua.AUTORANGE_OFF
--smua.source.autorangei = smua.AUTORANGE_OFF
--smua.source.rangev = rangeV
--smua.source.levelv = startV
--smua.source.limiti = limitI
--smua.source.delay = 0
--smua.source.settling = smua.SETTLE_FAST_POLARITY
--smub.source.autorangev = smub.AUTORANGE_OFF
--smub.source.autorangei = smub.AUTORANGE_OFF
--smub.source.rangev = rangeV
--smub.source.levelv = startV
--smub.source.limiti = limitI
--smub.source.delay = 0
--smub.source.settling = smub.SETTLE_FAST_POLARITY
-- Configure the Trigger Model
--============================
-- Timer 1 controls the time per points of the table
trigger.timer[1].delay = 1 / _srcRate
trigger.timer[1].count = table.getn(wfrmTbl) > 1 and table.getn(wfrmTbl) - 1 or 1
if trigLineIn == nil then
-- Immediate
node[1].trigger.timer[1].stimulus = node[1].smua.trigger.ARMED_EVENT_ID
elseif trigLineIn == 0 then
-- Front panel TRIG button
display.trigger.clear()
trigger.timer[1].stimulus = display.trigger.EVENT_ID
else
-- Digio Trigger
digio.trigger[trigLineIn].clear()
digio.trigger[trigLineIn].mode = digio.TRIG_EITHER
trigger.timer[1].stimulus = digio.trigger[trigLineIn].EVENT_ID
end
trigger.timer[1].passthrough = true
-- Configure SMU Trigger Model for Phase A waveform output
node[1].smua.trigger.source.listv(wfrmTbl)
node[1].smua.trigger.source.limiti = limitI
node[1].smua.trigger.measure.action = node[1].smua.DISABLE
node[1].smua.trigger.endpulse.action = node[1].smua.SOURCE_HOLD
node[1].smua.trigger.endsweep.action = node[1].smua.SOURCE_HOLD
node[1].smua.trigger.count = table.getn(wfrmTbl)
node[1].smua.trigger.arm.count = 1
node[1].smua.trigger.arm.stimulus = 0
node[1].smua.trigger.source.stimulus = trigger.timer[1].EVENT_ID
--node[nodenum].trigger.timer[1].stimulus = node[nodenum].tsplink.trigger[1].EVENT_ID
node[1].smua.trigger.measure.stimulus = 0
node[1].smua.trigger.endpulse.stimulus = 0
node[1].smua.trigger.source.action = node[1].smua.ENABLE
-- Configure SMU Trigger Model for Phase B waveform output
node[1].smub.trigger.source.listv(wfrmTbl_smub)
node[1].smub.trigger.source.limiti = limitI
node[1].smub.trigger.measure.action = node[1].smub.DISABLE
node[1].smub.trigger.endpulse.action = node[1].smub.SOURCE_HOLD
node[1].smub.trigger.endsweep.action = node[1].smub.SOURCE_HOLD
node[1].smub.trigger.count = table.getn(wfrmTbl_smub)
node[1].smub.trigger.arm.count = 1
node[1].smub.trigger.arm.stimulus = 0
node[1].smub.trigger.source.stimulus = trigger.timer[1].EVENT_ID
node[1].smub.trigger.measure.stimulus = 0
node[1].smub.trigger.endpulse.stimulus = 0
node[1].smub.trigger.source.action = node[1].smub.ENABLE
-- Configure SMU Trigger Model for Phase C waveform output
node[1].tsplink.trigger[2].stimulus = node[1].trigger.timer[1].EVENT_ID
--node[2].tsplink.trigger[2].stimulus = node[1].trigger.timer[1].EVENT_ID
node[2].smua.trigger.source.listv(wfrmTbl_node)
node[2].smua.trigger.source.limiti = limitI
node[2].smua.trigger.measure.action = node[2].smua.DISABLE
node[2].smua.trigger.endpulse.action = node[2].smua.SOURCE_HOLD
node[2].smua.trigger.endsweep.action = node[2].smua.SOURCE_HOLD
node[2].smua.trigger.count = table.getn(wfrmTbl_node)
node[2].smua.trigger.arm.count = 1
node[2].smua.trigger.arm.stimulus = 0
--node[2].smua.trigger.source.stimulus = trigger.timer[3].EVENT_ID
--node[2].smua.trigger.source.stimulus = tsplink.trigger[2]
--node[2].smua.trigger.source.stimulus = node[1].trigger.timer[3].EVENT_ID
node[2].smua.trigger.source.stimulus = node[2].tsplink.trigger[2].EVENT_ID
node[2].smua.trigger.measure.stimulus = 0
node[2].smua.trigger.endpulse.stimulus = 0
node[2].smua.trigger.source.action = node[2].smua.ENABLE
-- Configure SMU Trigger Model for arb waveform output
--smua.trigger.source.listv(wfrmTbl)
--smua.trigger.source.limiti = limitI
--smua.trigger.measure.action = smua.DISABLE
--smua.trigger.endpulse.action = smua.SOURCE_HOLD
--smua.trigger.endsweep.action = smua.SOURCE_HOLD
--smua.trigger.count = table.getn(wfrmTbl)
--smua.trigger.arm.count = 1
--smua.trigger.arm.stimulus = 0
--smua.trigger.source.stimulus = trigger.timer[1].EVENT_ID
--smua.trigger.measure.stimulus = 0
--smua.trigger.endpulse.stimulus = 0
--smua.trigger.source.action = smua.ENABLE
--smub.trigger.source.listv(wfrmTbl_smub)
--smub.trigger.source.limiti = limitI
--smub.trigger.measure.action = smub.DISABLE
--smub.trigger.endpulse.action = smub.SOURCE_HOLD
--smub.trigger.endsweep.action = smub.SOURCE_HOLD
--smub.trigger.count = table.getn(wfrmTbl)
--smub.trigger.arm.count = 1
--smub.trigger.arm.stimulus = 0
--smub.trigger.source.stimulus = trigger.timer[1].EVENT_ID -- ALC
--smub.trigger.measure.stimulus = 0
--smub.trigger.endpulse.stimulus = 0
--smub.trigger.source.action = smub.ENABLE
--==============================
-- End Trigger Model Configuration
if errorqueue.count > 0 then
return true,"Error occured during setup. Please check that your parameters are valid."
else
return false,"No error."
end
end
--function RunAWG(numCycles)
--if numCycles == nil or numCycles < 0 then
--numCycles = 1
--end
-- Set the number of cycles to output
--smua.trigger.arm.count = numCycles
--smub.trigger.arm.count = numCycles
-- Turn output on
--smua.source.output = smua.OUTPUT_ON
--smub.source.output = smub.OUTPUT_ON
-- Start the trigger model execution
--smub.trigger.initiate() -- start this first
--smua.trigger.initiate()
--if errorqueue.count > 0 then
--return true,"Error occurred. See error queue for details."
--else
--return false,"No error."
--end
--end
--[[ Name: StopAWG()
Usage: err,msg = StopAWG()
Description:
This function stops the waveform output and turns the SMU
output off.
--]]
--function StopAWG()
--smua.abort()
--smua.source.output = 0
--smua.source.levelv = _startV
--smub.abort()
--smub.source.output = 0
--smub.source.levelv = _startV
--if errorqueue.count > 0 then
--return true,"Error occured. See error queue for details."
--else
--return false,"No error."
--end
--end
function RunAWG(numCycles)
if numCycles == nil or numCycles < 0 then
numCycles = 1
end
-- Set the number of cycles to output
node[1].smua.trigger.arm.count = numCycles
node[1].smub.trigger.arm.count = numCycles
node[2].smua.trigger.arm.count = numCycles
-- Turn output on
node[1].smua.source.output = node[1].smua.OUTPUT_ON
node[1].smub.source.output = node[1].smub.OUTPUT_ON
node[2].smua.source.output = node[2].smua.OUTPUT_ON
-- Start the trigger model execution
node[1].smua.trigger.initiate()
node[1].smub.trigger.initiate()
node[2].smua.trigger.initiate()
if errorqueue.count > 0 then
return true,"Error occurred. See error queue for details."
else
return false,"No error."
end
end
--[[ Name: StopAWG()
Usage: err,msg = StopAWG()
Description:
This function stops the waveform output and turns the SMU
output off.
--]]
function StopAWG()
node[1].smua.abort()
node[1].smua.source.output = 0
node[1].smua.source.levelv = _startV
node[1].smub.abort()
node[1].smub.source.output = 0
node[1].smub.source.levelv = _startV
node[2].smua.abort()
node[2].smua.source.output = 0
node[2].smua.source.levelv = _startV
if errorqueue.count > 0 then
return true,"Error occured. See error queue for details."
else
return false,"No error."
end
end
----------------------