Example_SignalGenerator

This is an example of using the library.

  1# %% ==========================================================================
  2# Import and Definitions
  3# =============================================================================
  4import datetime
  5import time
  6
  7import matplotlib.pyplot as plt
  8import numpy as np
  9import pandas as pd
 10from tqdm import tqdm
 11
 12# Instrument Libraries Github: https://github.com/MartinMiroslavovMihaylov/Python_Instruments_Automation_Scripts
 13# Install with:
 14# pip install git+https://github.com/MartinMiroslavovMihaylov/Python_Instruments_Automation_Scripts.git
 15from Instruments_Libraries.MG3694C import MG3694C  # Anritsu SigGen
 16
 17# from Instruments_Libraries.SMA100B import SMA100B  # Rohde&Schwarz SigGen
 18
 19# %% ==========================================================================
 20# Select Instruments and Load Instrument Libraries
 21# =============================================================================
 22# Anritsu MG3694C Signal Generator
 23SignalGenerator = MG3694C("169.254.236.243")
 24# Find the IP adress by running "arp -a" in a terminal and search for the 
 25# MAC-Address of the MG3694C (written on top/back-side of the instrument). If 
 26# it does not show up open NI-Max or Keysight Connection Expert and try to 
 27# auto-discover the instrument. NI-Max works somewhat better. Do !!!NOT!!! 
 28# change your Network Adapter to a static IP! As of 13.01.2026 the MG3694C is 
 29# set up in dynamic DHCP mode.
 30
 31# Rhode und Schwarz SMA100B Signal Generator
 32# SignalGenerator = SMA100B("169.254.2.20")
 33# The SMA100B is in dynamic mode. It typically shows you its IP address on the 
 34# instrument screen.
 35
 36SignalGenerator.reset()
 37# %% ==========================================================================
 38# Setup the Measurement
 39# =============================================================================
 40sleep_time = 1 # in seconds
 41test_frequencies = np.linspace(1e9, 40e9, 10) # frequencies in Hz
 42test_powerlevels = np.atleast_1d([-4,-2]) # powerlevels in dBm
 43
 44SigGen_freq_init = test_frequencies[0]
 45SigGen_power_init = test_powerlevels[0]
 46
 47# %% ==========================================================================
 48# Configure the Instrument
 49# =============================================================================
 50SignalGenerator.set_freq_cw(test_frequencies[0])
 51SignalGenerator.set_rf_power(test_powerlevels[0])
 52
 53# %% ==========================================================================
 54# Measurement
 55# =============================================================================
 56num_of_measurements = len(test_frequencies) * len(test_powerlevels) + 1  # 1 reference trace
 57
 58# SG_Anritsu.set_output("ON") # turn on the Signal Generator
 59
 60records = [] # Empty list to store data and meta data
 61# Loop frequencies*powerlevels
 62for idx in tqdm(range(num_of_measurements)):
 63    rec = {} # single record
 64
 65    if idx < num_of_measurements - 1:
 66        # Do some changes, like change input frequency and power
 67        power_idx = int(np.floor(idx / len(test_frequencies)))
 68        freq_idx = np.mod(idx, len(test_frequencies))
 69        rec["Signal_Power"] = test_powerlevels[power_idx]
 70        rec["Signal_Frequency"] = test_frequencies[freq_idx]
 71
 72        SignalGenerator.set_rf_power(test_powerlevels[power_idx]) # Set Power
 73        SignalGenerator.set_freq_cw(test_frequencies[freq_idx]) # Set Frequency
 74    else:  # Capture a reference measurement with no signal applied
 75        SignalGenerator.set_output(0) # turn OFF
 76        rec["Signal_Power"] = -100
 77        rec["Signal_Frequency"] = 0
 78
 79    # Take the Measurement
 80    time.sleep(sleep_time)
 81    # rec["data_peak"] = mySpecAnalyser.ExtractTraceData(SA_TraceNum,True)
 82    rec["data_peak"] = idx # measure something, this is just an example
 83
 84    # Write Meta Data
 85    rec["VCC"] = 4 # V
 86    
 87    # append the record
 88    rec["Timestamps"] = datetime.datetime.now()
 89    records.append(rec)
 90
 91########################### Measurement Ends ###########################
 92SignalGenerator.set_output("OFF") # turn off the Signal Generator
 93    
 94
 95# %% ==========================================================================
 96# Create Dataframe
 97# =============================================================================
 98meas_df = pd.DataFrame.from_records(records)
 99
100# %% ==========================================================================
101# Plot the Measurement (Using only Dataframe)
102# =============================================================================
103plt.figure(figsize=(10, 6))
104
105# 1. Optional: Filter out the "Reference" trace (where Freq was set to 0) 
106# so it doesn't mess up the X-axis scaling.
107plot_df = meas_df[meas_df["Signal_Frequency"] > 0].copy()
108
109# 2. Group the data by 'Signal_Power'
110# This automatically finds all unique power levels in the column
111grouped = plot_df.groupby("Signal_Power")
112
113# 3. Iterate through each group and plot
114for power, frame in grouped:
115    # 'power' is the unique value (e.g., -4)
116    # 'frame' is the dataframe subset for that power
117    
118    # Sort by frequency to ensure lines are drawn in order
119    frame = frame.sort_values(by="Signal_Frequency")
120    
121    plt.plot(frame["Signal_Frequency"], frame["data_peak"], 
122             label=f'Power: {power} dBm', 
123             marker='.')
124
125plt.xlabel('Frequency (Hz)')
126plt.ylabel('Measured Data') 
127plt.title('Frequency vs Measured Data')
128plt.legend()
129plt.grid(True)
130plt.show()
131# %% ==========================================================================
132# Save Dataframe
133# =============================================================================
134# Save DataFrame to HDF5 (better than CSV)
135meas_df.to_hdf("measurements.h5", key="data", mode="w")
136# key="data" is like a "dataset name" inside the HDF5 file 
137# (you can store multiple DataFrames in one file with different keys).
138# mode="w" overwrites the file. Use mode="a" if you want to append new datasets.
139
140# Later: Load it back
141loaded_df = pd.read_hdf("measurements.h5", key="data")
142print(loaded_df.head())
143
144#or
145
146# Save DataFrame to CSV
147meas_df.to_csv("measurements.csv", index=False)
148
149# Load it back, auto-parsing the "Timestamps" column as datetime
150loaded_df = pd.read_csv("measurements.csv", parse_dates=["Timestamps"])
151print(loaded_df.head())
152
153# %% ==========================================================================
154# Close Instrument
155# =============================================================================
156SignalGenerator.Close()