ddsctrl 0.5.4__tar.gz → 0.7.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {ddsctrl-0.5.4 → ddsctrl-0.7.0}/LICENSE.txt +0 -0
- {ddsctrl-0.5.4/ddsctrl.egg-info → ddsctrl-0.7.0}/PKG-INFO +1 -1
- {ddsctrl-0.5.4 → ddsctrl-0.7.0}/bin/ddscontroller +145 -52
- ddsctrl-0.7.0/ddsctrl/ad9912ui.py +225 -0
- ddsctrl-0.7.0/ddsctrl/ad9915ui.py +245 -0
- {ddsctrl-0.5.4 → ddsctrl-0.7.0}/ddsctrl/constants.py +4 -0
- ddsctrl-0.7.0/ddsctrl/ddsctrlui.py +126 -0
- {ddsctrl-0.5.4 → ddsctrl-0.7.0}/ddsctrl/version.py +5 -1
- {ddsctrl-0.5.4 → ddsctrl-0.7.0/ddsctrl.egg-info}/PKG-INFO +1 -1
- {ddsctrl-0.5.4 → ddsctrl-0.7.0}/ddsctrl.egg-info/SOURCES.txt +2 -0
- ddsctrl-0.5.4/ddsctrl/ddsctrlui.py +0 -302
- {ddsctrl-0.5.4 → ddsctrl-0.7.0}/MANIFEST.in +0 -0
- {ddsctrl-0.5.4 → ddsctrl-0.7.0}/README +0 -0
- {ddsctrl-0.5.4 → ddsctrl-0.7.0}/ddsctrl/__init__.py +0 -0
- {ddsctrl-0.5.4 → ddsctrl-0.7.0}/ddsctrl/version.local.py +0 -0
- {ddsctrl-0.5.4 → ddsctrl-0.7.0}/ddsctrl.egg-info/dependency_links.txt +0 -0
- {ddsctrl-0.5.4 → ddsctrl-0.7.0}/ddsctrl.egg-info/requires.txt +0 -0
- {ddsctrl-0.5.4 → ddsctrl-0.7.0}/ddsctrl.egg-info/top_level.txt +0 -0
- {ddsctrl-0.5.4 → ddsctrl-0.7.0}/setup.cfg +0 -0
- {ddsctrl-0.5.4 → ddsctrl-0.7.0}/setup.py +0 -0
|
File without changes
|
|
@@ -16,12 +16,15 @@ import sys
|
|
|
16
16
|
import argparse
|
|
17
17
|
import os.path as path
|
|
18
18
|
import logging
|
|
19
|
-
import itertools
|
|
20
19
|
import usb.core
|
|
21
|
-
import
|
|
20
|
+
import serial
|
|
21
|
+
import serial.tools.list_ports
|
|
22
|
+
from decimal import Decimal
|
|
22
23
|
from PyQt5.QtCore import QSettings
|
|
23
24
|
from PyQt5.QtWidgets import QApplication, QMainWindow, QMessageBox
|
|
24
25
|
|
|
26
|
+
from ad9xdds.interfaces.fx2intf import Fx2Intf
|
|
27
|
+
from ad9xdds.interfaces.stm32intf import Stm32Intf
|
|
25
28
|
import ad9xdds.sad9912dev as sad9912dev
|
|
26
29
|
import ad9xdds.ad9912dev as ad9912dev
|
|
27
30
|
import ad9xdds.sad9915dev_umr232hm as sad9915dev
|
|
@@ -34,7 +37,8 @@ from ddsctrl.constants import ORGANIZATION, APP_NAME, DEFAULT_AUTO_UPDATE, \
|
|
|
34
37
|
DEFAULT_AD9912_CP_CURRENT, DEFAULT_AD9912_VCO_RANGE, \
|
|
35
38
|
DEFAULT_AD9912_PLL_DOUBLER, DEFAULT_AD9912_HSTL_EN, \
|
|
36
39
|
DEFAULT_AD9912_CMOS_EN, DEFAULT_AD9912_IFREQ, \
|
|
37
|
-
DEFAULT_AD9912_HSTL_DOUBLER
|
|
40
|
+
DEFAULT_AD9912_HSTL_DOUBLER, \
|
|
41
|
+
DDS_MODEL
|
|
38
42
|
|
|
39
43
|
CONSOLE_LOG_LEVEL = logging.DEBUG
|
|
40
44
|
FILE_LOG_LEVEL = logging.DEBUG
|
|
@@ -77,13 +81,15 @@ def parse_cmd_line():
|
|
|
77
81
|
type=str, default='', help='-ini=<str>')
|
|
78
82
|
parser.add_argument('-wt', '--window_title', dest='wtitle',
|
|
79
83
|
type=str, default="DDS controller")
|
|
80
|
-
parser.add_argument('-d', '--dds_model', choices=["
|
|
84
|
+
parser.add_argument('-d', '--dds_model', choices=["AD9912_FX2", "AD9912_STM32", "AD9915"],
|
|
81
85
|
dest='model')
|
|
82
86
|
parser.add_argument('-b', '--dds_bus', type=int, dest='bus')
|
|
83
87
|
parser.add_argument('-a', '--dds_address', type=int, dest='address')
|
|
88
|
+
parser.add_argument('-s', '--serial_device', type=str, dest='serial_device')
|
|
84
89
|
args = parser.parse_args()
|
|
85
|
-
if args.model and (args.bus is None or args.address is None)
|
|
86
|
-
|
|
90
|
+
if ( args.model and (args.bus is None or args.address is None) ) and \
|
|
91
|
+
args.model=="AD9912_STM32" and args.serial is None:
|
|
92
|
+
parser.error("--dds_model requires --dds_bus and --dds_adress or --serial_device.")
|
|
87
93
|
if not args.model and (args.bus is not None or args.address is not None):
|
|
88
94
|
parser.error("--dds_bus and --dds_adress requires --dds_model.")
|
|
89
95
|
return args
|
|
@@ -129,7 +135,7 @@ def save_all_settings(ui):
|
|
|
129
135
|
settings.setValue('dds_ctrl/phase', ui.ocontroller_ui.phy_tuning.value())
|
|
130
136
|
settings.setValue('dds_ctrl/amp', ui.ocontroller_ui.amp_tuning.value())
|
|
131
137
|
settings.setValue('dds_ctrl/ifreq', ui.icontroller_ui.ifreq_dsb.value())
|
|
132
|
-
if model ==
|
|
138
|
+
if model == DDS_MODEL.AD9912_FX2 or model == DDS_MODEL.AD9912_STM32:
|
|
133
139
|
settings.setValue('dds_ctrl/hstl_en',
|
|
134
140
|
int(ui.ocontroller_ui.out_hstl_ckbox.isChecked()))
|
|
135
141
|
settings.setValue('dds_ctrl/cmos_en',
|
|
@@ -203,13 +209,13 @@ def check_ini_file(user_ini_file=None):
|
|
|
203
209
|
|
|
204
210
|
|
|
205
211
|
# =============================================================================
|
|
206
|
-
def init_ui(ui, model):
|
|
212
|
+
def init_ui(ui, model, dds):
|
|
207
213
|
"""Init UI.
|
|
208
214
|
:param ui: ddsctrlui class instance (object)
|
|
209
215
|
:param model: dds model i.e. "AD9912" or "AD9915" (str)
|
|
210
216
|
:returns: None
|
|
211
217
|
"""
|
|
212
|
-
if model ==
|
|
218
|
+
if model == DDS_MODEL.AD9912_FX2 or model == DDS_MODEL.AD9912_STM32:
|
|
213
219
|
amax = ad9912dev.AMAX
|
|
214
220
|
ifmax = ad9912dev.IFMAX
|
|
215
221
|
else:
|
|
@@ -221,9 +227,9 @@ def init_ui(ui, model):
|
|
|
221
227
|
# Init UI with respect to (ini file) actual dds configuration
|
|
222
228
|
settings = QSettings()
|
|
223
229
|
auto_update = bool(settings.value('dds_ctrl/auto_update'))
|
|
224
|
-
ofreq =
|
|
225
|
-
ifreq =
|
|
226
|
-
phase =
|
|
230
|
+
ofreq = Decimal(settings.value('dds_ctrl/ofreq'))
|
|
231
|
+
ifreq = Decimal(settings.value('dds_ctrl/ifreq'))
|
|
232
|
+
phase = Decimal(settings.value('dds_ctrl/phase'))
|
|
227
233
|
amp = int(settings.value('dds_ctrl/amp'))
|
|
228
234
|
##ui.ocontroller_ui.auto_update_ckbox.setChecked(False)
|
|
229
235
|
ui.ocontroller_ui.auto_update_ckbox.setChecked(auto_update)
|
|
@@ -231,7 +237,9 @@ def init_ui(ui, model):
|
|
|
231
237
|
ui.ocontroller_ui.phy_tuning.setValue(phase)
|
|
232
238
|
ui.ocontroller_ui.amp_tuning.setValue(amp)
|
|
233
239
|
ui.icontroller_ui.ifreq_dsb.setValue(ifreq)
|
|
234
|
-
|
|
240
|
+
|
|
241
|
+
|
|
242
|
+
if model == DDS_MODEL.AD9912_FX2 or model == DDS_MODEL.AD9912_STM32:
|
|
235
243
|
hstl_en = bool(int(settings.value('dds_ctrl/hstl_en')))
|
|
236
244
|
cmos_en = bool(int(settings.value('dds_ctrl/cmos_en')))
|
|
237
245
|
hstl_doubler = bool(int(settings.value('dds_ctrl/hstl_doubler')))
|
|
@@ -249,7 +257,20 @@ def init_ui(ui, model):
|
|
|
249
257
|
ui.icontroller_ui.vco_range_cbox.setCurrentIndex(vco_range)
|
|
250
258
|
ui.icontroller_ui.cp_current_cbox.setCurrentIndex(cp_current)
|
|
251
259
|
ui.icontroller_ui.update_sys_clock_label()
|
|
252
|
-
|
|
260
|
+
|
|
261
|
+
elif model == DDS_MODEL.AD9915:
|
|
262
|
+
# Update input PLL configuration
|
|
263
|
+
ui.icontroller_ui.input_doubler_enable_ckbox.setChecked(dds.get_input_doubler_state())
|
|
264
|
+
ui.icontroller_ui.input_divider_sbox.setValue(dds.get_input_divider_factor())
|
|
265
|
+
# Update PLL attribute
|
|
266
|
+
ui.icontroller_ui.pll_enable_ckbox.setChecked(dds.get_pll_state())
|
|
267
|
+
ui.icontroller_ui.pll_multiplier_sbox.setValue(dds.get_pll_multiplier_factor())
|
|
268
|
+
#
|
|
269
|
+
if ui.icontroller_ui.pll_enable_ckbox.isChecked() is True:
|
|
270
|
+
if dds.vco_calibration() is False:
|
|
271
|
+
logging.error("PLL is not locked")
|
|
272
|
+
#
|
|
273
|
+
ui.icontroller_ui.update_sys_clock_label(dds.get_sysfreq())
|
|
253
274
|
|
|
254
275
|
# =============================================================================
|
|
255
276
|
def init_dev(dds, model):
|
|
@@ -259,15 +280,15 @@ def init_dev(dds, model):
|
|
|
259
280
|
:returns: None
|
|
260
281
|
"""
|
|
261
282
|
settings = QSettings()
|
|
262
|
-
ofreq =
|
|
263
|
-
ifreq =
|
|
264
|
-
phase =
|
|
283
|
+
ofreq = Decimal(settings.value('dds_ctrl/ofreq'))
|
|
284
|
+
ifreq = Decimal(settings.value('dds_ctrl/ifreq'))
|
|
285
|
+
phase = Decimal(settings.value('dds_ctrl/phase'))
|
|
265
286
|
amp = int(settings.value('dds_ctrl/amp'))
|
|
266
287
|
dds.set_ofreq(ofreq)
|
|
267
288
|
dds.set_ifreq(ifreq)
|
|
268
289
|
dds.set_phy(phase)
|
|
269
290
|
dds.set_amp(amp)
|
|
270
|
-
if model ==
|
|
291
|
+
if model == DDS_MODEL.AD9912_FX2 or model == DDS_MODEL.AD9912_STM32:
|
|
271
292
|
hstl_en = bool(int(settings.value('dds_ctrl/hstl_en')))
|
|
272
293
|
cmos_en = bool(int(settings.value('dds_ctrl/cmos_en')))
|
|
273
294
|
hstl_doubler = bool(int(settings.value('dds_ctrl/hstl_doubler')))
|
|
@@ -303,7 +324,7 @@ def handle_ocontroller_ui_behavior(ui, dds, model):
|
|
|
303
324
|
lambda: amplitude_changed(ui, dds))
|
|
304
325
|
ui.ocontroller_ui.phy_tuning.valueChanged[str].connect(
|
|
305
326
|
lambda: phase_changed(ui, dds))
|
|
306
|
-
if model ==
|
|
327
|
+
if model == DDS_MODEL.AD9912_FX2 or model == DDS_MODEL.AD9912_STM32:
|
|
307
328
|
ui.ocontroller_ui.out_hstl_ckbox.stateChanged.connect(
|
|
308
329
|
lambda: hstl_output_changed(ui, dds))
|
|
309
330
|
ui.ocontroller_ui.hstl_doubler_ckbox.stateChanged.connect(
|
|
@@ -337,8 +358,10 @@ def get_reg_btn_clicked(ui, dds):
|
|
|
337
358
|
:returns: None
|
|
338
359
|
"""
|
|
339
360
|
try:
|
|
340
|
-
reg_val = dds.get_reg(
|
|
341
|
-
int(str(ui.debug_ui.reg_add_led.text()), 16)
|
|
361
|
+
reg_val = dds._intf.get_reg(
|
|
362
|
+
int(str(ui.debug_ui.reg_add_led.text()), 16),
|
|
363
|
+
1
|
|
364
|
+
)
|
|
342
365
|
except ValueError as ex:
|
|
343
366
|
pass # Error handled in dds.get_reg
|
|
344
367
|
except Exception as ex:
|
|
@@ -354,8 +377,11 @@ def set_reg_btn_clicked(ui, dds):
|
|
|
354
377
|
:param dds: dds class instance (object)
|
|
355
378
|
:returns: None
|
|
356
379
|
"""
|
|
357
|
-
dds.set_reg(
|
|
358
|
-
|
|
380
|
+
dds._intf.set_reg(
|
|
381
|
+
int(str(ui.debug_ui.reg_add_led.text()), 16),
|
|
382
|
+
1,
|
|
383
|
+
int(str(ui.debug_ui.reg_val_led.text()), 16)
|
|
384
|
+
)
|
|
359
385
|
|
|
360
386
|
|
|
361
387
|
# =============================================================================
|
|
@@ -366,7 +392,7 @@ def ofreq_changed(ui, dds, **kwargs):
|
|
|
366
392
|
:returns: None
|
|
367
393
|
"""
|
|
368
394
|
act_ofreq = dds.set_ofreq(ui.ocontroller_ui.ofreq_tuning.value())
|
|
369
|
-
ui.ocontroller_ui.ofreq_tuning.setValue(
|
|
395
|
+
ui.ocontroller_ui.ofreq_tuning.setValue(Decimal(act_ofreq))
|
|
370
396
|
QSettings().setValue('dds_ctrl/ofreq', act_ofreq)
|
|
371
397
|
|
|
372
398
|
|
|
@@ -378,7 +404,7 @@ def amplitude_changed(ui, dds):
|
|
|
378
404
|
:returns: None
|
|
379
405
|
"""
|
|
380
406
|
act_amp = dds.set_amp(int(ui.ocontroller_ui.amp_tuning.value()))
|
|
381
|
-
ui.ocontroller_ui.amp_tuning.setValue(
|
|
407
|
+
ui.ocontroller_ui.amp_tuning.setValue(Decimal(act_amp))
|
|
382
408
|
QSettings().setValue('dds_ctrl/amp', act_amp)
|
|
383
409
|
|
|
384
410
|
|
|
@@ -390,7 +416,7 @@ def phase_changed(ui, dds):
|
|
|
390
416
|
:returns: None
|
|
391
417
|
"""
|
|
392
418
|
act_phy = dds.set_phy(ui.ocontroller_ui.phy_tuning.value())
|
|
393
|
-
ui.ocontroller_ui.phy_tuning.setValue(
|
|
419
|
+
ui.ocontroller_ui.phy_tuning.setValue(Decimal(act_phy))
|
|
394
420
|
QSettings().setValue('dds_ctrl/phase', act_phy)
|
|
395
421
|
|
|
396
422
|
|
|
@@ -439,7 +465,9 @@ def apply_btn_ictrl_clicked(ui, dds, model):
|
|
|
439
465
|
"""
|
|
440
466
|
# Update input frequency
|
|
441
467
|
dds.set_ifreq(ui.icontroller_ui.ifreq_dsb.value())
|
|
442
|
-
|
|
468
|
+
|
|
469
|
+
# Configure System Clock
|
|
470
|
+
if model == DDS_MODEL.AD9912_FX2 or model == DDS_MODEL.AD9912_STM32:
|
|
443
471
|
# Update PLL attribute
|
|
444
472
|
dds.set_pll_state(ui.icontroller_ui.advanced_config_gbox.isChecked())
|
|
445
473
|
dds.set_pll_doubler_state(
|
|
@@ -448,9 +476,25 @@ def apply_btn_ictrl_clicked(ui, dds, model):
|
|
|
448
476
|
ui.icontroller_ui.pll_factor_sbox.value())
|
|
449
477
|
dds.set_cp_current(ui.icontroller_ui.cp_current_cbox.currentIndex())
|
|
450
478
|
dds.set_vco_range(ui.icontroller_ui.vco_range_cbox.currentIndex())
|
|
479
|
+
elif model == DDS_MODEL.AD9915:
|
|
480
|
+
# Update input PLL configuration
|
|
481
|
+
dds.set_input_doubler_state(ui.icontroller_ui.input_doubler_enable_ckbox.isChecked())
|
|
482
|
+
dds.set_input_divider_factor(ui.icontroller_ui.input_divider_sbox.value())
|
|
483
|
+
# Update PLL attribute
|
|
484
|
+
dds.set_pll_state(ui.icontroller_ui.pll_enable_ckbox.isChecked())
|
|
485
|
+
dds.set_pll_multiplier_factor(
|
|
486
|
+
ui.icontroller_ui.pll_multiplier_sbox.value())
|
|
487
|
+
if ui.icontroller_ui.pll_enable_ckbox.isChecked() is True:
|
|
488
|
+
if dds.vco_calibration() is False:
|
|
489
|
+
logging.error("PLL is not locked")
|
|
490
|
+
|
|
491
|
+
# Update system clock label after system clock configuration
|
|
492
|
+
ui.icontroller_ui.sfreq_led.setText(str(dds.get_sysfreq()))
|
|
493
|
+
|
|
451
494
|
# Force update of output frequency in case of system clock is updated
|
|
452
495
|
# because output is a function of system clock frequency value.
|
|
453
|
-
|
|
496
|
+
ofreq_changed(ui, dds)
|
|
497
|
+
|
|
454
498
|
# Save new settings in ini file.
|
|
455
499
|
settings = QSettings()
|
|
456
500
|
settings.setValue('dds_ctrl/ifreq', ui.icontroller_ui.ifreq_dsb.value())
|
|
@@ -468,6 +512,7 @@ def apply_btn_ictrl_clicked(ui, dds, model):
|
|
|
468
512
|
ui.icontroller_ui.cp_current_cbox.currentIndex())
|
|
469
513
|
|
|
470
514
|
|
|
515
|
+
|
|
471
516
|
# =============================================================================
|
|
472
517
|
def apply_btn_octrl_clicked(ui, dds, model):
|
|
473
518
|
"""Define action when apply button of output control form is clicked.
|
|
@@ -479,33 +524,49 @@ def apply_btn_octrl_clicked(ui, dds, model):
|
|
|
479
524
|
ofreq_changed(ui, dds)
|
|
480
525
|
amplitude_changed(ui, dds)
|
|
481
526
|
phase_changed(ui, dds)
|
|
482
|
-
if model ==
|
|
527
|
+
if model == DDS_MODEL.AD9912_FX2 or model == DDS_MODEL.AD9912_STM32:
|
|
483
528
|
hstl_output_changed(ui, dds)
|
|
484
529
|
hstl_doubler_changed(ui, dds)
|
|
485
530
|
cmos_output_changed(ui, dds)
|
|
486
531
|
|
|
487
532
|
|
|
533
|
+
# =============================================================================
|
|
534
|
+
def list_registers(dds: ad9915dev.Ad9915Dev) -> None:
|
|
535
|
+
for reg_name in ad9915dev.REGNAME2ADDR:
|
|
536
|
+
reg_address = ad9915dev.REGNAME2ADDR[reg_name]
|
|
537
|
+
reg_value = dds.get_reg(reg_address)
|
|
538
|
+
print(f"{reg_name} (0x{reg_address:02X}): {reg_value:04X}")
|
|
539
|
+
|
|
540
|
+
|
|
488
541
|
# =============================================================================
|
|
489
542
|
def find_dds_board():
|
|
490
|
-
"""Return
|
|
543
|
+
"""Return dictionary of AD9912 and AD9915 dds development board connected to
|
|
491
544
|
computer.
|
|
492
|
-
:returns:
|
|
545
|
+
:returns: dict of lists of AD9912 and AD9915 dds (list of usb.core.Device)
|
|
493
546
|
"""
|
|
494
|
-
# if firmware of AD9912Dev board is not loaded, display msgbox.
|
|
547
|
+
# if (FX2) firmware of AD9912Dev board is not loaded, display msgbox.
|
|
495
548
|
if usb.core.find(idVendor=0x0456, idProduct=0xee08) is not None or \
|
|
496
549
|
usb.core.find(idVendor=0x0456, idProduct=0xee24) is not None:
|
|
497
550
|
QMessageBox().warning(None, title="AD9912 detected",
|
|
498
|
-
text="
|
|
499
|
-
# AD9912 dev board
|
|
500
|
-
|
|
551
|
+
text="FX2 interface detected but firmware is not loaded")
|
|
552
|
+
# AD9912 dev board with FX2 interface (native USB port of eval board)
|
|
553
|
+
ad9912devFx2 = usb.core.find(idVendor=0x0456, idProduct=0xee09, find_all=True)
|
|
554
|
+
# AD9912 dev board with STM32 uC interface (external control of eval board)
|
|
555
|
+
ad9912devStm32 = usb.core.find(idVendor=0x0483, find_all=True)
|
|
501
556
|
# AD9915 dev board (through FTDI UM232H)
|
|
502
557
|
ad9915dev = usb.core.find(idVendor=0x0403, idProduct=0x6014, find_all=True)
|
|
503
|
-
return {
|
|
558
|
+
return {
|
|
559
|
+
'ad9912devFx2': ad9912devFx2,
|
|
560
|
+
'ad9912devStm32': ad9912devStm32,
|
|
561
|
+
'ad9915dev': ad9915dev,
|
|
562
|
+
}
|
|
504
563
|
|
|
505
564
|
|
|
506
565
|
# =============================================================================
|
|
507
566
|
def ddscontroller(ini_file, window_title,
|
|
508
|
-
dds_model=None,
|
|
567
|
+
dds_model=None,
|
|
568
|
+
dds_bus=None, dds_address=None,
|
|
569
|
+
serial_device=None):
|
|
509
570
|
"""Main program of DDS controller project.
|
|
510
571
|
:returns: None
|
|
511
572
|
"""
|
|
@@ -513,33 +574,64 @@ def ddscontroller(ini_file, window_title,
|
|
|
513
574
|
app.setOrganizationName(ORGANIZATION)
|
|
514
575
|
app.setApplicationName(APP_NAME)
|
|
515
576
|
|
|
516
|
-
if dds_model is None or dds_bus is None or dds_address is None:
|
|
577
|
+
if dds_model is None or dds_bus is None or dds_address is None or serial_device is None:
|
|
517
578
|
# Select board to handle
|
|
518
579
|
dds_boards = find_dds_board()
|
|
519
|
-
|
|
580
|
+
if 'ad9912devStm32' in dds_boards:
|
|
581
|
+
dds_serial = serial.tools.list_ports.grep("0483*")
|
|
582
|
+
dds_boards_for_human = [
|
|
583
|
+
f"AD9912 with serial ID {dds.device}" for dds in dds_serial
|
|
584
|
+
]
|
|
585
|
+
else:
|
|
586
|
+
dds_boards_for_human = []
|
|
587
|
+
dds_boards_for_human += \
|
|
520
588
|
["AD9912 on bus {} address {}".format(dds.bus, dds.address)
|
|
521
|
-
for dds in dds_boards['
|
|
589
|
+
for dds in dds_boards['ad9912devFx2']] + \
|
|
522
590
|
["AD9915 on bus {} address {}".format(dds.bus, dds.address)
|
|
523
591
|
for dds in dds_boards['ad9915dev']]
|
|
524
592
|
msg = DdsBoardSelection(dds_boards_for_human)
|
|
525
593
|
retval = msg.exec_()
|
|
526
594
|
if retval == 1:
|
|
527
595
|
dds_item = msg.dds_selected()
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
596
|
+
if dds_item.split()[0] == "AD9915":
|
|
597
|
+
dds_model = DDS_MODEL.AD9915
|
|
598
|
+
dds_bus = int(dds_item.split()[3])
|
|
599
|
+
dds_address = int(dds_item.split()[5])
|
|
600
|
+
elif dds_item.split()[0] == "AD9912Fx2":
|
|
601
|
+
dds_model = DDS_MODEL.AD9912_FX2
|
|
602
|
+
dds_bus = int(dds_item.split()[3])
|
|
603
|
+
dds_address = int(dds_item.split()[5])
|
|
604
|
+
else:
|
|
605
|
+
dds_model= DDS_MODEL.AD9912_STM32
|
|
606
|
+
print(f"dds_item: {dds_item}")
|
|
607
|
+
serial_device = dds_item.split()[4]
|
|
531
608
|
else:
|
|
532
609
|
sys.exit()
|
|
533
|
-
if dds_model ==
|
|
534
|
-
|
|
535
|
-
|
|
610
|
+
if dds_model == DDS_MODEL.AD9912_FX2:
|
|
611
|
+
intf = Fx2Intf(
|
|
612
|
+
vendor_id=0x0456,
|
|
613
|
+
product_id=0xee09,
|
|
614
|
+
bus=dds_bus,
|
|
615
|
+
address=dds_address)
|
|
616
|
+
dds = sad9912dev.SAd9912Dev(intf)
|
|
617
|
+
retval = dds.connect()
|
|
536
618
|
if retval is False:
|
|
537
619
|
logging.critical('DDS connection problem')
|
|
538
620
|
QMessageBox.critical(None, 'DDS connection error',
|
|
539
621
|
'DDS connection problem\nCheck connection')
|
|
540
622
|
sys.exit()
|
|
541
|
-
ui = DdsCtrlUi(
|
|
542
|
-
elif dds_model ==
|
|
623
|
+
ui = DdsCtrlUi(DDS_MODEL.AD9912_FX2, dds)
|
|
624
|
+
elif dds_model == DDS_MODEL.AD9912_STM32:
|
|
625
|
+
intf = Stm32Intf(port=serial_device)
|
|
626
|
+
dds = sad9912dev.SAd9912Dev(intf)
|
|
627
|
+
retval = dds.connect()
|
|
628
|
+
if retval is False:
|
|
629
|
+
logging.critical('DDS connection problem')
|
|
630
|
+
QMessageBox.critical(None, 'DDS connection error',
|
|
631
|
+
'DDS connection problem\nCheck connection')
|
|
632
|
+
sys.exit()
|
|
633
|
+
ui = DdsCtrlUi(DDS_MODEL.AD9912_STM32, dds)
|
|
634
|
+
elif dds_model == DDS_MODEL.AD9915:
|
|
543
635
|
dds = sad9915dev.SAd9915Dev()
|
|
544
636
|
url = "ftdi://ftdi:232h:{:x}:{:x}/1".format(dds_bus, dds_address)
|
|
545
637
|
retval = dds.connect(url)
|
|
@@ -548,8 +640,8 @@ def ddscontroller(ini_file, window_title,
|
|
|
548
640
|
QMessageBox.critical(None, 'DDS connection error',
|
|
549
641
|
'DDS connection problem\nCheck connection')
|
|
550
642
|
sys.exit()
|
|
551
|
-
dds.set_operation_mode(ad9915dev.OPMODE['modulus'])
|
|
552
|
-
ui = DdsCtrlUi(
|
|
643
|
+
dds.set_operation_mode(ad9915dev.OPMODE['modulus']) # Fine output frequency tuning mode
|
|
644
|
+
ui = DdsCtrlUi(DDS_MODEL.AD9915, dds)
|
|
553
645
|
else:
|
|
554
646
|
logging.critical('DDS selection problem')
|
|
555
647
|
QMessageBox.critical(None, 'DDS selection error',
|
|
@@ -562,7 +654,7 @@ def ddscontroller(ini_file, window_title,
|
|
|
562
654
|
check_ini_file()
|
|
563
655
|
|
|
564
656
|
init_dev(dds, dds_model)
|
|
565
|
-
init_ui(ui, dds_model)
|
|
657
|
+
init_ui(ui, dds_model, dds)
|
|
566
658
|
handle_ocontroller_ui_behavior(ui, dds, dds_model)
|
|
567
659
|
|
|
568
660
|
# Logic
|
|
@@ -573,7 +665,8 @@ def ddscontroller(ini_file, window_title,
|
|
|
573
665
|
# # - update dds frequency to current display of output frequency
|
|
574
666
|
# # Choice was taken to used the last behavior.
|
|
575
667
|
dds.ifreq_updated.connect(lambda **kwargs: ofreq_changed(ui, dds))
|
|
576
|
-
if dds_model ==
|
|
668
|
+
if dds_model == DDS_MODEL.AD9912_FX2 \
|
|
669
|
+
or dds_model == DDS_MODEL.AD9912_STM32:
|
|
577
670
|
dds.pll_state_updated.connect(
|
|
578
671
|
lambda **kwargs: ofreq_changed(ui, dds))
|
|
579
672
|
dds.pll_doubler_updated.connect(
|
|
@@ -0,0 +1,225 @@
|
|
|
1
|
+
# -*- coding: utf-8 -*-
|
|
2
|
+
|
|
3
|
+
"""package ddsctrl
|
|
4
|
+
author Benoit Dubois
|
|
5
|
+
copyright FEMTO ENGINEERING, 2025
|
|
6
|
+
license GPL v3.0+
|
|
7
|
+
brief Specific User Interface for the AD9912 DDS device.
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
from PyQt5.QtCore import pyqtSignal, pyqtSlot
|
|
11
|
+
from PyQt5.QtWidgets import QWidget, QLineEdit, QPushButton, QLabel, \
|
|
12
|
+
QGridLayout, QVBoxLayout, QHBoxLayout, QGroupBox, QDoubleSpinBox, \
|
|
13
|
+
QCheckBox, QSpinBox, QComboBox
|
|
14
|
+
|
|
15
|
+
import ddsctrl.constants as cst
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
#==============================================================================
|
|
19
|
+
class InControllerUi(QWidget):
|
|
20
|
+
"""Controller UI form used to manage DDS input.
|
|
21
|
+
"""
|
|
22
|
+
|
|
23
|
+
sys_clock_changed = pyqtSignal(float)
|
|
24
|
+
|
|
25
|
+
# def __init__(self, device):
|
|
26
|
+
def __init__(self):
|
|
27
|
+
"""Initialization.
|
|
28
|
+
"""
|
|
29
|
+
super().__init__()
|
|
30
|
+
# self._dev = device
|
|
31
|
+
self.ifreq_dsb = QDoubleSpinBox()
|
|
32
|
+
self.sfreq_led = QLineEdit()
|
|
33
|
+
self.sfreq_led.setReadOnly(True)
|
|
34
|
+
self.apply_btn = QPushButton("&Apply")
|
|
35
|
+
self.advanced_config_gbox = self._system_clock_frequency_config_gbox()
|
|
36
|
+
ifreq_layout = QVBoxLayout()
|
|
37
|
+
ifreq_layout.addWidget(QLabel("Input clock frequency (Hz)"))
|
|
38
|
+
ifreq_layout.addWidget(self.ifreq_dsb)
|
|
39
|
+
ifreq_layout.addWidget(QLabel("System clock frequency (Hz)"))
|
|
40
|
+
ifreq_layout.addWidget(self.sfreq_led)
|
|
41
|
+
ifreq_layout.addWidget(self.advanced_config_gbox)
|
|
42
|
+
ifreq_layout.addStretch(1)
|
|
43
|
+
ifreq_layout.addWidget(self.apply_btn)
|
|
44
|
+
self.setLayout(ifreq_layout)
|
|
45
|
+
# Device update
|
|
46
|
+
# self.advanced_config_gbox.toggled.connect(self._dev.set_pll_state)
|
|
47
|
+
# self.pll_doubler_ckbox.toggled.connect(self._dev.set_pll_doubler_state)
|
|
48
|
+
# self.pll_factor_sbox.valueChanged.connect(self._dev.set_pll_multiplier_factor)
|
|
49
|
+
# System clock display update
|
|
50
|
+
self.advanced_config_gbox.toggled.connect(self.update_sys_clock_label)
|
|
51
|
+
self.pll_doubler_ckbox.toggled.connect(self.update_sys_clock_label)
|
|
52
|
+
self.pll_factor_sbox.valueChanged.connect(self.update_sys_clock_label)
|
|
53
|
+
# UI enable/disable parts
|
|
54
|
+
|
|
55
|
+
self.apply_btn.released.connect(self.update_sys_clock_label)
|
|
56
|
+
self._init_ui()
|
|
57
|
+
|
|
58
|
+
@pyqtSlot(float)
|
|
59
|
+
def set_ifmax(self, value):
|
|
60
|
+
"""Define maximum frequency handled by widget.
|
|
61
|
+
:param value: New maximal value of parameter (float)
|
|
62
|
+
:returns: None
|
|
63
|
+
"""
|
|
64
|
+
self.ifreq_dsb.setMaximum(value)
|
|
65
|
+
|
|
66
|
+
def _system_clock_frequency_config_gbox(self):
|
|
67
|
+
"""Create groupbox for system clock configuration.
|
|
68
|
+
:returns: the system frequency configuration groupbox (QGroupBox)
|
|
69
|
+
"""
|
|
70
|
+
self.pll_doubler_ckbox = QCheckBox("Enable input frequency doubler")
|
|
71
|
+
self.pll_factor_sbox = QSpinBox()
|
|
72
|
+
self.pll_factor_sbox.setRange(4, 66)
|
|
73
|
+
self.pll_factor_sbox.setSingleStep(2)
|
|
74
|
+
self.vco_range_cbox = QComboBox()
|
|
75
|
+
self.cp_current_cbox = QComboBox()
|
|
76
|
+
group_box = QGroupBox("Enable System Clock PLL")
|
|
77
|
+
group_box.setCheckable(True)
|
|
78
|
+
group_box.setChecked(False)
|
|
79
|
+
advanced_layout = QVBoxLayout()
|
|
80
|
+
advanced_layout.addWidget(QLabel("Input PLL doubler"))
|
|
81
|
+
advanced_layout.addWidget(self.pll_doubler_ckbox)
|
|
82
|
+
advanced_layout.addWidget(QLabel("PLL multiplier"))
|
|
83
|
+
advanced_layout.addWidget(self.pll_factor_sbox)
|
|
84
|
+
advanced_layout.addWidget(QLabel("VCO range"))
|
|
85
|
+
advanced_layout.addWidget(self.vco_range_cbox)
|
|
86
|
+
advanced_layout.addWidget(QLabel("Charge pump current"))
|
|
87
|
+
advanced_layout.addWidget(self.cp_current_cbox)
|
|
88
|
+
group_box.setLayout(advanced_layout)
|
|
89
|
+
return group_box
|
|
90
|
+
|
|
91
|
+
def _init_ui(self):
|
|
92
|
+
"""Init UI.
|
|
93
|
+
:returns: None
|
|
94
|
+
"""
|
|
95
|
+
for k, val in cst.AD9912_VCO_RANGE.items():
|
|
96
|
+
self.vco_range_cbox.insertItem(k, val)
|
|
97
|
+
for k, val in cst.AD9912_CP_CURRENT.items():
|
|
98
|
+
self.cp_current_cbox.insertItem(k, val)
|
|
99
|
+
self.update_sys_clock_label()
|
|
100
|
+
|
|
101
|
+
@pyqtSlot()
|
|
102
|
+
def update_sys_clock_label(self):
|
|
103
|
+
"""Procedure when system clock is modified.
|
|
104
|
+
:returns: None
|
|
105
|
+
"""
|
|
106
|
+
if self.advanced_config_gbox.isChecked() is True:
|
|
107
|
+
if self.pll_doubler_ckbox.isChecked() is True:
|
|
108
|
+
doubler = 2.0
|
|
109
|
+
else:
|
|
110
|
+
doubler = 1.0
|
|
111
|
+
ifreq = self.ifreq_dsb.value()
|
|
112
|
+
factor = self.pll_factor_sbox.value()
|
|
113
|
+
sfreq_value = ifreq * doubler * factor
|
|
114
|
+
else:
|
|
115
|
+
sfreq_value = self.ifreq_dsb.value()
|
|
116
|
+
self.sys_clock_changed.emit(sfreq_value)
|
|
117
|
+
self.sfreq_led.setText(str(sfreq_value))
|
|
118
|
+
|
|
119
|
+
# @pyqtSlot()
|
|
120
|
+
# def update_sys_clock_label(self, value):
|
|
121
|
+
# """Procedure when system clock is modified.
|
|
122
|
+
# :returns: None
|
|
123
|
+
# """
|
|
124
|
+
# self.sfreq_led.setText(str(value))
|
|
125
|
+
# self.sfreq_led.setText(str(self._dev.get_sysfreq()))
|
|
126
|
+
|
|
127
|
+
|
|
128
|
+
#==============================================================================
|
|
129
|
+
class OutControllerUi(QWidget):
|
|
130
|
+
"""Controller UI form used to control DDS device.
|
|
131
|
+
"""
|
|
132
|
+
|
|
133
|
+
def __init__(self, device, ofmax=400000000.0, amax=1023):
|
|
134
|
+
"""The constructor.
|
|
135
|
+
"""
|
|
136
|
+
super().__init__()
|
|
137
|
+
self.auto_update_ckbox = QCheckBox("Automatic update")
|
|
138
|
+
ofreq_lab = QLabel("Output frequency (Hz)")
|
|
139
|
+
self.ofreq_tuning = QDoubleSpinBox()
|
|
140
|
+
self.ofreq_tuning.setDecimals(10)
|
|
141
|
+
self.ofreq_tuning.setRange(0.0, ofmax)
|
|
142
|
+
ofreq_layout = QVBoxLayout()
|
|
143
|
+
ofreq_layout.addWidget(ofreq_lab)
|
|
144
|
+
ofreq_layout.addWidget(self.ofreq_tuning)
|
|
145
|
+
ofreq_layout.addStretch(1)
|
|
146
|
+
phy_lab = QLabel("Output phase (deg.)")
|
|
147
|
+
self.phy_tuning = QDoubleSpinBox()
|
|
148
|
+
self.phy_tuning.setRange(0.0, 359.9)
|
|
149
|
+
self.phy_tuning.setDecimals(2)
|
|
150
|
+
self.phy_tuning.setSingleStep(1.0)
|
|
151
|
+
phy_layout = QVBoxLayout()
|
|
152
|
+
phy_layout.addWidget(phy_lab)
|
|
153
|
+
phy_layout.addWidget(self.phy_tuning)
|
|
154
|
+
phy_layout.addStretch(1)
|
|
155
|
+
amp_lab = QLabel("Output amplitude (a.u.)")
|
|
156
|
+
self.amp_tuning = QDoubleSpinBox()
|
|
157
|
+
self.amp_tuning.setRange(0, amax)
|
|
158
|
+
self.amp_tuning.setDecimals(0)
|
|
159
|
+
self.amp_tuning.setSingleStep(1)
|
|
160
|
+
amp_layout = QVBoxLayout()
|
|
161
|
+
amp_layout.addWidget(amp_lab)
|
|
162
|
+
amp_layout.addWidget(self.amp_tuning)
|
|
163
|
+
amp_layout.addStretch(1)
|
|
164
|
+
ctrl_layout = QHBoxLayout()
|
|
165
|
+
ctrl_layout.addLayout(ofreq_layout)
|
|
166
|
+
ctrl_layout.addLayout(phy_layout)
|
|
167
|
+
ctrl_layout.addLayout(amp_layout)
|
|
168
|
+
self.out_hstl_ckbox = QCheckBox("Enable HSTL output")
|
|
169
|
+
self.out_cmos_ckbox = QCheckBox("Enable CMOS output")
|
|
170
|
+
self.hstl_doubler_ckbox = QCheckBox("Enable HSTL frequency doubler")
|
|
171
|
+
##self.out_cmos_div_sbox = QSpinBox()
|
|
172
|
+
##self.out_cmos_div_sbox.setRange(1, 65535)
|
|
173
|
+
out_layout = QGridLayout()
|
|
174
|
+
out_layout.addWidget(self.out_hstl_ckbox, 0, 0)
|
|
175
|
+
out_layout.addWidget(self.out_cmos_ckbox, 1, 0)
|
|
176
|
+
out_layout.addWidget(self.hstl_doubler_ckbox, 2, 0)
|
|
177
|
+
##out_layout.addWidget(self.out_cmos_div_sbox, 3, 0)
|
|
178
|
+
##out_layout.addWidget(QLabel("CMOS output divider factor"), 3, 1)
|
|
179
|
+
self.apply_btn = QPushButton("&Apply")
|
|
180
|
+
btn_layout = QHBoxLayout()
|
|
181
|
+
btn_layout.addWidget(self.apply_btn)
|
|
182
|
+
self._layout = QVBoxLayout()
|
|
183
|
+
self._layout.addWidget(self.auto_update_ckbox)
|
|
184
|
+
self._layout.addLayout(ctrl_layout)
|
|
185
|
+
self._layout.addLayout(out_layout)
|
|
186
|
+
self._layout.addStretch(1)
|
|
187
|
+
self._layout.addLayout(btn_layout)
|
|
188
|
+
self.setLayout(self._layout)
|
|
189
|
+
# Set state of CMOS divider with respect to CMOS output state
|
|
190
|
+
##self.out_cmos_ckbox.toggled.connect(self.out_cmos_div_sbox.setEnabled)
|
|
191
|
+
##self.out_cmos_div_sbox.setEnabled(False)
|
|
192
|
+
# Set state of HSTL doubler with respect to HSTL output state
|
|
193
|
+
self.out_hstl_ckbox.toggled.connect(self.hstl_doubler_ckbox.setEnabled)
|
|
194
|
+
self.hstl_doubler_ckbox.setEnabled(False)
|
|
195
|
+
# Handle apply button state in respect with auto update checkbox
|
|
196
|
+
self.auto_update_ckbox.stateChanged.connect(self.apply_btn.setDisabled)
|
|
197
|
+
|
|
198
|
+
@pyqtSlot(float)
|
|
199
|
+
def set_ofmax(self, value):
|
|
200
|
+
"""Define maximum frequency handled by widget.
|
|
201
|
+
:param value: New maximal value of parameter (float)
|
|
202
|
+
:returns: None
|
|
203
|
+
"""
|
|
204
|
+
self.ofreq_tuning.setMaximum(value)
|
|
205
|
+
|
|
206
|
+
@pyqtSlot(int)
|
|
207
|
+
def set_amax(self, value):
|
|
208
|
+
"""Define maximum amplitude handled by widget.
|
|
209
|
+
:param value: New maximal value of parameter (float)
|
|
210
|
+
:returns: None
|
|
211
|
+
"""
|
|
212
|
+
self.amp_tuning.setMaximum(value)
|
|
213
|
+
|
|
214
|
+
|
|
215
|
+
|
|
216
|
+
#==============================================================================
|
|
217
|
+
if __name__ == '__main__':
|
|
218
|
+
import sys
|
|
219
|
+
from PyQt5.QtWidgets import QApplication
|
|
220
|
+
import ad9xdds.dds_emul as dds_emul
|
|
221
|
+
|
|
222
|
+
APP = QApplication(sys.argv)
|
|
223
|
+
UI = InControllerUi(dds_emul.Ad9912Emul())
|
|
224
|
+
UI.show()
|
|
225
|
+
sys.exit(APP.exec_())
|