AD9913Controller
- class rfblocks.ad9913_controller.AD9913Controller(controller_id: str, device: ad9913, config: Dict | None = None)[source]
Higher level control for the AD9913 DDS.
Documentation for the AD9913 DDS signal source rfblocks module can be found here: An AD9913 DDS Signal Source.
- Parameters:
controller_id (str) – The controller name.
device (
rfblocks.ad9913
) – An instance ofrfblocks.ad9913
.config (Optional[Dict]) – Initial configuration for the AD9913 DDS board. If this is None the default configuration will be used. See
AD9913Controller.DEFAULT_DDS_CONFIG
for a brief description of the structure forconfig
.
AD9913Controller
maintains the state configuration for an AD9913 DDS board. The following signals are defined:state_changed(int)
freq_changed(float)
phase_changed(float)
level_changed(float)
pmod_changed(bool)
selected_profile_changed(int)
profile_type_changed(ad9913.SweepType)
sweep_start_changed(float)
sweep_end_changed(float)
sweep_type_changed(ad9913.SweepType)
ramp_type_changed(ad9913.SweepRampType)
rising_step_changed(float)
falling_step_changed(float)
rising_rate_changed(float)
falling_rate_changed(float)
profile_freq_changed(int, float)
profile_phase_changed(int, float)
An example of creating a
DDSChan
instance:DEFAULT_DDS_FREQ = 10.0 DEFAULT_DDS_PHASE = 0.0 DEFAULT_DDS_LEVEL = 512 DEFAULT_DDS_CONFIG: Dict = { 'freq': DEFAULT_DDS_FREQ, 'ph': DEFAULT_DDS_PHASE, 'lvl': DEFAULT_DDS_LEVEL, 'lvl_units': DDSChan.LVL_UNITS.DBM, 'pmod': False, 'state': ad9913.POWER_UP, 'sweep': { 'type': ad9913.SweepType.FREQUENCY, 'start': 0.5, 'end': 5.0, 'ramp': ad9913.SweepRampType.SWEEP_OFF, 'delta': [0.1, 0.1], # MHz per step 'rate': [10.0, 10.0], # microsecs per step 'dwell': False, 'trigsrc': ad9913.SweepTriggerSource.REGISTER, 'trigtype': ad9913.SweepTriggerType.EDGE_TRIGGER }, 'profiles': [[0.5, 0.0], [1.0, 0.0], [2.0, 0.0], [5.0, 0.0], [10.0, 0.0], [20.0, 0.0], [50.0, 0.0], [100.0, 0.0]], 'selected_profile': 0, 'profile_type': ad9913.SweepType.FREQUENCY } DDSCH1_HWCONF: Dict = {'cs': 'C6', 'io_update': 'B6', 'reset': 'C4', 'ps0': 'D4', 'ps1': 'D5', 'ps2': 'D6', 'board_model': '27dB-RF'} device = ad9913(**DDSCH1_HWCONF) ch1_ctl = AD9913Controller('dds1', device, DEFAULT_DDS_CONFIG)
- configure(ser: Serial) None [source]
Set the DDS hardware ‘base’ configuration.
- Parameters:
ser (serial.Serial) –
This will configure the DDS hardware for the following:
Output frequency
Phase offset
Output level
Active state - powered on or off
If the programmable modulus flag is set to
True
, the controller will also attempt to set the configured output frequency precisely.
- configure_profile(ser: Serial) None [source]
Update the AD9913 registers to use the currently selected profile.
- configure_sweep(ser: Serial) None [source]
Update the DDS hardware registers with the current sweep parameters.
- property dbm: float
The DDS output level in dBm.
This emits the
level_changed(float)
signal if thelevel
value is changed.- Type:
float
Note that
configure()
must be invoked in order to update the DDS hardware>>> dds = ad9913('d1', 'd2', sysclk=250.0, board_model="20dB") >>> ctl = AD9913Controller('dds_ctl', dds) >>> ctl.millivolts = 300 >>> f'{ctl.dbm:.2f}' '2.55' >>> ctl.dbm = -10.0 >>> ctl.level 118
- property dbm_range: List[float]
Return the range of the dbm (power) setting.
The range is returned as a two element list: [min_dbm, max_dbm]
- dbm_to_level(d) float [source]
Return the DAC code for a given output power (in dBm).
>>> dds = ad9913('d1', 'd2') >>> ctl = AD9913Controller('dds_ctl', dds) >>> ctl.freq = 40.0 >>> f'{ctl.level_to_dbm(100):.2f}' '-12.35' >>> f'{ctl.level_to_dbm(512):.2f}' '1.56' >>> f'{ctl.dbm_to_level(1.56):.2f}' '512.00' >>> f'{ctl.dbm_to_level(-12.35):.2f}' '100.00'
- property direct_switch_enabled: bool
Enable or disable direct switch mode.
Setting direct switch mode to
True
enables profile switching. Profile switching may then be done using the internal mode, for example:dds_ctl.direct_switch_enabled = True dds_ctl.profile_type = ad9913.SweepType.FREQUENCY dds_ctl.selected_profile = 4 dds_ctl.configure_profile(ser)
Alternatively, profile switching can be achieved using the profile select pins, for example:
dds_ctl.direct_switch_enabled = True dds_ctl.profile_type = ad9913.SweepType.FREQUENCY dds_ctl._ad9913.set_profile_control( ad9913.ProfileControl.PINS) cmd = 'M{},{:02X},{:02X}:'.format(dds.ps_port, dds.ps_mask, 4) write_cmd(ser, cmd)
- dump_config() Dict [source]
Return the current configuration for this channel.
- Returns:
- A dictionary containing the current DDS channel
configuration:
- freq:
channel frequency
- ph:
phase offset
- lvl:
channel output level (in DAC units)
- pmod:
programmable modulus mode. True for active.
- state:
channel state (active or powered down)
- sweep:
sweep information
- profiles:
channel profile configuration
- selected_profile:
currently selected profile
- profile_type:
frequency or phase
>>> dds = ad9913('d1', 'd2') >>> ctl = AD9913Controller('dds_ctl', dds) >>> from pprint import pprint as pp >>> pp(ctl.dump_config()) {'freq': 10.0, 'lvl': 512, 'ph': 0.0, 'pmod': False, 'profile_type': <SweepType.FREQUENCY: 0>, 'profiles': [[0.5, 0.0], [1.0, 0.0], [2.0, 0.0], [5.0, 0.0], [10.0, 0.0], [20.0, 0.0], [50.0, 0.0], [100.0, 0.0]], 'selected_profile': 0, 'state': 1, 'sweep': {'delta': [0.1, 0.1], 'dwell': False, 'end': 5.0, 'ramp': <SweepRampType.SWEEP_OFF: 0>, 'rate': [10.0, 10.0], 'start': 0.5, 'trigsrc': <SweepTriggerSource.REGISTER: 134217728>, 'trigtype': <SweepTriggerType.EDGE_TRIGGER: 0>, 'type': <SweepType.FREQUENCY: 0>}}
- property freq: float
The DDS output frequency in MHz.
The
freq_changed(float)
signal is emitted if thefreq
value changes.- Type:
float
Note that
configure()
must be invoked in order to update the DDS hardware>>> dds = ad9913('d1', 'd2') >>> ctl = AD9913Controller('dds_ctl', dds) >>> ctl.freq 10.0 >>> ctl.freq = 25.0 >>> ctl.freq 25.0
- property intercept: float
Return the intercept of the DAC code/output mV relation at the currently set output frequency.
- Type:
float
>>> dds = ad9913('d1', 'd2') >>> ctl = AD9913Controller('dds_ctl', dds) >>> dds.freq = 20.0 >>> f'{ctl.intercept:.2f}' '1.78'
- property label
Returns a label string.
This is generally used when constructing a user interface for the controller. See, for example,
qtrfblocks.DDSChan
. If no label has been specified when the controller instance was created thecontroller_id
will be used.>>> dds = ad9913('d1', 'd2') >>> ctl = AD9913Controller('dds_ctl', dds) >>> ctl.label 'dds_ctl'
>>> ctl2 = AD9913Controller( ... 'dds_ctl', dds, ... { 'label': 'Chan X', **AD9913Controller.DEFAULT_DDS_CONFIG }) >>> ctl2.label 'Chan X'
- property level: float
The DDS output level in DDS DAC units (DAC code).
The AD9913 uses a 10-bit DAC so the level range is 0 to 1023. This emits the
level_changed(float)
signal if thelevel
value changes.- Type:
float
Note that
configure()
must be invoked in order to update the DDS hardware>>> dds = ad9913('d1', 'd2') >>> ctl = AD9913Controller('dds_ctl', dds) >>> ctl.level 512 >>> ctl.level = 250 >>> ctl.level 250
- property level_range: List[float]
Return the range of the level setting.
The range is returned as a two element list: [min_level, max_level]
- level_to_dbm(lvl) float [source]
Return the output power (in dBm) for a given DAC code.
>>> dds = ad9913('d1', 'd2') >>> ctl = AD9913Controller('dds_ctl', dds) >>> ctl.freq = 40.0 >>> f'{ctl.level_to_dbm(100):.2f}' '-12.35' >>> f'{ctl.level_to_dbm(512):.2f}' '1.56'
- level_to_millivolts(lvl)[source]
Return RMS output voltage for a given DAC code.
>>> dds = ad9913('d1', 'd2') >>> ctl = AD9913Controller('dds_ctl', dds) >>> ctl.freq = 20.0 >>> f'{ctl.level_to_millivolts(100.0):.2f}' '56.91' >>> ctl.freq = 40.0 >>> f'{ctl.level_to_millivolts(100.0):.2f}' '53.97'
- load_config(config: Dict) None [source]
Set the current configuration for this channel.
- Parameters:
config (Dict) – A dictionary containing the channel configuration to be set.
Note that in order to update the DDS channel hardware
configure()
should be called.>>> dds = ad9913('d1', 'd2') >>> ctl = AD9913Controller('dds_ctl', dds) >>> import copy >>> config = copy.deepcopy(AD9913Controller.DEFAULT_DDS_CONFIG) >>> config['freq'] = 45.0 >>> config['lvl'] = 100 >>> config['sweep']['type'] = ad9913.SweepType.PHASE >>> config['profiles'][0][0] = 10.0 >>> ctl.load_config(config) >>> from pprint import pprint as pp >>> pp(ctl.dump_config()) {'freq': 45.0, 'lvl': 100, 'ph': 0.0, 'pmod': False, 'profile_type': <SweepType.FREQUENCY: 0>, 'profiles': [[10.0, 0.0], [1.0, 0.0], [2.0, 0.0], [5.0, 0.0], [10.0, 0.0], [20.0, 0.0], [50.0, 0.0], [100.0, 0.0]], 'selected_profile': 0, 'state': 1, 'sweep': {'delta': [0.1, 0.1], 'dwell': False, 'end': 5.0, 'ramp': <SweepRampType.SWEEP_OFF: 0>, 'rate': [10.0, 10.0], 'start': 0.5, 'trigsrc': <SweepTriggerSource.REGISTER: 134217728>, 'trigtype': <SweepTriggerType.EDGE_TRIGGER: 0>, 'type': <SweepType.PHASE: 4096>}}
- property millivolts: float
The DDS output level in millivolts.
This emits the
level_changed(float)
signal if thelevel
value is changed.- Type:
float
Note that
configure()
must be invoked in order to update the DDS hardware>>> dds = ad9913('d1', 'd2', board_model="20dB") >>> ctl = AD9913Controller('dds_ctl', dds) >>> ctl.level = 256 >>> f'{ctl.millivolts:.2f}' '151.05' >>> ctl.millivolts = 300 >>> ctl.level 511
- property millivolts_range: List[float]
Return the range of the millivolts setting.
The range is returned as a two element list: [min_millivolts, max_millivolts]
- millivolts_to_level(mv) int [source]
Return the DAC code which produces a given RMS output voltage.
>>> dds = ad9913('d1', 'd2') >>> ctl = AD9913Controller('dds_ctl', dds) >>> ctl.freq = 40.0 >>> f'{ctl.level_to_millivolts(100.0):.2f}' '53.97' >>> f'{ctl.millivolts_to_level(53.97):.2f}' '100.00'
- property phase: float
The DDS phase offset in degrees.
The
phase_changed(float)
signal is emitted if thephase
value changes.- Type:
float
Note that
configure()
must be invoked in order to update the DDS hardware>>> dds = ad9913('d1', 'd2') >>> ctl = AD9913Controller('dds_ctl', dds) >>> ctl.phase 0.0 >>> ctl.phase = 90.0 >>> ctl.phase 90.0
- property pmod: bool
The current DDS programmable modulus state.
This emits the
pmod_changed(bool)
signal if thepmod
value changes.- Type:
bool
This will be True if the programmable modulus facility is active and False otherwise.
- profile_freq(profnum: int) float [source]
The profile frequency (in MHz) for the specified profile.
- Parameters:
profnum (An integer in the range 0 to 7.) – The target profile number.
- Raise:
IndexError if an invalid profile is specified.
- profile_phase(profnum: int) float [source]
The profile phase (in degrees) for the specified profile.
- Parameters:
profnum (An integer in the range 0 to 7.) – The target profile number.
- Raise:
IndexError if an invalid profile is specified.
- property profile_type: SweepType
The current profile type.
This emits a
profile_type_changed(ad9913.SweepType)
signal if theprofile_type
value changes.
- property profiles
Returns a list of the currently configured profiles.
The list contains a sublist for each of the eight DDS profiles. Each sublist contains the profile frequency and phase.
- Type:
List
>>> from pprint import pprint as pp >>> dds = ad9913('d1', 'd2') >>> ctl = AD9913Controller('dds_ctl', dds) >>> pp(ctl.profiles) [[0.5, 0.0], [1.0, 0.0], [2.0, 0.0], [5.0, 0.0], [10.0, 0.0], [20.0, 0.0], [50.0, 0.0], [100.0, 0.0]]
- property selected_profile: int
The currently selected DDS profile (0 to 7).
This emits the
selected_profile_changed(int)
signal if theselected_profile
value changes.- Type:
int
- set_cal_data(cal_data: Dict[float, List[float]]) None [source]
Set the calibration data for the board associated with the controller.
- Parameters:
cal_data (Dict[float, List[float]]) – The new calibration data to be used.
The format for the data is:
{ freq-0: [slope-0, intercept-0], freq-1: [slope-1, intercept-1], ... freq-n [slope-n, intercept-n] }
where the frequencies run from 0 to 100MHz.
The slope and intercept data are derived from calibration measurements using the procedure documented in Calibration Procedure.
Note that calibration measurements are made down to 5MHz so the values measured at 5MHz are also used for 0MHz. This allows slope and intercept numbers to be interpolated for f < 5MHz.
This will override any default calibration data as loaded during the initialization of the controller instance.
- set_profile_freq(profnum: int, f: float) None [source]
Set the frequency of the specified profile.
- Parameters:
profnum (An integer in the range 0 to 7.) – The target profile number.
f (float) – The frequency in MHz.
- Raise:
IndexError if an invalid profile is specified.
- set_profile_phase(profnum: int, p: float) None [source]
Set the phase of the specified profile.
- Parameters:
profnum (An integer in the range 0 to 7.) – The target profile number.
p (float) – The phase in degrees.
- Raise:
IndexError if an invalid profile is specified.
- property slope: float
Return the slope of the DAC code/output mV relation at the currently set output frequency.
- Type:
float
>>> dds = ad9913('d1', 'd2') >>> ctl = AD9913Controller('dds_ctl', dds) >>> dds.freq = 20.0 >>> f'{ctl.slope:.2f}' '0.58'
- property state: int
The state of the DDS channel.
This should be either
ad9913.POWER_UP
orad9913.POWER_DOWN
. Thestate_changed(int)
signal is emitted if thestate
changes.- Type:
int
Note that
configure()
must be invoked in order to update the DDS hardware
- property sweep_config
Returns a dict containing the current DDS sweep parameters.
>>> from pprint import pprint as pp >>> dds = ad9913('d1', 'd2') >>> ctl = AD9913Controller('dds_ctl', dds) >>> pp(ctl.sweep_config) {'delta': [0.1, 0.1], 'dwell': False, 'end': 5.0, 'ramp': <SweepRampType.SWEEP_OFF: 0>, 'rate': [10.0, 10.0], 'start': 0.5, 'trigsrc': <SweepTriggerSource.REGISTER: 134217728>, 'trigtype': <SweepTriggerType.EDGE_TRIGGER: 0>, 'type': <SweepType.FREQUENCY: 0>}
- property sweep_end: float
The sweep end value.
This will be the frequency if
sweep_type
isad9913.SweepType.FREQUENCY
or the phase ifsweep_type
isad9913.SweepType.PHASE
.This will emit a
sweep_end_changed(float)
signal if thesweep_end
value changes.
- property sweep_falling_rate: float
The current falling rate value in seconds.
- Type:
float
A
falling_rate_changed(float)
signal is emitted if thesweep_falling_rate
value changes.Note
Note that there is granularity with the sweep ramp rates. This will depend on the system clock. With the default system clock of 250 MHz the smallest possible step is 4 nS. This will also be the smallest increment between step sizes.
Since the ramp rate registers are 16 bits, the largest possible step size (with a 250 MHz system clock) will be 262 uS.
- property sweep_falling_step: float
The current sweep falling step value.
This will be a frequency value if
sweep_type
isad9913.SweepType.FREQUENCY
or a phase value ifsweep_type
isad9913.SweepType.PHASE
.- Type:
float
A
falling_step_changed(float)
signal is emitted if thefalling_step
value changes.
- property sweep_ramp_type: SweepRampType
The current sweep ramp type.
This is one of values defined in
rfblocks.ad9913.SweepRampType
.A
ramp_type_changed(ad9913.SweepRampType)
signal is emitted if thesweep_ramp_type
changes.
- property sweep_rising_rate: float
The current rising rate value in seconds.
- Type:
float
A
rising_rate_changed(float)
signal is emitted if thesweep_rising_rate
value changes.Note
Note that there is granularity with the sweep ramp rates. This will depend on the system clock. With the default system clock of 250 MHz the smallest possible step is 4 nS. This will also be the smallest increment between step sizes.
Since the ramp rate registers are 16 bits, the largest possible step size (with a 250 MHz system clock) will be 262 uS.
- property sweep_rising_step: float
The current sweep rising step value.
This will be a frequency value if
sweep_type
isad9913.SweepType.FREQUENCY
or a phase value ifsweep_type
isad9913.SweepType.PHASE
.- Type:
float
A
rising_step_changed(float)
signal is emitted if therising_step
value changes.
- property sweep_start: float
The sweep start value.
This will be the frequency if
sweep_type
isad9913.SweepType.FREQUENCY
or the phase ifsweep_type
isad9913.SweepType.PHASE
.This will emit a
sweep_start_changed(float)
signal if thesweep_start
value changes.
- property sweep_type: SweepType
The current sweep type.
This is one of values defined in
rfblocks.ad9913.SweepType
.A
sweep_type_changed(ad9913.SweepType)
signal is emitted if thesweep_type
changes.