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()