Hello,
For my experiment I need to run multiple frequencies scans but at non overlapping times. However, while programming the setting for a RAM scan it effects the other channel of a DDS in ways that don't quite make sense to me.
What i need the experiment to do is turn on on DDS channel to a center frequency f1 and pulse on while a second DDS channel scans through resonance from say (f2 - df2/2 -> f2 +df2/2). During this scan, a ttl pulse will arrive to tell the scan to stop at frequency f_ttl and it will freeze the RAM scan at this entry in RAM to allow us to keep it at this frequency for the second scan. During the second scan, we need the initially stationary DDS channel to now do it's own scan through (f1-df1/2 -> f1+df1/2) while the scanned DDS from the first pulse holds its value of f_ttl. I am able to get this to work in the scenario where we just have a single DDS channel scan, pause on trigger, and then constant probe at this frequency for a second pulse, but when adding in the second scan I can not seem to get the system to behave. For my test code I have just replaced the RAM freeze with a fixed index=900 for testing.
Any help or tips with how to do these DDS scans easier and move seamlessly between the two would be much appreciated. See below for sample code.
`from artiq.experiment import *
from scan_framework import Scan1D, TimeScan
import numpy as np
from CoolingClass import _Cooling
from CameraClass import _Camera
from BraggClass import _Bragg
from StateControlClass import _state_control
from artiq.coredevice import ad9910
from artiq.coredevice.ad9910 import PHASE_MODE_TRACKING
class TTL_VRS(EnvExperiment):
def build(self, **kwargs):
super().build(**kwargs)
# hardware and class objects
self.setattr_device("ttl5")
self.setattr_device("ttl0")
self.Bragg = _Bragg(self) # urukukl card for controlling bragg beams
# Arguments
self.setattr_argument("freq_center_trigg",
NumberValue(
3*1e6,
min=0.1*1e6,
max=200.0*1e6,
scale=1e6,
unit="MHz",
ndecimals = 3),
"scans")
self.setattr_argument("freq_width_trigg",
NumberValue(
1*1e6,
min=-50.0*1e6,
max=50.0*1e6,
scale=1e6,
unit="MHz"),
"scans")
self.setattr_argument("scan_time_trigg",
NumberValue(
1000*1e-6,
min=1*1e-6,
max=50000*1e-6,
scale=1e-6,
unit='us'),
"scans")
self.setattr_argument("freq_center_meas",
NumberValue(
80*1e6,
min=0.1*1e6,
max=200.0*1e6,
scale=1e6,
unit="MHz",
ndecimals = 3),
"scans")
self.setattr_argument("freq_width_meas",
NumberValue(
1*1e6,
min=-50.0*1e6,
max=50.0*1e6,
scale=1e6,
unit="MHz"),
"scans")
self.setattr_argument("scan_time_meas",
NumberValue(
1000*1e-6,
min=1*1e-6,
max=50000*1e-6,
scale=1e-6,
unit='us'),
"scans")
# Prep DDS scan
self.freq_list= np.linspace(0.0*MHz, 0.0*MHz, 1024)
self.freq_list_ram = np.full(1024, 1)
self.step_size = 0
self.scan_dds_trigg = self.Bragg.urukul_channels[1]
self.scan_dds_meas = self.Bragg.urukul_channels[2]
@kernel
def run(self, point):
self.core.reset()
self.ttl0.input() # for reading input trigger
self.ttl5.off() # for timing scans on scope
self.Bragg.init_aoms() # initialized cpld and initial frequency for DDS chip
delay(1*ms)
self.Bragg.aom_bragg1.set(frequency=self.freq_center_trigg-self.freq_width_trigg/2, amplitude=0.8)
self.Bragg.aom_bragg2.set(frequency=self.freq_center_meas, amplitude=0.8)
delay(1*ms)
self.load_scan(self.scan_dds_trigg, self.freq_center_trigg, self.freq_width_trigg, self.scan_time_trigg)
self.load_scan(self.scan_dds_meas, self.freq_center_meas, self.freq_width_meas, self.scan_time_meas)
delay(1*ms)
self.scan_dds_trigg.set_cfr1(internal_profile=0, ram_enable=1, ram_destination=ad9910.RAM_DEST_FTW)
delay(1*ms)
self.scan_dds_trigg.set_att(self.Bragg.atten_Bragg1)
self.scan_dds_meas.set_att(self.Bragg.atten_Bragg2)
self.Bragg.aom_bragg2.set(frequency=self.freq_center_meas, amplitude=0.8) # scan load messes up, so need to reset
delay(1*ms)
### PULSE 1 ######################
with parallel:
self.ttl5.on()
self.scan_dds_trigg.sw.on()
self.scan_dds_meas.sw.on()
self.scan_dds_trigg.cpld.io_update.pulse_mu(8) # scans first DDS channel, keeps second at fixed frequency
delay(self.scan_time_trigg)
with parallel:
self.ttl5.off()
self.scan_dds_trigg.sw.off()
self.scan_dds_meas.sw.off()
###################################
### PREP FOR PULSE 2 ###############
with parallel:
delay(100*us)
with sequential:
self.freeze_RAM(900) # idx = 900 just for testing. will be replaced with something calculated from reading in pulse times on TTL0
self.scan_dds_meas.set_cfr1(internal_profile=0, ram_enable=1, ram_destination=ad9910.RAM_DEST_FTW)
###################################
#### PULSE 2 ####################
with parallel:
self.ttl5.on()
self.scan_dds_trigg.sw.on()
self.scan_dds_meas.sw.on()
self.scan_dds_meas.cpld.io_update.pulse_mu(8)
delay(self.scan_time_meas)
with parallel:
self.scan_dds_trigg.sw.off()
self.scan_dds_meas.sw.off()
self.ttl5.off()
###################################
@kernel
def freeze_RAM(self, idx):
self.scan_dds_trigg.set_profile_ram(start=idx,end=idx,
step=(self.step_size | (2**6 - 1) << 16),profile=0,mode=ad9910.RAM_MODE_RAMPUP)
self.scan_dds_trigg.cpld.io_update.pulse_mu(8)
@kernel
def load_scan(self, dds, f_center, f_width, scan_time):
step_size = int(scan_time/(1024*4*ns) )
f0 = f_center + f_width/2
#continuous
f_step = f_width /1023
for i in range(1024):
self.freq_list[i] = f0 - f_step*i
dds.frequency_to_ram(self.freq_list, self.freq_list_ram)
self.core.break_realtime()
delay(10 * ms)
dds.set_cfr1(ram_enable=0)
dds.cpld.io_update.pulse_mu(8)
dds.set_profile_ram(start=0, end=1023, step=(step_size | (2**6 - 1 ) << 16),
profile=0, mode=ad9910.RAM_MODE_RAMPUP)
delay(5*ms)
dds.cpld.set_profile(0)
dds.cpld.io_update.pulse_mu(8)
dds.write_ram(self.freq_list_ram)
delay(1*ms)
dds.cpld.set_profile(0)
delay(50*ms)
self.core.wait_until_mu(now_mu())
@kernel
def freeze_RAM(self, idx):
self.scan_dds_trigg.set_profile_ram(start=idx,end=idx,
step=(self.step_size | (2**6 - 1) << 16),profile=0,mode=ad9910.RAM_MODE_RAMPUP)
self.scan_dds_trigg.cpld.io_update.pulse_mu(8)
`