ddsctrl 0.5.1__tar.gz → 0.5.4__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/LICENSE.txt +17 -0
- ddsctrl-0.5.4/PKG-INFO +26 -0
- ddsctrl-0.5.4/bin/ddscontroller +614 -0
- ddsctrl-0.5.4/ddsctrl/constants.py +32 -0
- {ddsctrl-0.5.1 → ddsctrl-0.5.4}/ddsctrl/ddsctrlui.py +87 -42
- {ddsctrl-0.5.1 → ddsctrl-0.5.4}/ddsctrl/version.local.py +0 -0
- ddsctrl-0.5.4/ddsctrl/version.py +8 -0
- ddsctrl-0.5.4/ddsctrl.egg-info/PKG-INFO +26 -0
- {ddsctrl-0.5.1 → ddsctrl-0.5.4}/ddsctrl.egg-info/SOURCES.txt +2 -0
- ddsctrl-0.5.4/ddsctrl.egg-info/requires.txt +3 -0
- {ddsctrl-0.5.1 → ddsctrl-0.5.4}/setup.py +7 -5
- ddsctrl-0.5.1/PKG-INFO +0 -17
- ddsctrl-0.5.1/bin/ddscontroller +0 -491
- ddsctrl-0.5.1/ddsctrl/constants.py +0 -28
- ddsctrl-0.5.1/ddsctrl/version.py +0 -5
- ddsctrl-0.5.1/ddsctrl.egg-info/PKG-INFO +0 -17
- {ddsctrl-0.5.1 → ddsctrl-0.5.4}/MANIFEST.in +0 -0
- {ddsctrl-0.5.1 → ddsctrl-0.5.4}/README +0 -0
- {ddsctrl-0.5.1 → ddsctrl-0.5.4}/ddsctrl/__init__.py +0 -0
- {ddsctrl-0.5.1 → ddsctrl-0.5.4}/ddsctrl.egg-info/dependency_links.txt +0 -0
- {ddsctrl-0.5.1 → ddsctrl-0.5.4}/ddsctrl.egg-info/top_level.txt +0 -0
- {ddsctrl-0.5.1 → ddsctrl-0.5.4}/setup.cfg +0 -0
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
Benoit Dubois
|
|
2
|
+
Copyright (C) 2019 FEMTO Engineering
|
|
3
|
+
benoit.dubois@femto-st.fr
|
|
4
|
+
|
|
5
|
+
This program is free software; you can redistribute it and/or
|
|
6
|
+
modify it under the terms of the GNU Lesser General Public
|
|
7
|
+
License as published by the Free Software Foundation; either
|
|
8
|
+
version 3 of the License, or (at your option) any later version.
|
|
9
|
+
|
|
10
|
+
This program is distributed in the hope that it will be useful,
|
|
11
|
+
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
12
|
+
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
13
|
+
Lesser General Public License for more details.
|
|
14
|
+
|
|
15
|
+
You should have received a copy of the GNU Lesser General Public License
|
|
16
|
+
along with this program; if not, write to the Free Software Foundation,
|
|
17
|
+
Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
ddsctrl-0.5.4/PKG-INFO
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: ddsctrl
|
|
3
|
+
Version: 0.5.4
|
|
4
|
+
Summary: ddscontroller allow basic handling of AD9912 and AD9915 DDS development board.
|
|
5
|
+
Home-page: https://gitlab.com/bendub/ddsctrl
|
|
6
|
+
Author: Benoit Dubois
|
|
7
|
+
Author-email: benoit.dubois@femto-engineering.fr
|
|
8
|
+
License: LGPL-3.0-or-later
|
|
9
|
+
Classifier: Development Status :: 4 - Beta
|
|
10
|
+
Classifier: Intended Audience :: Science/Research
|
|
11
|
+
Classifier: Natural Language :: English
|
|
12
|
+
Classifier: Operating System :: POSIX :: Linux
|
|
13
|
+
Classifier: Programming Language :: Python
|
|
14
|
+
Classifier: Topic :: Scientific/Engineering
|
|
15
|
+
License-File: LICENSE.txt
|
|
16
|
+
Requires-Dist: PyQt5
|
|
17
|
+
Requires-Dist: ad9xdds
|
|
18
|
+
Requires-Dist: signalslot
|
|
19
|
+
Dynamic: author
|
|
20
|
+
Dynamic: author-email
|
|
21
|
+
Dynamic: classifier
|
|
22
|
+
Dynamic: home-page
|
|
23
|
+
Dynamic: license
|
|
24
|
+
Dynamic: license-file
|
|
25
|
+
Dynamic: requires-dist
|
|
26
|
+
Dynamic: summary
|
|
@@ -0,0 +1,614 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
# -*- coding: utf-8 -*-
|
|
3
|
+
|
|
4
|
+
"""package ddsctrl
|
|
5
|
+
author Benoit Dubois
|
|
6
|
+
copyright FEMTO ENGINEERING, 2016, 2023
|
|
7
|
+
license GPL v3.0+
|
|
8
|
+
brief Main script of the DDS controller project.
|
|
9
|
+
"""
|
|
10
|
+
|
|
11
|
+
# Ctrl-c closes the application
|
|
12
|
+
import signal
|
|
13
|
+
signal.signal(signal.SIGINT, signal.SIG_DFL)
|
|
14
|
+
|
|
15
|
+
import sys
|
|
16
|
+
import argparse
|
|
17
|
+
import os.path as path
|
|
18
|
+
import logging
|
|
19
|
+
import itertools
|
|
20
|
+
import usb.core
|
|
21
|
+
import usb.util
|
|
22
|
+
from PyQt5.QtCore import QSettings
|
|
23
|
+
from PyQt5.QtWidgets import QApplication, QMainWindow, QMessageBox
|
|
24
|
+
|
|
25
|
+
import ad9xdds.sad9912dev as sad9912dev
|
|
26
|
+
import ad9xdds.ad9912dev as ad9912dev
|
|
27
|
+
import ad9xdds.sad9915dev_umr232hm as sad9915dev
|
|
28
|
+
import ad9xdds.ad9915dev_umr232hm as ad9915dev
|
|
29
|
+
|
|
30
|
+
from ddsctrl.ddsctrlui import DdsCtrlUi, DdsBoardSelection
|
|
31
|
+
from ddsctrl.constants import ORGANIZATION, APP_NAME, DEFAULT_AUTO_UPDATE, \
|
|
32
|
+
DEFAULT_AD9912_OFREQ, DEFAULT_AD9912_AMP, DEFAULT_AD9912_PHASE, \
|
|
33
|
+
DEFAULT_AD9912_PLL_EN, DEFAULT_AD9912_PLL_FACTOR, \
|
|
34
|
+
DEFAULT_AD9912_CP_CURRENT, DEFAULT_AD9912_VCO_RANGE, \
|
|
35
|
+
DEFAULT_AD9912_PLL_DOUBLER, DEFAULT_AD9912_HSTL_EN, \
|
|
36
|
+
DEFAULT_AD9912_CMOS_EN, DEFAULT_AD9912_IFREQ, \
|
|
37
|
+
DEFAULT_AD9912_HSTL_DOUBLER
|
|
38
|
+
|
|
39
|
+
CONSOLE_LOG_LEVEL = logging.DEBUG
|
|
40
|
+
FILE_LOG_LEVEL = logging.DEBUG
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
# =============================================================================
|
|
44
|
+
def configure_logging():
|
|
45
|
+
"""Configures logs.
|
|
46
|
+
"""
|
|
47
|
+
home = path.expanduser("~")
|
|
48
|
+
log_file = "." + APP_NAME + ".log"
|
|
49
|
+
abs_log_file = path.join(home, log_file)
|
|
50
|
+
date_fmt = "%d/%m/%Y %H:%M:%S"
|
|
51
|
+
log_format = "%(asctime)s %(levelname) -8s %(filename)s " + \
|
|
52
|
+
" %(funcName)s (%(lineno)d): %(message)s"
|
|
53
|
+
logging.basicConfig(level=FILE_LOG_LEVEL,
|
|
54
|
+
datefmt=date_fmt,
|
|
55
|
+
format=log_format,
|
|
56
|
+
filename=abs_log_file,
|
|
57
|
+
filemode='w')
|
|
58
|
+
console = logging.StreamHandler()
|
|
59
|
+
# define a Handler which writes messages to the sys.stderr
|
|
60
|
+
console.setLevel(CONSOLE_LOG_LEVEL)
|
|
61
|
+
# set a format which is simpler for console use
|
|
62
|
+
console_format = '%(levelname) -8s %(filename)s (%(lineno)d): %(message)s'
|
|
63
|
+
formatter = logging.Formatter(console_format)
|
|
64
|
+
# tell the handler to use this format
|
|
65
|
+
console.setFormatter(formatter)
|
|
66
|
+
# add the handler to the root logger
|
|
67
|
+
logging.getLogger('').addHandler(console)
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
# =============================================================================
|
|
71
|
+
def parse_cmd_line():
|
|
72
|
+
"""Parses command line: allow user to choose a different ini file.
|
|
73
|
+
:returns: parser object (object)
|
|
74
|
+
"""
|
|
75
|
+
parser = argparse.ArgumentParser(description='DdsController')
|
|
76
|
+
parser.add_argument('-ini', '--ini_file', dest='ini_file',
|
|
77
|
+
type=str, default='', help='-ini=<str>')
|
|
78
|
+
parser.add_argument('-wt', '--window_title', dest='wtitle',
|
|
79
|
+
type=str, default="DDS controller")
|
|
80
|
+
parser.add_argument('-d', '--dds_model', choices=["AD9912", "AD9915"],
|
|
81
|
+
dest='model')
|
|
82
|
+
parser.add_argument('-b', '--dds_bus', type=int, dest='bus')
|
|
83
|
+
parser.add_argument('-a', '--dds_address', type=int, dest='address')
|
|
84
|
+
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.")
|
|
87
|
+
if not args.model and (args.bus is not None or args.address is not None):
|
|
88
|
+
parser.error("--dds_bus and --dds_adress requires --dds_model.")
|
|
89
|
+
return args
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
# =============================================================================
|
|
93
|
+
def reset_ini(settings):
|
|
94
|
+
"""Resets the "settings" file with default values.
|
|
95
|
+
We use AD9912 default values because these values are "conservative" values
|
|
96
|
+
which could be used with AD9915 device (ofreq, ifreq, amp).
|
|
97
|
+
Note: due to conversion problem from QVariant (type returned by QSettings)
|
|
98
|
+
to boolean type in Python, boolean value are stored in integer:
|
|
99
|
+
0 <-> False, 1 <-> True.
|
|
100
|
+
:param ui: ddsctrlui class instance (object)
|
|
101
|
+
:param settings: a valid QSettings object (QSettings)
|
|
102
|
+
:returns: None
|
|
103
|
+
"""
|
|
104
|
+
settings.clear()
|
|
105
|
+
settings.setValue('dds_ctrl/auto_update', int(DEFAULT_AUTO_UPDATE))
|
|
106
|
+
settings.setValue('dds_ctrl/ifreq', DEFAULT_AD9912_IFREQ)
|
|
107
|
+
settings.setValue('dds_ctrl/ofreq', DEFAULT_AD9912_OFREQ)
|
|
108
|
+
settings.setValue('dds_ctrl/amp', DEFAULT_AD9912_AMP)
|
|
109
|
+
settings.setValue('dds_ctrl/phase', DEFAULT_AD9912_PHASE)
|
|
110
|
+
settings.setValue('dds_ctrl/hstl_en', int(DEFAULT_AD9912_HSTL_EN))
|
|
111
|
+
settings.setValue('dds_ctrl/cmos_en', int(DEFAULT_AD9912_CMOS_EN))
|
|
112
|
+
settings.setValue('dds_ctrl/hstl_doubler', int(DEFAULT_AD9912_HSTL_DOUBLER))
|
|
113
|
+
settings.setValue('dds_ctrl/pll_en', int(DEFAULT_AD9912_PLL_EN))
|
|
114
|
+
settings.setValue('dds_ctrl/pll_doubler', int(DEFAULT_AD9912_PLL_DOUBLER))
|
|
115
|
+
settings.setValue('dds_ctrl/pll_factor', DEFAULT_AD9912_PLL_FACTOR)
|
|
116
|
+
settings.setValue('dds_ctrl/vco_range', DEFAULT_AD9912_VCO_RANGE)
|
|
117
|
+
settings.setValue('dds_ctrl/cp_current', DEFAULT_AD9912_CP_CURRENT)
|
|
118
|
+
|
|
119
|
+
|
|
120
|
+
# =============================================================================
|
|
121
|
+
def save_all_settings(ui):
|
|
122
|
+
"""Save current setting to ini file.
|
|
123
|
+
:returns: None
|
|
124
|
+
"""
|
|
125
|
+
settings = QSettings()
|
|
126
|
+
settings.setValue('dds_ctrl/auto_update',
|
|
127
|
+
ui.ocontroller_ui.auto_update_ckbox.isChecked())
|
|
128
|
+
settings.setValue('dds_ctrl/ofreq', ui.ocontroller_ui.ofreq_tuning.value())
|
|
129
|
+
settings.setValue('dds_ctrl/phase', ui.ocontroller_ui.phy_tuning.value())
|
|
130
|
+
settings.setValue('dds_ctrl/amp', ui.ocontroller_ui.amp_tuning.value())
|
|
131
|
+
settings.setValue('dds_ctrl/ifreq', ui.icontroller_ui.ifreq_dsb.value())
|
|
132
|
+
if model == "AD9912":
|
|
133
|
+
settings.setValue('dds_ctrl/hstl_en',
|
|
134
|
+
int(ui.ocontroller_ui.out_hstl_ckbox.isChecked()))
|
|
135
|
+
settings.setValue('dds_ctrl/cmos_en',
|
|
136
|
+
int(ui.ocontroller_ui.out_cmos_ckbox.isChecked()))
|
|
137
|
+
settings.setValue(
|
|
138
|
+
'dds_ctrl/hstl_doubler',
|
|
139
|
+
int(ui.ocontroller_ui.hstl_doubler_ckbox.isChecked()))
|
|
140
|
+
settings.setValue(
|
|
141
|
+
'dds_ctrl/pll_en',
|
|
142
|
+
int(ui.icontroller_ui.advanced_config_gbox.isChecked()))
|
|
143
|
+
settings.setValue(
|
|
144
|
+
'dds_ctrl/pll_doubler',
|
|
145
|
+
int(ui.icontroller_ui.pll_doubler_ckbox.checkState()))
|
|
146
|
+
settings.setValue('dds_ctrl/pll_factor',
|
|
147
|
+
ui.icontroller_ui.pll_factor_sbox.value())
|
|
148
|
+
settings.setValue('dds_ctrl/vco_range',
|
|
149
|
+
ui.icontroller_ui.vco_range_cbox.currentIndex())
|
|
150
|
+
settings.setValue('dds_ctrl/cp_current',
|
|
151
|
+
ui.icontroller_ui.cp_current_cbox.currentIndex())
|
|
152
|
+
|
|
153
|
+
|
|
154
|
+
# =============================================================================
|
|
155
|
+
def check_ini_file(user_ini_file=None):
|
|
156
|
+
"""Check validity of ini file. Ini file can be given by user or can be the
|
|
157
|
+
system default ini file. If the system ini file is broken or missing,
|
|
158
|
+
create an ini file with default values.
|
|
159
|
+
:param user_ini_file: Ini file given by user (str)
|
|
160
|
+
:returns: None
|
|
161
|
+
"""
|
|
162
|
+
if user_ini_file is not None:
|
|
163
|
+
settings = QSettings(user_ini_file)
|
|
164
|
+
else:
|
|
165
|
+
settings = QSettings()
|
|
166
|
+
retval = list()
|
|
167
|
+
try:
|
|
168
|
+
retval.append(settings.value('dds_ctrl/auto_update'))
|
|
169
|
+
retval.append(settings.value('dds_ctrl/ofreq'))
|
|
170
|
+
retval.append(settings.value('dds_ctrl/phase'))
|
|
171
|
+
retval.append(settings.value('dds_ctrl/amp'))
|
|
172
|
+
retval.append(settings.value('dds_ctrl/hstl_en'))
|
|
173
|
+
retval.append(settings.value('dds_ctrl/cmos_en'))
|
|
174
|
+
retval.append(settings.value('dds_ctrl/hstl_doubler'))
|
|
175
|
+
retval.append(settings.value('dds_ctrl/ifreq'))
|
|
176
|
+
retval.append(settings.value('dds_ctrl/pll_en'))
|
|
177
|
+
retval.append(settings.value('dds_ctrl/pll_doubler'))
|
|
178
|
+
retval.append(settings.value('dds_ctrl/pll_factor'))
|
|
179
|
+
retval.append(settings.value('dds_ctrl/vco_range'))
|
|
180
|
+
retval.append(settings.value('dds_ctrl/cp_current'))
|
|
181
|
+
if None in retval:
|
|
182
|
+
# Test needded because QSettings does not catch exception when
|
|
183
|
+
# a parameter is missing in ini file, instead QSettings return None.
|
|
184
|
+
if user_ini_file is None:
|
|
185
|
+
logging.warning("Missing or broken ini file")
|
|
186
|
+
reset_ini(settings)
|
|
187
|
+
logging.info("Create ini file with default values")
|
|
188
|
+
else:
|
|
189
|
+
logging.critical("Ini file given by user is not valid")
|
|
190
|
+
QMessageBox().critical(None, "Ini file error", \
|
|
191
|
+
"User ini file not valid")
|
|
192
|
+
sys.exit()
|
|
193
|
+
except Exception as ex:
|
|
194
|
+
if user_ini_file is None:
|
|
195
|
+
logging.warning("Missing or broken ini file")
|
|
196
|
+
reset_ini(settings)
|
|
197
|
+
logging.info("Create ini file with default values")
|
|
198
|
+
else:
|
|
199
|
+
logging.critical("User ini file not valid %r", ex)
|
|
200
|
+
QMessageBox().critical(None, "Ini file error",
|
|
201
|
+
"User ini file not valid: {}".format(ex))
|
|
202
|
+
sys.exit()
|
|
203
|
+
|
|
204
|
+
|
|
205
|
+
# =============================================================================
|
|
206
|
+
def init_ui(ui, model):
|
|
207
|
+
"""Init UI.
|
|
208
|
+
:param ui: ddsctrlui class instance (object)
|
|
209
|
+
:param model: dds model i.e. "AD9912" or "AD9915" (str)
|
|
210
|
+
:returns: None
|
|
211
|
+
"""
|
|
212
|
+
if model == "AD9912":
|
|
213
|
+
amax = ad9912dev.AMAX
|
|
214
|
+
ifmax = ad9912dev.IFMAX
|
|
215
|
+
else:
|
|
216
|
+
amax = ad9915dev.AMAX
|
|
217
|
+
ifmax = ad9915dev.IFMAX
|
|
218
|
+
ui.icontroller_ui.set_ifmax(ifmax)
|
|
219
|
+
ui.ocontroller_ui.set_ofmax(ifmax*0.4)
|
|
220
|
+
ui.ocontroller_ui.set_amax(amax)
|
|
221
|
+
# Init UI with respect to (ini file) actual dds configuration
|
|
222
|
+
settings = QSettings()
|
|
223
|
+
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'))
|
|
227
|
+
amp = int(settings.value('dds_ctrl/amp'))
|
|
228
|
+
##ui.ocontroller_ui.auto_update_ckbox.setChecked(False)
|
|
229
|
+
ui.ocontroller_ui.auto_update_ckbox.setChecked(auto_update)
|
|
230
|
+
ui.ocontroller_ui.ofreq_tuning.setValue(ofreq)
|
|
231
|
+
ui.ocontroller_ui.phy_tuning.setValue(phase)
|
|
232
|
+
ui.ocontroller_ui.amp_tuning.setValue(amp)
|
|
233
|
+
ui.icontroller_ui.ifreq_dsb.setValue(ifreq)
|
|
234
|
+
if model == "AD9912":
|
|
235
|
+
hstl_en = bool(int(settings.value('dds_ctrl/hstl_en')))
|
|
236
|
+
cmos_en = bool(int(settings.value('dds_ctrl/cmos_en')))
|
|
237
|
+
hstl_doubler = bool(int(settings.value('dds_ctrl/hstl_doubler')))
|
|
238
|
+
pll_en = bool(int(settings.value('dds_ctrl/pll_en')))
|
|
239
|
+
pll_doubler = bool(int(settings.value('dds_ctrl/pll_doubler')))
|
|
240
|
+
pll_factor = int(settings.value('dds_ctrl/pll_factor'))
|
|
241
|
+
vco_range = int(settings.value('dds_ctrl/vco_range'))
|
|
242
|
+
cp_current = int(settings.value('dds_ctrl/cp_current'))
|
|
243
|
+
ui.ocontroller_ui.out_hstl_ckbox.setChecked(hstl_en)
|
|
244
|
+
ui.ocontroller_ui.hstl_doubler_ckbox.setChecked(hstl_doubler)
|
|
245
|
+
ui.ocontroller_ui.out_cmos_ckbox.setChecked(cmos_en)
|
|
246
|
+
ui.icontroller_ui.advanced_config_gbox.setChecked(pll_en)
|
|
247
|
+
ui.icontroller_ui.pll_doubler_ckbox.setChecked(pll_doubler)
|
|
248
|
+
ui.icontroller_ui.pll_factor_sbox.setValue(pll_factor)
|
|
249
|
+
ui.icontroller_ui.vco_range_cbox.setCurrentIndex(vco_range)
|
|
250
|
+
ui.icontroller_ui.cp_current_cbox.setCurrentIndex(cp_current)
|
|
251
|
+
ui.icontroller_ui.update_sys_clock_label()
|
|
252
|
+
|
|
253
|
+
|
|
254
|
+
# =============================================================================
|
|
255
|
+
def init_dev(dds, model):
|
|
256
|
+
"""Init device.
|
|
257
|
+
:param dds: dds class instance (object)
|
|
258
|
+
:param model: dds model i.e. "AD9912" or "AD9915" (str)
|
|
259
|
+
:returns: None
|
|
260
|
+
"""
|
|
261
|
+
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'))
|
|
265
|
+
amp = int(settings.value('dds_ctrl/amp'))
|
|
266
|
+
dds.set_ofreq(ofreq)
|
|
267
|
+
dds.set_ifreq(ifreq)
|
|
268
|
+
dds.set_phy(phase)
|
|
269
|
+
dds.set_amp(amp)
|
|
270
|
+
if model == "AD9912":
|
|
271
|
+
hstl_en = bool(int(settings.value('dds_ctrl/hstl_en')))
|
|
272
|
+
cmos_en = bool(int(settings.value('dds_ctrl/cmos_en')))
|
|
273
|
+
hstl_doubler = bool(int(settings.value('dds_ctrl/hstl_doubler')))
|
|
274
|
+
pll_en = bool(int(settings.value('dds_ctrl/pll_en')))
|
|
275
|
+
pll_doubler = bool(int(settings.value('dds_ctrl/pll_doubler')))
|
|
276
|
+
pll_factor = int(settings.value('dds_ctrl/pll_factor'))
|
|
277
|
+
vco_range = int(settings.value('dds_ctrl/vco_range'))
|
|
278
|
+
cp_current = int(settings.value('dds_ctrl/cp_current'))
|
|
279
|
+
dds.set_hstl_output_state(hstl_en)
|
|
280
|
+
dds.set_cmos_output_state(cmos_en)
|
|
281
|
+
dds.set_hstl_doubler_state(hstl_doubler)
|
|
282
|
+
dds.set_pll_state(pll_en)
|
|
283
|
+
dds.set_pll_doubler_state(pll_doubler)
|
|
284
|
+
dds.set_pll_multiplier_factor(pll_factor)
|
|
285
|
+
dds.set_vco_range(vco_range)
|
|
286
|
+
dds.set_cp_current(cp_current)
|
|
287
|
+
|
|
288
|
+
|
|
289
|
+
# =============================================================================
|
|
290
|
+
def handle_ocontroller_ui_behavior(ui, dds, model):
|
|
291
|
+
"""Handle behavior of output widget tab with respect to automatic update
|
|
292
|
+
checkbox: if auto is true, modification are directly take into account,
|
|
293
|
+
else modification are take into account after apply button is pressed.
|
|
294
|
+
:param ui: ddsctrlui class instance (object)
|
|
295
|
+
:param dds: dds class instance (object)
|
|
296
|
+
:param model: dds model i.e. "AD9912" or "AD9915" (str)
|
|
297
|
+
:returns: None
|
|
298
|
+
"""
|
|
299
|
+
if ui.ocontroller_ui.auto_update_ckbox.isChecked() is True:
|
|
300
|
+
ui.ocontroller_ui.ofreq_tuning.valueChanged[str].connect(
|
|
301
|
+
lambda: ofreq_changed(ui, dds))
|
|
302
|
+
ui.ocontroller_ui.amp_tuning.valueChanged[str].connect(
|
|
303
|
+
lambda: amplitude_changed(ui, dds))
|
|
304
|
+
ui.ocontroller_ui.phy_tuning.valueChanged[str].connect(
|
|
305
|
+
lambda: phase_changed(ui, dds))
|
|
306
|
+
if model == "AD9912":
|
|
307
|
+
ui.ocontroller_ui.out_hstl_ckbox.stateChanged.connect(
|
|
308
|
+
lambda: hstl_output_changed(ui, dds))
|
|
309
|
+
ui.ocontroller_ui.hstl_doubler_ckbox.stateChanged.connect(
|
|
310
|
+
lambda: hstl_doubler_changed(ui, dds))
|
|
311
|
+
ui.ocontroller_ui.out_cmos_ckbox.stateChanged.connect(
|
|
312
|
+
lambda: cmos_output_changed(ui, dds))
|
|
313
|
+
try:
|
|
314
|
+
ui.ocontroller_ui.apply_btn.clicked.disconnect()
|
|
315
|
+
except Exception as ex:
|
|
316
|
+
logging.warning(ex)
|
|
317
|
+
else:
|
|
318
|
+
ui.ocontroller_ui.apply_btn.clicked.connect(
|
|
319
|
+
lambda: apply_btn_octrl_clicked(ui, dds, model))
|
|
320
|
+
try:
|
|
321
|
+
ui.ocontroller_ui.ofreq_tuning.valueChanged[str].disconnect()
|
|
322
|
+
ui.ocontroller_ui.amp_tuning.valueChanged[str].disconnect()
|
|
323
|
+
ui.ocontroller_ui.phy_tuning.valueChanged[str].disconnect()
|
|
324
|
+
if model == "AD9912":
|
|
325
|
+
ui.ocontroller_ui.out_hstl_ckbox.stateChanged.disconnect()
|
|
326
|
+
ui.ocontroller_ui.hstl_doubler_ckbox.stateChanged.disconnect()
|
|
327
|
+
ui.ocontroller_ui.out_cmos_ckbox.stateChanged.disconnect()
|
|
328
|
+
except Exception as ex:
|
|
329
|
+
logging.warning(ex)
|
|
330
|
+
|
|
331
|
+
|
|
332
|
+
# =============================================================================
|
|
333
|
+
def get_reg_btn_clicked(ui, dds):
|
|
334
|
+
"""Get register value.
|
|
335
|
+
:param ui: ddsctrlui class instance (object)
|
|
336
|
+
:param dds: dds class instance (object)
|
|
337
|
+
:returns: None
|
|
338
|
+
"""
|
|
339
|
+
try:
|
|
340
|
+
reg_val = dds.get_reg(
|
|
341
|
+
int(str(ui.debug_ui.reg_add_led.text()), 16))
|
|
342
|
+
except ValueError as ex:
|
|
343
|
+
pass # Error handled in dds.get_reg
|
|
344
|
+
except Exception as ex:
|
|
345
|
+
logging.warning("Problem when getting register value: %s", str(ex))
|
|
346
|
+
else:
|
|
347
|
+
ui.debug_ui.reg_val_led.setText(format(reg_val, '#02x'))
|
|
348
|
+
|
|
349
|
+
|
|
350
|
+
# =============================================================================
|
|
351
|
+
def set_reg_btn_clicked(ui, dds):
|
|
352
|
+
"""Set register value.
|
|
353
|
+
:param ui: ddsctrlui class instance (object)
|
|
354
|
+
:param dds: dds class instance (object)
|
|
355
|
+
:returns: None
|
|
356
|
+
"""
|
|
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))
|
|
359
|
+
|
|
360
|
+
|
|
361
|
+
# =============================================================================
|
|
362
|
+
def ofreq_changed(ui, dds, **kwargs):
|
|
363
|
+
"""Update output frequency
|
|
364
|
+
:param ui: ddsctrlui class instance (object)
|
|
365
|
+
:param dds: dds class instance (object)
|
|
366
|
+
:returns: None
|
|
367
|
+
"""
|
|
368
|
+
act_ofreq = dds.set_ofreq(ui.ocontroller_ui.ofreq_tuning.value())
|
|
369
|
+
ui.ocontroller_ui.ofreq_tuning.setValue(float(act_ofreq))
|
|
370
|
+
QSettings().setValue('dds_ctrl/ofreq', act_ofreq)
|
|
371
|
+
|
|
372
|
+
|
|
373
|
+
# =============================================================================
|
|
374
|
+
def amplitude_changed(ui, dds):
|
|
375
|
+
"""Update output amplitude
|
|
376
|
+
:param ui: ddsctrlui class instance (object)
|
|
377
|
+
:param dds: dds class instance (object)
|
|
378
|
+
:returns: None
|
|
379
|
+
"""
|
|
380
|
+
act_amp = dds.set_amp(int(ui.ocontroller_ui.amp_tuning.value()))
|
|
381
|
+
ui.ocontroller_ui.amp_tuning.setValue(float(act_amp))
|
|
382
|
+
QSettings().setValue('dds_ctrl/amp', act_amp)
|
|
383
|
+
|
|
384
|
+
|
|
385
|
+
# =============================================================================
|
|
386
|
+
def phase_changed(ui, dds):
|
|
387
|
+
"""Update output phase
|
|
388
|
+
:param ui: ddsctrlui class instance (object)
|
|
389
|
+
:param dds: dds class instance (object)
|
|
390
|
+
:returns: None
|
|
391
|
+
"""
|
|
392
|
+
act_phy = dds.set_phy(ui.ocontroller_ui.phy_tuning.value())
|
|
393
|
+
ui.ocontroller_ui.phy_tuning.setValue(float(act_phy))
|
|
394
|
+
QSettings().setValue('dds_ctrl/phase', act_phy)
|
|
395
|
+
|
|
396
|
+
|
|
397
|
+
# =============================================================================
|
|
398
|
+
def hstl_output_changed(ui, dds):
|
|
399
|
+
"""Update HSTL outputs state
|
|
400
|
+
:param ui: ddsctrlui class instance (object)
|
|
401
|
+
:param dds: dds class instance (object)
|
|
402
|
+
:returns: None
|
|
403
|
+
"""
|
|
404
|
+
dds.set_hstl_output_state(ui.ocontroller_ui.out_hstl_ckbox.isChecked())
|
|
405
|
+
QSettings().setValue('dds_ctrl/hstl_en',
|
|
406
|
+
int(ui.ocontroller_ui.out_hstl_ckbox.isChecked()))
|
|
407
|
+
|
|
408
|
+
|
|
409
|
+
# =============================================================================
|
|
410
|
+
def hstl_doubler_changed(ui, dds):
|
|
411
|
+
"""Update HSTL doubler state
|
|
412
|
+
:param ui: ddsctrlui class instance (object)
|
|
413
|
+
:param dds: dds class instance (object)
|
|
414
|
+
:returns: None
|
|
415
|
+
"""
|
|
416
|
+
dds.set_hstl_doubler_state(ui.ocontroller_ui.hstl_doubler_ckbox.isChecked())
|
|
417
|
+
QSettings().setValue('dds_ctrl/hstl_doubler',
|
|
418
|
+
int(ui.ocontroller_ui.hstl_doubler_ckbox.isChecked()))
|
|
419
|
+
|
|
420
|
+
|
|
421
|
+
# =============================================================================
|
|
422
|
+
def cmos_output_changed(ui, dds):
|
|
423
|
+
"""Update CMOS outputs state
|
|
424
|
+
:param ui: ddsctrlui class instance (object)
|
|
425
|
+
:param dds: dds class instance (object)
|
|
426
|
+
:returns: None
|
|
427
|
+
"""
|
|
428
|
+
dds.set_cmos_output_state(ui.ocontroller_ui.out_cmos_ckbox.isChecked())
|
|
429
|
+
QSettings().setValue('dds_ctrl/cmos_en',
|
|
430
|
+
int(ui.ocontroller_ui.out_cmos_ckbox.isChecked()))
|
|
431
|
+
|
|
432
|
+
|
|
433
|
+
# =============================================================================
|
|
434
|
+
def apply_btn_ictrl_clicked(ui, dds, model):
|
|
435
|
+
"""Define action when apply button of input control form is clicked.
|
|
436
|
+
:param ui: ddsctrlui class instance (object)
|
|
437
|
+
:param dds: dds class instance (object)
|
|
438
|
+
:returns: None
|
|
439
|
+
"""
|
|
440
|
+
# Update input frequency
|
|
441
|
+
dds.set_ifreq(ui.icontroller_ui.ifreq_dsb.value())
|
|
442
|
+
if model == "AD9912":
|
|
443
|
+
# Update PLL attribute
|
|
444
|
+
dds.set_pll_state(ui.icontroller_ui.advanced_config_gbox.isChecked())
|
|
445
|
+
dds.set_pll_doubler_state(
|
|
446
|
+
ui.icontroller_ui.pll_doubler_ckbox.isChecked())
|
|
447
|
+
dds.set_pll_multiplier_factor(
|
|
448
|
+
ui.icontroller_ui.pll_factor_sbox.value())
|
|
449
|
+
dds.set_cp_current(ui.icontroller_ui.cp_current_cbox.currentIndex())
|
|
450
|
+
dds.set_vco_range(ui.icontroller_ui.vco_range_cbox.currentIndex())
|
|
451
|
+
# Force update of output frequency in case of system clock is updated
|
|
452
|
+
# because output is a function of system clock frequency value.
|
|
453
|
+
####ofreq_changed(ui, dds)
|
|
454
|
+
# Save new settings in ini file.
|
|
455
|
+
settings = QSettings()
|
|
456
|
+
settings.setValue('dds_ctrl/ifreq', ui.icontroller_ui.ifreq_dsb.value())
|
|
457
|
+
if model == "AD9912":
|
|
458
|
+
settings.setValue(
|
|
459
|
+
'dds_ctrl/pll_en',
|
|
460
|
+
int(ui.icontroller_ui.advanced_config_gbox.isChecked()))
|
|
461
|
+
settings.setValue('dds_ctrl/pll_doubler',
|
|
462
|
+
int(ui.icontroller_ui.pll_doubler_ckbox.isChecked()))
|
|
463
|
+
settings.setValue('dds_ctrl/pll_factor',
|
|
464
|
+
ui.icontroller_ui.pll_factor_sbox.value())
|
|
465
|
+
settings.setValue('dds_ctrl/vco_range',
|
|
466
|
+
ui.icontroller_ui.vco_range_cbox.currentIndex())
|
|
467
|
+
settings.setValue('dds_ctrl/cp_current',
|
|
468
|
+
ui.icontroller_ui.cp_current_cbox.currentIndex())
|
|
469
|
+
|
|
470
|
+
|
|
471
|
+
# =============================================================================
|
|
472
|
+
def apply_btn_octrl_clicked(ui, dds, model):
|
|
473
|
+
"""Define action when apply button of output control form is clicked.
|
|
474
|
+
:param ui: ddsctrlui class instance (object)
|
|
475
|
+
:param dds: dds class instance (object)
|
|
476
|
+
:param model: dds model i.e. "AD9912" or "AD9915" (str)
|
|
477
|
+
:returns: None
|
|
478
|
+
"""
|
|
479
|
+
ofreq_changed(ui, dds)
|
|
480
|
+
amplitude_changed(ui, dds)
|
|
481
|
+
phase_changed(ui, dds)
|
|
482
|
+
if model == "AD9912":
|
|
483
|
+
hstl_output_changed(ui, dds)
|
|
484
|
+
hstl_doubler_changed(ui, dds)
|
|
485
|
+
cmos_output_changed(ui, dds)
|
|
486
|
+
|
|
487
|
+
|
|
488
|
+
# =============================================================================
|
|
489
|
+
def find_dds_board():
|
|
490
|
+
"""Return list of AD9912 and AD9915 dds development board connected to
|
|
491
|
+
computer.
|
|
492
|
+
:returns: list of AD9912 and AD9915 dds (list of usb.core.Device)
|
|
493
|
+
"""
|
|
494
|
+
# if firmware of AD9912Dev board is not loaded, display msgbox.
|
|
495
|
+
if usb.core.find(idVendor=0x0456, idProduct=0xee08) is not None or \
|
|
496
|
+
usb.core.find(idVendor=0x0456, idProduct=0xee24) is not None:
|
|
497
|
+
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)
|
|
501
|
+
# AD9915 dev board (through FTDI UM232H)
|
|
502
|
+
ad9915dev = usb.core.find(idVendor=0x0403, idProduct=0x6014, find_all=True)
|
|
503
|
+
return {'ad9912dev': ad9912dev, 'ad9915dev': ad9915dev}
|
|
504
|
+
|
|
505
|
+
|
|
506
|
+
# =============================================================================
|
|
507
|
+
def ddscontroller(ini_file, window_title,
|
|
508
|
+
dds_model=None, dds_bus=None, dds_address=None):
|
|
509
|
+
"""Main program of DDS controller project.
|
|
510
|
+
:returns: None
|
|
511
|
+
"""
|
|
512
|
+
app = QApplication(sys.argv)
|
|
513
|
+
app.setOrganizationName(ORGANIZATION)
|
|
514
|
+
app.setApplicationName(APP_NAME)
|
|
515
|
+
|
|
516
|
+
if dds_model is None or dds_bus is None or dds_address is None:
|
|
517
|
+
# Select board to handle
|
|
518
|
+
dds_boards = find_dds_board()
|
|
519
|
+
dds_boards_for_human = \
|
|
520
|
+
["AD9912 on bus {} address {}".format(dds.bus, dds.address)
|
|
521
|
+
for dds in dds_boards['ad9912dev']] + \
|
|
522
|
+
["AD9915 on bus {} address {}".format(dds.bus, dds.address)
|
|
523
|
+
for dds in dds_boards['ad9915dev']]
|
|
524
|
+
msg = DdsBoardSelection(dds_boards_for_human)
|
|
525
|
+
retval = msg.exec_()
|
|
526
|
+
if retval == 1:
|
|
527
|
+
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])
|
|
531
|
+
else:
|
|
532
|
+
sys.exit()
|
|
533
|
+
if dds_model == "AD9912":
|
|
534
|
+
dds = sad9912dev.SAd9912Dev()
|
|
535
|
+
retval = dds.connect(bus=dds_bus, address=dds_address)
|
|
536
|
+
if retval is False:
|
|
537
|
+
logging.critical('DDS connection problem')
|
|
538
|
+
QMessageBox.critical(None, 'DDS connection error',
|
|
539
|
+
'DDS connection problem\nCheck connection')
|
|
540
|
+
sys.exit()
|
|
541
|
+
ui = DdsCtrlUi("ad9912")
|
|
542
|
+
elif dds_model == "AD9915":
|
|
543
|
+
dds = sad9915dev.SAd9915Dev()
|
|
544
|
+
url = "ftdi://ftdi:232h:{:x}:{:x}/1".format(dds_bus, dds_address)
|
|
545
|
+
retval = dds.connect(url)
|
|
546
|
+
if retval is False:
|
|
547
|
+
logging.critical('DDS connection problem')
|
|
548
|
+
QMessageBox.critical(None, 'DDS connection error',
|
|
549
|
+
'DDS connection problem\nCheck connection')
|
|
550
|
+
sys.exit()
|
|
551
|
+
dds.set_operation_mode(ad9915dev.OPMODE['modulus'])
|
|
552
|
+
ui = DdsCtrlUi("ad9915")
|
|
553
|
+
else:
|
|
554
|
+
logging.critical('DDS selection problem')
|
|
555
|
+
QMessageBox.critical(None, 'DDS selection error',
|
|
556
|
+
'Problem with the DDS selection')
|
|
557
|
+
sys.exit()
|
|
558
|
+
|
|
559
|
+
if path.isfile(ini_file) is True:
|
|
560
|
+
check_ini_file(ini_file)
|
|
561
|
+
else:
|
|
562
|
+
check_ini_file()
|
|
563
|
+
|
|
564
|
+
init_dev(dds, dds_model)
|
|
565
|
+
init_ui(ui, dds_model)
|
|
566
|
+
handle_ocontroller_ui_behavior(ui, dds, dds_model)
|
|
567
|
+
|
|
568
|
+
# Logic
|
|
569
|
+
# # Following parameters, input freq, pll state, pll doubler state and
|
|
570
|
+
# # pll factor, modify system clock frequency which in turn modify
|
|
571
|
+
# # output frequency. Behavior of UI can be:
|
|
572
|
+
# # - update display of output frequency to new dds frequency
|
|
573
|
+
# # - update dds frequency to current display of output frequency
|
|
574
|
+
# # Choice was taken to used the last behavior.
|
|
575
|
+
dds.ifreq_updated.connect(lambda **kwargs: ofreq_changed(ui, dds))
|
|
576
|
+
if dds_model == "AD9912":
|
|
577
|
+
dds.pll_state_updated.connect(
|
|
578
|
+
lambda **kwargs: ofreq_changed(ui, dds))
|
|
579
|
+
dds.pll_doubler_updated.connect(
|
|
580
|
+
lambda **kwargs: ofreq_changed(ui, dds))
|
|
581
|
+
dds.pll_factor_updated.connect(
|
|
582
|
+
lambda **kwargs: ofreq_changed(ui, dds))
|
|
583
|
+
|
|
584
|
+
ui.ocontroller_ui.auto_update_ckbox.stateChanged.connect(
|
|
585
|
+
lambda: handle_ocontroller_ui_behavior(ui, dds, dds_model))
|
|
586
|
+
ui.ocontroller_ui.auto_update_ckbox.stateChanged.connect(
|
|
587
|
+
lambda: QSettings().setValue('dds_ctrl/auto_update',
|
|
588
|
+
ui.ocontroller_ui.auto_update_ckbox.isChecked()))
|
|
589
|
+
|
|
590
|
+
ui.icontroller_ui.apply_btn.clicked.connect(
|
|
591
|
+
lambda: apply_btn_ictrl_clicked(ui, dds, dds_model))
|
|
592
|
+
|
|
593
|
+
ui.debug_ui.get_reg_btn.clicked.connect(
|
|
594
|
+
lambda: get_reg_btn_clicked(ui, dds))
|
|
595
|
+
ui.debug_ui.set_reg_btn.clicked.connect(
|
|
596
|
+
lambda: set_reg_btn_clicked(ui, dds))
|
|
597
|
+
|
|
598
|
+
form = QMainWindow()
|
|
599
|
+
form.setCentralWidget(ui)
|
|
600
|
+
form.setWindowTitle(window_title)
|
|
601
|
+
form.show()
|
|
602
|
+
|
|
603
|
+
sys.exit(app.exec_())
|
|
604
|
+
|
|
605
|
+
|
|
606
|
+
#==============================================================================
|
|
607
|
+
if __name__ == '__main__':
|
|
608
|
+
# Setup logger
|
|
609
|
+
configure_logging()
|
|
610
|
+
# Parse command line
|
|
611
|
+
args = parse_cmd_line()
|
|
612
|
+
# Setup application
|
|
613
|
+
ddscontroller(args.ini_file, args.wtitle,
|
|
614
|
+
args.model, args.bus, args.address)
|