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.
File without changes
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: ddsctrl
3
- Version: 0.5.4
3
+ Version: 0.7.0
4
4
  Summary: ddscontroller allow basic handling of AD9912 and AD9915 DDS development board.
5
5
  Home-page: https://gitlab.com/bendub/ddsctrl
6
6
  Author: Benoit Dubois
@@ -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 usb.util
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=["AD9912", "AD9915"],
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
- parser.error("--dds_model requires --dds_bus and --dds_adress.")
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 == "AD9912":
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 == "AD9912":
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 = float(settings.value('dds_ctrl/ofreq'))
225
- ifreq = float(settings.value('dds_ctrl/ifreq'))
226
- phase = float(settings.value('dds_ctrl/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
- if model == "AD9912":
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 = float(settings.value('dds_ctrl/ofreq'))
263
- ifreq = float(settings.value('dds_ctrl/ifreq'))
264
- phase = float(settings.value('dds_ctrl/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 == "AD9912":
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 == "AD9912":
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(int(str(ui.debug_ui.reg_add_led.text()), 16),
358
- int(str(ui.debug_ui.reg_val_led.text()), 16))
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(float(act_ofreq))
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(float(act_amp))
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(float(act_phy))
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
- if model == "AD9912":
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
- ####ofreq_changed(ui, dds)
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 == "AD9912":
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 list of AD9912 and AD9915 dds development board connected to
543
+ """Return dictionary of AD9912 and AD9915 dds development board connected to
491
544
  computer.
492
- :returns: list of AD9912 and AD9915 dds (list of usb.core.Device)
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="AD9912 detected but firmware not loaded")
499
- # AD9912 dev board
500
- ad9912dev = usb.core.find(idVendor=0x0456, idProduct=0xee09, find_all=True)
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 {'ad9912dev': ad9912dev, 'ad9915dev': ad9915dev}
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, dds_bus=None, dds_address=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
- dds_boards_for_human = \
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['ad9912dev']] + \
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
- dds_model = dds_item.split()[0]
529
- dds_bus = int(dds_item.split()[3])
530
- dds_address = int(dds_item.split()[5])
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 == "AD9912":
534
- dds = sad9912dev.SAd9912Dev()
535
- retval = dds.connect(bus=dds_bus, address=dds_address)
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("ad9912")
542
- elif dds_model == "AD9915":
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("ad9915")
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 == "AD9912":
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_())