-
Notifications
You must be signed in to change notification settings - Fork 67
AD9959 DDS Sweeper #126
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
AD9959 DDS Sweeper #126
Conversation
… for user to specify mode. Currently defaults to single steps
A result from testing in the lab: we should either remove timing mode or implement it fully. At the moment, the labscript device just sends an instruction table without timings (regardless of whether timing mode is 1 or 0), which fails. I think I am leaning towards removing it, but I could go either way. On a related note, we should check that the number of bytes we want to send matches the bytes the Pico is ready for. |
…n binary mode. At the moment, if it fails it will simply send all zeros and throw an error. I think this is the best option, as it prevents the device from getting locked up waiting for bytes (if we restart the blacs tab).
I added the "ready for ... bytes" check and an option to use an external reference clock for the labscript device. These should be tested before merging. |
Talked a tad with David and we settled on not supporting internal timing at all since the prawnblaster clockline is much better for less work. I'll remove those and try to add any remaining docs |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Mostly minor tweaks, with two more significant things
- I am now noticing that we basically have a firmware limitation that there must be at least one dynamic output to use this device. I get why and it isn't totally unreasonable, I do think it could be worth allowing for all the outputs to be StaticDDS.
- The BLACS worker transition functions feel a bit rough. They don't quite respect the conventional boundaries (which are admittedly poorly documented) and I think
transition_to_buffered
has a number of edge case issues that need to be tested and fixed. I would appreciate testing confirmation that everything works as expected if you do the following things:
- Program no outputs
- Only use StaticDDS
- Only Use DDS
- That the BLACS tab updates to the final values of all outputs correctly
- That aborts leave the device in a functional state
) | ||
|
||
def program_manual(self, values): | ||
self.intf.abort() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not sure how I feel about calling abort
all the time. I'd much rather it only be called when we actually need to abort a shot. If we are having troubles with synchronization of device state with the worker, I'd just as soon solve them.
|
||
with h5py.File(h5file, 'r') as hdf5_file: | ||
group = hdf5_file['devices'][device_name] | ||
dds_data = group['dds_data'] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is dds_data
guaranteed to always be present? I suppose it is since you can't actually set the number of dynamic channels to 0...
#chann2 = DDS( 'chann2', AD9959, 'channel 2') | ||
chann3 = DDS( 'chann3', AD9959, 'channel 3') |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We should remove commented lines from the example.
Also, this configuration is wrong, since you can't specify a dynamic channel after a Static one (at the firmware level at least).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think this is resolved in latest commit.
def __init__(self, name, parent_device, com_port, | ||
sweep_mode=0, | ||
ref_clock_external=0, ref_clock_frequency=125e6, pll_mult=4, **kwargs): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
A somewhat unclear limitation that is only implicitly enforced is that there must be at least one dynamic channel for the device to work correctly.
While we could just document that and move on, I think I'd just as soon make a minor tweak to the firmware to allow for the Sweeper outputs to all be static (if desired). Maybe not a common request, but I think it will actually make handling static and dynamic channels a little easier as there won't be any strange edge cases to handle.
def set_output(self, channel, frequency, amplitude, phase): | ||
'''Set frequency, amplitude, and phase of a channel.''' |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
While I can maybe be persuaded that not all functions in parameter and return docstrings, functions like this one would benefit from being more explicit about types and units of the inputs.
Other mandatory ones for me include set
, set_channels
, set_batch
, stop
, and __init__
, though I personally think all functions of this interface class should be fully documented, if at all.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think this is resolved in latest commit.
chan_int = int(chan[8:]) | ||
self.intf.set_output(chan_int, values[chan]['freq'], values[chan]['amp'], values[chan]['phase']) | ||
|
||
def transition_to_buffered(self, device_name, h5file, initial_values, fresh): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We should add smart programming support (basically copied from PrawnDO)
…l need to implement PrawnDO style smart cache and graceful aborts
I found a rather insidious bug: if two instructions are spaced by less than the reprogramming time (10us for 4 channels), the trigger for the second instruction arrives before the DDS Sweeper has finished reprogramming the AD9959. I think the result of this is a partial update of the AD9959 parameters. For an example of this, we were trying to use the DDS Sweeper to pulse on an AOM for 4us, 8us, and 12us. In the first two cases, the second instruction doesn't run, and the AOM never turns off. |
Ah, good catch. The firmware definitely doesn't do sensible things when a trigger is missed (since it doesn't even know it missed it). It's on the @Json-To-String I believe the NovaTechDDS has the core infrastructure necessary to handle this (though we'll need to update the rate based on mode and number of dynamic channels). If I remember correctly, you only need to define the right class variable and the labscript machinery will catch the error automatically at compile time. |
No description provided.