Source code for Instruments_Libraries.MG3694C

"""
Created on Wed Dec  1 07:00:19 2021

@author: Martin.Mihylov

Currently the Anritsu MG3694C Signal Generator is in dynamic DHCP mode!!!
    1) Make sure Instrument and PC are connected vie ethernet cable.
    2) In the Windows terminal run: arp -a
    3) Find the IP-Adress corresponding to the MAC:Adress printed on the device
    4) If it does not show up try opening NI-MAX and run auto-discover.
    5) Try "arp -a" again.

Legacy instructions (not applicable as of 13.01.2026):
    1) Go to Network Adapter Settings -> 'Internetprotocoll, Version 4(TCP/IPv4)'
    2) Change the IP-Address from 'automatic' to 'static' and give the IP:192.168.0.1
    3) DNS will be filled automatically! Press 'OK' and leave.
    4) The standard IP of the instrument is: 192.168.0.254
    5) After your measurement dont forget to change the IP back to 'automatic'!
"""

import functools
import time
from collections.abc import Callable
from typing import Any, cast

import pyvisa as visa
import pyvisa.constants as vi_const

from .BaseInstrument import BaseInstrument


[docs] def auto_reconnect(func: Callable) -> Callable: """ Decorator that catches VISA errors, reconnects, and retries the command. """ @functools.wraps(func) def wrapper(self, *args: Any, **kwargs: Any) -> Any: attempts = 3 for i in range(attempts): try: # Try to execute the function (write/query) return func(self, *args, **kwargs) except (visa.errors.VisaIOError, OSError) as e: # If it's the last attempt, raise the error if i == attempts - 1: print(f"Failed after {attempts} attempts. Error: {e}") raise e print( f"""Connection lost during {getattr(func, "__name__", "function")}. Reconnecting (Attempt {i + 1}/{attempts})...""" ) try: self.reconnect() except Exception as reconnect_err: print(f"Reconnect failed: {reconnect_err}") time.sleep(1) # Wait a bit before next loop return wrapper
[docs] class MG3694C(BaseInstrument): """ This class uses pyvisa to connect to an Anritsu MG3694C Signal Generator. """ def __init__(self, resource_str: str = "169.254.20.95", visa_library: str = "@ivi", **kwargs): kwargs.setdefault("read_termination", "\n") kwargs.setdefault("query_delay", 0.5) self._pyvisa_kwargs = kwargs super().__init__(str(resource_str), visa_library=visa_library, **kwargs) self.visa_library = visa_library try: self._resource.set_visa_attribute(vi_const.VI_ATTR_TCPIP_KEEPALIVE, True) # type: ignore except visa.errors.VisaIOError: pass print(f"Connected to: {self.get_idn()}")
[docs] def connect(self): self._rm = visa.ResourceManager(self.visa_library) self._resource = cast( visa.resources.MessageBasedResource, self._rm.open_resource(str(self.resource_str), **self._pyvisa_kwargs), ) try: self._resource.set_visa_attribute(vi_const.VI_ATTR_TCPIP_KEEPALIVE, True) # type: ignore except visa.errors.VisaIOError: pass
[docs] def reconnect(self): try: self._resource.close() except Exception: pass time.sleep(1) self.connect()
[docs] @auto_reconnect def write(self, command: str) -> None: super().write(command)
[docs] @auto_reconnect def query(self, command: str) -> str: return super().query(command)
[docs] @auto_reconnect def read(self) -> str: return self._resource.read()
# ============================================================================= # Abort Command # =============================================================================
[docs] def abort(self): """ Forces the trigger system to the idle state. Any sweep in progress is aborted as soon as possible. Parameters: None """ return self.write(":ABORt")
# ============================================================================= # Ask Instrument about Stats and Parameters # =============================================================================
[docs] def get_output_protection(self) -> str: """ Requests the currently programmed state of the MG369xC RF output during frequency changes in CW or step sweep mode. """ return self.query(":OUTPut:PROTection?")
[docs] def get_output_retrace(self) -> str: """ Requests the currently programmed state of the MG369xC RF output during sweep retrace. """ return self.query(":OUTPut:PROTection:RETRace?")
[docs] def get_output_impedance(self) -> float: """ Queries the MG369xC RF output impedance. The impedance is nominally 50 ohms and is not settable. """ return float(self.query(":OUTPut:IMPedance?"))
[docs] def get_output_power_level(self) -> float: """ Requests the value currently programmed for the RF output power level. """ return float(self.query(":SOURce:POWer:LEVel:IMMediate:AMPLitude?") or 0.0)
[docs] def get_maximal_power_level(self) -> float: """ Requests the maximum RF output power level value that can be programmed for the particular MG369xC model. """ return float(self.query(":SOURce:POWer? MAX"))
# ============================================================================= # Ask Source Amplitude Modulation # =============================================================================
[docs] def get_am_logsens(self) -> float: """ Requests the currently programmed AM sensitivity value for the external AM Log mode. """ return float(self.query(":SOURce:AM:LOGSens?"))
[docs] def get_am_logdepth(self) -> float: """ Requests the currently programmed modulation depth value for the internal AM Log mode. """ return float(self.query(":SOURce:AM:LOGDepth?"))
[docs] def get_am_internal_wave(self) -> str: """ Requests the currently selected modulating waveform for the internal AM function. """ return self.query(":SOURce:AM:INTernal:WAVE?")
[docs] def get_am_internal_freq(self) -> float: """Requests the currently programmed modulating waveform frequency value for the internal AM function. """ return float(self.query(":SOURce:AM:INTernal:FREQuency?"))
[docs] def get_am_state(self) -> str: """Requests currently programmed amplitude modulation state (on/off)""" return self.query(":SOURce:AM:STATe?")
[docs] def get_am_type(self) -> str: """Requests the currently programmed AM operating mode.""" return self.query(":SOURce:AM:TYPE?")
# ============================================================================= # Frequency Modulation # =============================================================================
[docs] def get_fm_internal_wave(self) -> str: """ Requests the currently selected modulating waveform for the internal FM function. """ return self.query(":SOURce:FM:INTernal:WAVE?")
[docs] def get_fm_internal_freq(self) -> float: """ Requests the currently programmed modulating waveform frequency value for the internal FM function. """ return float(self.query(":SOURce:FM:INTernal:FREQuency?"))
[docs] def get_fm_mode(self) -> str: """ Requests the currently programmed synthesis mode used to generate the FM signal. """ return self.query(":SOURce:FM:MODE?")
[docs] def get_fm_bwidth(self) -> str: """ Requests the currently programmed Unlocked FM synthesis mode of operation (narrow or wide). """ return self.query(":SOURce:FM:BWIDth?")
[docs] def get_fm_state(self) -> str: """ Requests the currently programmed frequency modulation state (on/off). """ return self.query(":SOURce:FM:STATe?")
# ============================================================================= # Frequency Commands # =============================================================================
[docs] def get_freq_cw(self) -> float: """ Requests the current value of the frequency parameter. """ return float(self.query(":SOURce:FREQuency:CW?") or 0.0)
[docs] def get_freq_step(self) -> float: """ Requests the current step increment value of the frequency parameter. """ return float(self.query(":SOURce:FREQuency:CW:STEP:INCRement?") or 0.0)
[docs] def get_freq_center_freq(self) -> float: """ Requests the current value of the RF output center frequency. """ return float(self.query(":SOURce:FREQuency:CENTer?") or 0.0)
[docs] def get_freq_mode(self) -> str: """ Requests the currently selected programming mode for frequency control. """ return self.query(":SOURce:FREQuency:MODE?")
[docs] def get_freq_span(self) -> float: """ Requests the current value for SWEep[1] sweep span. """ return float(self.query(":SOURce:FREQuencySPAN:?") or 0.0)
[docs] def get_freq_start(self) -> float: """ Requests the current value for SWEep[1] start frequency. """ return float(self.query(":SOURce:FREQuency:STARt?") or 0.0)
[docs] def get_freq_stop(self) -> float: """ Requests the current value for SWEep[1] stop frequency. """ return float(self.query(":SOURce:FREQuency:STOP?") or 0.0)
[docs] def get_freq_unit(self) -> str: """ Requests the currently selected frequency unit. """ return self.query("UNIT:FREQuency?")
# ============================================================================= # Pulse Modulation # =============================================================================
[docs] def get_pm_bwidth(self) -> str: """ Requests the currently programmed phase modulation operating mode. """ return self.query(":SOURce:PM:BWIDth?")
[docs] def get_pm_internal_wave(self) -> str: """ Requests the currently selected modulating waveform for the internal phase modulation function. """ return self.query(":SOURce:PM:INTernal:WAVE?")
[docs] def get_pm_internal_freq(self) -> float: """ Requests the currently programmed modulating waveform frequency value for the internal phase modulation function. """ return float(self.query(":SOURce:PM:INTernal:FREQuency?") or 0.0)
[docs] def get_pm_state(self) -> str: """ Requests the currently programmed phase modulation state (on/off). """ return self.query(":SOURce:PM:STATe?")
# ============================================================================= # Set Output # =============================================================================
[docs] def set_rf_output(self, state: int | str) -> None: """Activates the Signal Genrator RF Output. Parameters ---------- state : str | int ``ON`` | ``OFF`` | ``1`` | ``0`` Raises ------ ValueError Valid values are: ON, OFF, 1, 0 """ state = self._parse_state(state) self.write(f":OUTPut:STATe {state}")
[docs] def set_output(self, state: int | str) -> None: """Activates the Signal Genrator RF Output. Parameters ---------- state : str | int ``ON`` | ``OFF`` | ``1`` | ``0`` Raises ------ ValueError Valid values are: ON, OFF, 1, 0 """ self.set_rf_output(state)
[docs] def set_output_protection(self, state: int | str) -> None: """ ON causes the MG369xC RF output to be turned off (blanked) during frequency changes in CW or step sweep mode. OFF leaves RF output turned on (un blanked). Parameters ---------- state : str | int Default: ``ON`` * ``ON`` | ``OFF`` | ``1`` | ``0`` """ state = self._parse_state(state) self.write(f":OUTPut:PROTection {state}")
[docs] def set_output_retrace(self, state: int | str) -> None: """ ON causes the MG369xC RF output to be turned off during sweep retrace. OFF leaves RF output turned on. Parameters ---------- state : str | int Default: ``OFF`` * ``ON`` | ``OFF`` | ``1`` | ``0`` """ state = self._parse_state(state) self.write(":OUTPut:PROTection:RETRace " + str(state))
# ============================================================================= # SOURce:POWer subsystem # =============================================================================
[docs] def set_rf_power(self, value: int | float): """Sets the Signal Generator Output Power in dBm. Parameters ---------- value : int | float Output Power in dBm """ min_val = -20.0 max_val = 30.0 if value > max_val or value < min_val: raise ValueError( f"Power out of range! You can set power between {min_val} and {max_val} dBm!" ) self.write(f":SOURce:POWer:LEVel:IMMediate:AMPLitude {str(value)} dBm")
[docs] def set_output_power_level(self, value: int | float) -> None: """Sets the Signal Generator Output Power in dBm. Alias for set_rf_power(). Parameters ---------- value : int | float Output Power in dBm """ self.set_rf_power(value)
# ============================================================================= # Set Control system commands: # Amplitude Modulation # Correction Commands # Frequency Modulation # Frequency Commands # Pulse Modulation # # ============================================================================= # ============================================================================= # Source - AM # =============================================================================
[docs] def set_am_logsens(self, value: int | float) -> None: """ Sets the AM sensitivity for the external AM Log mode. Parameters ---------- value : int | float Sensitivity (in dB/V) Range: 0 to 25 dB/V Default: 3 dB/V """ if 0 <= int(value) <= 25: self.write(":SOURce:AM:LOGSens " + str(value) + " dB/V") else: raise ValueError( f"Invalid AM log sensitivity: {value} dB/V. Valid range is 0 to 25 dB/V." )
[docs] def set_am_logdepth(self, value: int | float) -> None: """ Sets the modulation depth of the AM signal in the internal AM Log mode. Parameters ---------- value : int | float Modulation depth (in dB). Valid range is 0 to 25 dB. Default is 3 dB. """ if 0 <= int(value) <= 25: self.write(f":SOURce:AM:LOGDepth {int(value)} dB") else: raise ValueError(f"Invalid AM log depth: {int(value)} dB. Valid range is 0 to 25 dB.")
[docs] def set_am_internal_wave(self, state: str) -> None: """ Selects the modulating waveform (from the internal AM generator) for the internal AM function. Parameters ---------- state : str * ``'SINE'`` - Sine wave * ``'GAUSsian'`` - Gaussian noise * ``'RDOWn'`` - Negative ramp * ``'RUP'`` - Positive ramp * ``'SQUare'`` - Square wave * ``'TRIangle'`` - Triangle wave * ``'UNIForm'`` - Uniform noise """ valid_list = ["SINE", "GAUSsian", "RDOWn", "RUP", "SQUare", "TRIangle", "UNIForm"] valid_state = self._check_scpi_param(state, valid_list) self.write(":SOURce:AM:INTernal:WAVE " + valid_state)
[docs] def set_am_internal_freq(self, value: int | float, unit: str) -> None: """ Sets the frequency of the modulating waveform for the internal AM function (see :AM:INTernal:WAVE). Parameters ---------- value : int | float Frequency Range: 0.1 Hz to 1 MHz for sine wave. 0.1 Hz to 100 kHz for square, triangle, and ramp waveforms. Default: 1 kHz unit : str Unit of the frequency (Hz, kHz, MHz) """ wave = self.get_am_internal_wave().strip() multipliers = {"HZ": 1, "KHZ": 1e3, "MHZ": 1e6} if unit.upper() not in multipliers: raise ValueError(f"Invalid unit '{unit}'. Must be one of 'Hz', 'kHz', 'MHz'.") freq_hz = value * multipliers[unit.upper()] if wave == "SINE": if not (0.1 <= freq_hz <= 1e6): raise ValueError( f"Frequency {value} {unit} is out of range (0.1 Hz to 1 MHz) for SINE wave." ) else: if not (0.1 <= freq_hz <= 100e3): raise ValueError( f"Frequency {value} {unit} is out of range (0.1 Hz to 100 kHz) for {wave} wave." ) self.write(f":SOURce:AM:INTernal:FREQuency {value} {unit}")
[docs] def set_am_state(self, state: str | int) -> None: """ Enable/disable amplitude modulation of MG369xC RF output signal. Parameters ---------- state : str | int Default: ``OFF`` * ``ON`` | ``OFF`` | ``1`` | ``0`` """ state = self._parse_state(state) self.write(":SOURce:AM:STATe " + str(state))
[docs] def set_am_type(self, state: str) -> None: """ Selects the AM operating mode. Parameters ---------- state : str Default: ``LINear`` * ``LINear`` | ``LOGarithmic`` """ valid_state = self._check_scpi_param(state, ["LINear", "LOGarithmic"]) self.write(":SOURce:AM:TYPE " + valid_state)
# ============================================================================= # Correction Commands # =============================================================================
[docs] def set_correction_commands(self, state: str | int) -> None: """ Turns the selected user level flatness correction power-offset table on/off. Parameters ---------- state : str | int Default: ``OFF`` * ``ON`` | ``OFF`` | ``1`` | ``0`` """ state = self._parse_state(state) self.write(":SOURce:CORRection:STATe " + str(state))
# ============================================================================= # Frequency Modulation # =============================================================================
[docs] def set_fm_internal_wave(self, state: str) -> None: """ Selects the modulating waveform (from the internal FM generator) for the internal FM function. Parameters ---------- state : str Default: SINE * ``SINE`` = Sine wave * ``GAUSsian`` = Gaussian noise * ``RDOWn`` = Negative ramp * ``RUP`` =Positive ramp * ``SQUare`` = Square wave * ``TRIangle`` = Triangle wave * ``UNIForm`` = Uniform noise """ valid_list = ["SINE", "GAUSsian", "RDOWn", "RUP", "SQUare", "TRIangle", "UNIForm"] valid_state = self._check_scpi_param(state, valid_list) self.write(":SOURce:FM:INTernal:WAVE " + valid_state)
[docs] def set_fm_internal_freq(self, value: int | float, unit: str) -> None: """ Sets the frequency of the modulating waveform for the internal FM function (see :FM:INTernal:WAVE). Parameters ---------- value : int | float Frequency Range: 0.1 Hz to 1 MHz for sine wave. 0.1 Hz to 100 kHz for square, triangle, and ramp waveforms. Default: 1 kHz unit : str Unit of the frequency (Hz, kHz, MHz) """ wave = self.get_fm_internal_wave().strip() multipliers = {"Hz": 1, "kHz": 1e3, "MHz": 1e6} if unit not in multipliers: raise ValueError(f"Invalid unit '{unit}'. Must be one of 'Hz', 'kHz', 'MHz'.") freq_hz = value * multipliers[unit] if wave == "SINE": if not (0.1 <= freq_hz <= 1e6): raise ValueError( f"Frequency {value} {unit} is out of range (0.1 Hz to 1 MHz) for SINE wave." ) else: if not (0.1 <= freq_hz <= 100e3): raise ValueError( f"Frequency {value} {unit} is out of range (0.1 Hz to 100 kHz) for {wave} wave." ) self.write(f":SOURce:FM:INTernal:FREQuency {value} {unit}")
[docs] def set_fm_mode(self, state: str) -> None: """ Selects the synthesis mode employed in generating the FM signal. If LOCKed[1] or LOCKed2 is set, the YIG phase-locked loop is used in synthesizing the FM signal. If UNLocked is set, the YIG phase-lock loop is disabled and the FM signal is obtained by applying the modulating signal to the tuning coils of the YIG-tuned oscillator. Parameters ---------- state : str Default: UNLocked * ``LOCKed[1]`` = Locked Narrow FM * ``LOCKed2`` = Locked Narrow Low-Noise FM * ``UNLocked`` = Unlocked FM """ valid_state = self._check_scpi_param(state, ["LOCKed[1]", "LOCKed2", "UNLocked"]) self.write(":SOURce:FM:MODE " + valid_state)
[docs] def set_fm_bwidth(self, state: str) -> None: """ Sets the Unlocked FM synthesis mode to wide or narrow mode of operation. The Unlocked Wide FM synthesis mode allows maximum deviations of ±100 MHz for DC to 100 Hz rates. The Unlocked Narrow FM synthesis mode allows maximum deviations of ±10 MHz for DC to 8 MHz rates. Parameters ---------- state : str * ``MIN`` = narrow mode * ``MAX`` = wide mode Default: MIN """ valid_state = self._check_scpi_param(state, ["MIN", "MAX"]) self.write(":SOURce:FM:BWIDth " + valid_state)
[docs] def set_fm_state(self, state: str | int) -> None: """ Enable/disable frequency modulation of MG369xC RF output signal. Parameters ---------- state : str | int Default: ``OFF`` * ``ON`` | ``OFF`` | ``1`` | ``0`` """ state = self._parse_state(state) self.write(":SOURce:FM:STATe " + str(state))
# ============================================================================= # Frequency Commands # =============================================================================
[docs] def set_freq_cw(self, value: int | float, unit: str | None = None) -> None: """ Parameters ---------- value : int | float Continuous Wave Frequency. Range: 10 MHz to 40 GHz unit : str (optional) Frequency Unit: 'GHz' or 'MHz' or 'Hz' """ min_freq = 10e6 # 10 MHz max_freq = 40e9 # 40 GHz if unit is None or unit.upper() == "HZ": unit = "Hz" if value <= max_freq and value >= min_freq: self.write(f":SOURce:FREQuency:CW {value} {unit}") else: raise ValueError("Minimum Frequency = 10 MHz and Maximum Frequency = 40 GHz") elif unit.upper() == "MHZ": if value * 1e6 <= max_freq and value * 1e6 >= min_freq: self.write(f":SOURce:FREQuency:CW {value} {unit}") else: raise ValueError("Minimum Frequency = 10 MHz and Maximum Frequency = 40 GHz") elif unit.upper() == "GHZ": if value * 1e9 <= max_freq and value * 1e9 >= min_freq: self.write(f":SOURce:FREQuency:CW {value} {unit}") else: raise ValueError("Minimum Frequency = 10 MHz and Maximum Frequency = 40 GHz") else: raise ValueError('Unknown input! Unit must be None or "Hz", "MHz", or "GHz"!')
[docs] def set_freq_step(self, value: int | float, unit: str) -> None: """ Sets the step increment size used with the :FREQuency:CW command. Parameters ---------- value : int | float Step increment size Range: 0.01 Hz to (MAX MIN) Default: 0.1 GHz unit : str Frequency Unit: 'Hz' or 'kHz' or 'MHz' or 'GHz' """ multipliers = {"HZ": 1, "KHZ": 1e3, "MHZ": 1e6, "GHZ": 1e9} if unit.upper() not in multipliers: raise ValueError('Unknown unit! Unit must be "Hz", "kHz", "MHz", or "GHz"') freq_hz = value * multipliers[unit.upper()] if freq_hz >= 0.01: self.write(f":SOURce:FREQuency:CW:STEP:INCRement {value} {unit}") else: raise ValueError(f"Frequency step must be >= 0.01 Hz. Got: {freq_hz} Hz")
[docs] def set_freq_cent(self, value: int | float, unit: str) -> None: """ Sets the MG369xC RF output center frequency to the value entered. :CENTER and :SPAN frequencies are coupled values. Entering the value for one will cause the other to be recalculated. (See notes under :FREQuency:SPAN) Parameters ---------- value : int | float Center frequency unit : str Frequency Unit: 'Hz' or 'kHz' or 'MHz' or 'GHz' """ multipliers = {"HZ": 1, "KHZ": 1e3, "MHZ": 1e6, "GHZ": 1e9} if unit.upper() not in multipliers: raise ValueError('Unknown unit! Unit must be "Hz", "kHz", "MHz", or "GHz"') freq_hz = value * multipliers[unit.upper()] if freq_hz >= 0.01: self.write(f":SOURce:FREQuency:CENTer {value} {unit}") else: raise ValueError(f"Center frequency must be >= 0.01 Hz. Got: {freq_hz} Hz")
[docs] def set_frequency_mode(self, mode): """ Specifies which command subsystem controls the MG369xC frequency. Parameters ---------- mode : str Default: ``CW`` * ``CW`` | ``FIXed`` = [:SOURce]:FREQuency:CW|FIXed * ``SWEep[1]`` = [:SOURce]:SWEep[1] :SWEep and :SWEep1 may be used interchangeably * ``SWCW`` = (see notes) * ``ALSW`` = (see notes) * ``LIST<n>`` = [:SOURce]:LIST<n> (n=1-4) """ valid_list = [ "CW", "FIXed", "SWEep[1]", "SWCW", "ALSW", "LIST[1]", "LIST2", "LIST3", "LIST4", ] valid_state = self._check_scpi_param(mode, valid_list) self.write(":SOURce:FREQuency:MODE " + valid_state)
[docs] def set_freq_span(self, value: int | float, unit: str) -> None: """ Sets sweep span for SWEep[1] to value entered. :SPAN and :CENTer are coupled values. Parameters ---------- value : int | float Range: 1 kHz to (MAX MIN) Default: MAX MIN unit : str Frequency Unit: 'Hz' or 'kHz' or 'MHz' or 'GHz' """ unit_list = ["HZ", "KHZ", "MHZ", "GHZ"] if unit.upper() in unit_list: self.write(":SOURce:FREQuency:SPAN " + str(value) + " " + str(unit)) else: raise ValueError("Unknown input! See function description for more info.")
[docs] def set_freq_start(self, value: int | float, unit: str) -> None: """ Sets start frequency for SWEep[1] to the value entered. Parameters ---------- value : int | float Range: MIN to MAX Default: MIN unit : str Frequency Unit: 'Hz' or 'kHz' or 'MHz' or 'GHz' """ unit_list = ["HZ", "KHZ", "MHZ", "GHZ"] if unit.upper() in unit_list: self.write(":SOURce:FREQuency:STARt " + str(value) + " " + str(unit)) else: raise ValueError("Unknown input! See function description for more info.")
[docs] def set_freq_stop(self, value: int | float, unit: str) -> None: """ Sets stop frequency for SWEep[1] to the value entered. Parameters ---------- value : int | float Range: MIN to MAX Default: MAX unit : str Frequency Unit: 'Hz' or 'kHz' or 'MHz' or 'GHz' """ unit_list = ["HZ", "KHZ", "MHZ", "GHZ"] if unit.upper() in unit_list: self.write(":SOURce:FREQuency:STOP " + str(value) + " " + str(unit)) else: raise ValueError("Unknown input! See function description for more info.")
# ============================================================================= # Pulse Modulation # =============================================================================
[docs] def set_pm_bwidth(self, state: str) -> None: """ Selects the phase modulation (ΦM) operating mode. The Narrow ΦM mode allows maximum deviations of ±3 radians for DC to 8 MHz rates. The Wide ΦM mode allows maximum deviations of ±400 radians for DC to 1 MHz rates. Parameters ---------- state : str Default: ``MIN`` * ``MIN`` = narrow mode * ``MAX`` = wide mode """ valid_state = self._check_scpi_param(state, ["MIN", "MAX"]) self.write(":SOURce:PM:BWIDth " + valid_state)
[docs] def set_pm_internal_wave(self, state: str) -> None: """ Selects the modulating waveform (from the internal ΦM generator) for the internal phase modulation function. Parameters ---------- state : str Default: ``SINE`` * ``SINE`` = Sine wave * ``GAUSsian`` = Gaussian noise * ``RDOWn`` = Negative ramp * ``RUP`` = Positive ramp * ``SQUare`` = Square wave * ``TRIangle`` = Triangle wave * ``UNIForm`` = Uniform noise """ valid_list = ["SINE", "GAUSsian", "RDOWn", "RUP", "SQUare", "TRIangle", "UNIForm"] valid_state = self._check_scpi_param(state, valid_list) self.write(":SOURce:PM:INTernal:WAVE " + valid_state)
[docs] def set_pm_internal_freq(self, value: int | float, unit: str) -> None: """ Sets the frequency of the modulating waveform for the internal phase modulation (see :PM:INTernal:WAVE) Parameters ---------- value : int | float Frequency Range: 0.1 Hz to 1 MHz for sine wave; 0.1 Hz to 100 kHz for square, triangle, and ramp waveforms. Default: 1 kHz unit : str Unit of the frequency (Hz, kHz, MHz) """ wave = self.get_pm_internal_wave().strip() multipliers = {"HZ": 1, "KHZ": 1e3, "MHZ": 1e6} if unit.upper() not in multipliers: raise ValueError(f"Invalid unit '{unit}'. Must be one of 'Hz', 'kHz', 'MHz'.") freq_hz = value * multipliers[unit.upper()] if wave == "SINE": if not (0.1 <= freq_hz <= 1e6): raise ValueError( f"Frequency {value} {unit} is out of range (0.1 Hz to 1 MHz) for SINE wave." ) else: if not (0.1 <= freq_hz <= 100e3): raise ValueError( f"Frequency {value} {unit} is out of range (0.1 Hz to 100 kHz) for {wave} wave." ) self.write(f":SOURce:PM:INTernal:FREQuency {value} {unit}")
[docs] def set_pm_state(self, state: str | int) -> None: """ Enable/disable phase modulation of the MG369xC RF output signal. Parameters ---------- state : str | int Default: ``OFF`` * ``ON`` | ``OFF`` | ``1`` | ``0`` """ state = self._parse_state(state) self.write(":SOURce:PM:STATe " + str(state))
# ============================================================================= # Get/Save Data # =============================================================================
[docs] def get_data(self) -> dict[str, float]: """ Return a dictionary with the measured Power and CW Frequency. """ return { "Power/dBm": self.get_output_power_level(), f"CW Frequency/{self.get_freq_unit().strip()}": self.get_freq_cw(), }