Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Drivers for Valon 5015 Frequency Synthesizer instrument #270

Merged
merged 9 commits into from
Nov 22, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
196 changes: 196 additions & 0 deletions docs/examples/Valon_5015.ipynb
Original file line number Diff line number Diff line change
@@ -0,0 +1,196 @@
{
"cells": [
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# QCoDeS example with Valon 5015"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"The Valon 5015 frequency synthesizer uses multiple phase lock loops to provide exceptionally low phase noise over the 10MHz to 15GHz range. The multiple loop technique also greatly reduced spurs while providing high resolution tuning precision. Calibrated output power may be accurately set from +13dBm down to -30dBm in 0.1dB steps."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"from qcodes_contrib_drivers.drivers.Valon.Valon_5015 import Valon5015"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Be sure to specify the correct IP address in the constructor. If the driver cannot find the device, a VisaIOError is raised."
]
},
{
"cell_type": "code",
"execution_count": 2,
"metadata": {},
"outputs": [],
"source": [
"ip = \"192.168.0.3\"\n",
"valon = Valon5015(name=\"Valon\", address=f\"TCPIP0::{ip}::23::SOCKET\")"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Get status of the device:"
]
},
{
"cell_type": "code",
"execution_count": 3,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Valon Technology, 5015, 12203435, R6 version 1.3c Build: May 8 2023 17:01:59\n",
" VBAT = 1223 5.911 V\n",
" IBAT = 336 0.108 Amps 0.638 Watts\n",
" UPTS = 1725 21.9 C\n",
" +5V = 0 0.000 V\n",
" -5V = 3654 0.451 V\n",
" +3.3VRF = 121 0.194 V\n",
" +3.3V = 30 0.048 V\n",
" LM = 31 11111\n",
" uP clock = 72 MHz\n",
" UID = 43194144 34555430 05d4ff35 REV_ID,DEV_ID=10016418 CR=0x0\n",
"FLASH size = 256k\n",
" Max freq = 15 GHz\n"
]
}
],
"source": [
"status = valon.status()\n",
"print(status)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Get the device identifying string:"
]
},
{
"cell_type": "code",
"execution_count": 4,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Valon Technology, 5015, 12203435, R6 version 1.3c Build: May 8 2023 17:01:59\n"
]
}
],
"source": [
"id = valon.id()\n",
"print(id)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Set device parameters:"
]
},
{
"cell_type": "code",
"execution_count": 5,
"metadata": {},
"outputs": [],
"source": [
"valon.frequency(10e6)\n",
"valon.offset(100.5)\n",
"valon.power(0.5)\n",
"valon.modulation_db(0.5)\n",
"valon.modulation_frequency(100)\n",
"valon.low_power_mode_enabled(True)\n",
"valon.buffer_amplifiers_enabled(True)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Retrieve device parameters:"
]
},
{
"cell_type": "code",
"execution_count": 6,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Frequency: 10000000.0\n",
"Offset: 100.0\n",
"Power: 0.5\n",
"Modulation dB: 0.5\n",
"Modulation Frequency: 10.0\n",
"Low Power Mode Enabled: True\n",
"Buffer Amplifiers Enabled: True\n"
]
}
],
"source": [
"frequency = valon.frequency()\n",
"offset = valon.offset()\n",
"power = valon.power()\n",
"modulation_db = valon.modulation_db()\n",
"modulation_frequency = valon.modulation_frequency()\n",
"low_power_mode_enabled = valon.low_power_mode_enabled()\n",
"buffer_amplifiers_enabled = valon.buffer_amplifiers_enabled()\n",
"\n",
"print(\"Frequency: \", frequency)\n",
"print(\"Offset: \", offset)\n",
"print(\"Power: \", power)\n",
"print(\"Modulation dB: \", modulation_db)\n",
"print(\"Modulation Frequency: \", modulation_frequency)\n",
"print(\"Low Power Mode Enabled: \", low_power_mode_enabled)\n",
"print(\"Buffer Amplifiers Enabled: \", buffer_amplifiers_enabled)\n"
]
}
],
"metadata": {
"kernelspec": {
"display_name": "qcodes-dev",
"language": "python",
"name": "python3"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.10.13"
},
"nbsphinx": {
"execute": "never"
}
},
"nbformat": 4,
"nbformat_minor": 2
}
197 changes: 197 additions & 0 deletions qcodes_contrib_drivers/drivers/Valon/Valon_5015.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,197 @@
"""
Driver for Valon 5015 Frequency Synthesizer.

Please refer to Valon's 5015 Frequency Synthesizer manual for further
details and functionality. This model is not SCPI compliant.
"""
import re
import logging
from typing import Any

from qcodes.utils.validators import Ints, Numbers, Bool
from qcodes import VisaInstrument
import pyvisa.constants as vi_const


log = logging.getLogger(__name__)


class Valon5015(VisaInstrument):
"""Driver for Valon 5015 Frequency Synthesizer.

This driver does not contain all commands available for the Valon 5015 but
only the ones most commonly used.
"""
__frequency_regex = re.compile(r"F (?P<frequency>\d+([.]\d+)?) MHz")
__offset_regex = re.compile(r"OFFSET (?P<offset>\d+([.]\d+)?) MHz")
__power_regex = re.compile(r"PWR (?P<power>\d+[.]\d+)")
__modulation_db_regex = re.compile(r"AMD (?P<modulation_db>\d+[.]\d+) dB")
__modulation_frequency_regex = re.compile(r"AMF (?P<modulation_frequency>\d+([.]\d+)?) kHz")
__low_power_mode_enabled_regex = re.compile(r"PDN (?P<low_power_mode_enabled>[01])")
__buffer_amplifiers_enabled_regex = re.compile(r"OEN (?P<buffer_amplifiers_enabled>[01])")

def __init__(self, name: str, address: str, **kwargs: Any):
super().__init__(name, address, terminator='\r\n', **kwargs)

self.add_parameter(name='status',
label='Status',
get_cmd=self._get_status,
docstring="Get the manufacturer's name, model, serial number, firmware version, firmware build date and time, uP clock rate, power supply voltage (VBAT), internal temperature, LM, and UID.")

self.add_parameter(name='id',
label='ID',
get_cmd=self._get_id,
set_cmd=self._set_id,
vals=Ints(0),
docstring="Get an identifying string containing the following information: Valon Technology, 5015/5019, serial number, firmware revision. Setting a number to the `id` method will flash the `Status LED` in short burst of 1 second for the number of times entered.")

self.add_parameter(name='frequency',
label='Frequency',
unit='Hz',
get_cmd=self._get_frequency,
set_cmd=self._set_frequency,
vals=Numbers(10e6, 15e9),
docstring="Get/set the frequency of the single tone. The allowed range is from 10 MHz to 15 GHz and the value is expressed in Hz.")

self.add_parameter(name='offset',
label='Offset',
unit='Hz',
get_cmd=self._get_offset,
set_cmd=self._set_offset,
vals=Numbers(-4295e3, 4295e3),
docstring="Get/set the offset to be added or substracted from the frequency. The allowed range is from -4.295 GHz to 4295 GHz and the value is expressed in Hz.")

self.add_parameter(name='power',
label='Power',
unit='dBm',
get_cmd=self._get_power,
set_cmd=self._set_power,
docstring="Get/Set the power level in dBm.")

self.add_parameter(name='modulation_db',
label='Modulation_dB',
unit='dB',
get_cmd=self._get_modulation_db,
set_cmd=self._set_modulation_db,
vals=Numbers(0.0),
docstring="Get/Set the AM modulation in dB. A value of 0 dB disables the AM modulation.")

self.add_parameter(name='modulation_frequency',
label='Modulation_Frequency',
unit='Hz',
get_cmd=self._get_modulation_frequency,
set_cmd=self._set_modulation_frequency,
vals=Numbers(1, 2e3),
docstring="Get/Set the AM modulation frequency. The allowed range is from 1 Hz to 2 kHz and the value is expressed in Hz.")

self.add_parameter(name='low_power_mode_enabled',
label='Low Power Mode Enabled',
get_cmd=self._get_low_power_mode_enabled,
set_cmd=self._set_low_power_mode_enabled,
vals=Bool(),
docstring="Enables or disables the low power mode.")

self.add_parameter(name='buffer_amplifiers_enabled',
label='Buffer Amplifiers Enabled',
get_cmd=self._get_buffer_amplifiers_enabled,
set_cmd=self._set_buffer_amplifiers_enabled,
vals=Bool(),
docstring="Enables or disables the RF output buffer amplifiers.")

def _get_status(self):
responses = [self.ask("stat") for _ in range(14)]
responses = "\n".join(responses[1:])
self._flush()
return responses

def _get_id(self):
responses = [self.ask("id") for _ in range(2)]
responses = "\n".join(responses[1:])
self._flush()
return responses

def _set_id(self, n):
self.ask(f"id {n}")
self._flush()

def _get_frequency(self):
response = [self.ask("frequency") for _ in range(2)][1]
match = self.__frequency_regex.match(response)
frequency = match.group("frequency")
self._flush()
return float(frequency) * 1e6

def _set_frequency(self, frequency):
self.ask(f"frequency {int(frequency)} Hz")
self._flush()

def _get_offset(self):
response = [self.ask("offset") for _ in range(2)][1]
match = self.__offset_regex.match(response)
offset = match.group("offset")
self._flush()
return float(offset) * 1e6

def _set_offset(self, offset):
self.ask(f"offset {int(offset)} Hz")
self._flush()

def _get_power(self):
response = [self.ask("power") for _ in range(2)][1]
match = self.__power_regex.match(response)
power = match.group("power")
self._flush()
return float(power)

def _set_power(self, power):
self.ask(f"power {power}")
self._flush()

def _get_modulation_db(self):
response = [self.ask("amd") for _ in range(2)][1]
match = self.__modulation_db_regex.match(response)
modulation_db = match.group("modulation_db")
self._flush()
return float(modulation_db)

def _set_modulation_db(self, modulation_db):
self.ask(f"amd {modulation_db}")
self._flush()

def _get_modulation_frequency(self):
response = [self.ask("amf") for _ in range(2)][1]
match = self.__modulation_frequency_regex.match(response)
modulation_frequency = match.group("modulation_frequency")
self._flush()
return float(modulation_frequency) * 1e3

def _set_modulation_frequency(self, modulation_frequency):
self.ask(f"amd {int(modulation_frequency)}")
self._flush()

def _get_low_power_mode_enabled(self):
response = [self.ask("pdn") for _ in range(2)][1]
match = self.__low_power_mode_enabled_regex.match(response)
low_power_mode_enabled = match.group("low_power_mode_enabled")
self._flush()
return True if low_power_mode_enabled == "1" else False

def _set_low_power_mode_enabled(self, low_power_mode_enabled):
low_power_mode_enabled = "1" if low_power_mode_enabled else "0"
self.ask(f"pdn {low_power_mode_enabled}")
self._flush()

def _get_buffer_amplifiers_enabled(self):
response = [self.ask("oen") for _ in range(2)][1]
match = self.__buffer_amplifiers_enabled_regex.match(response)
buffer_amplifiers_enabled = match.group("buffer_amplifiers_enabled")
self._flush()
return True if buffer_amplifiers_enabled == "1" else False

def _set_buffer_amplifiers_enabled(self, buffer_amplifiers_enabled):
buffer_amplifiers_enabled = "1" if buffer_amplifiers_enabled else "0"
self.ask(f"oen {buffer_amplifiers_enabled}")
self._flush()

def _flush(self):
self.visa_handle.flush(vi_const.VI_READ_BUF | vi_const.VI_READ_BUF_DISCARD)
Empty file.