Commit c7e59874 authored by Peter Jansweijer's avatar Peter Jansweijer

fswp DAT file read script (phase noise)

parent 090018e7
#!/usr/bin/python
"""
RohdeSchwarz_FSWP Phase Noise plot
-------------------------------------------------------------------------------
Copyright (C) 2023 Nikhef, Peter Jansweijer
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>
--------------------------------------------------------------------------------------------------------------------------------------------------------------
Usage:
RohdeSchwarz_FSWP.py
RohdeSchwarz_FSWP.py -h | --help
-d <dir> directory that contains the FSWP Phase Noise "DAT" files to be plotted
-a print all traces
-t <string> custom title (default = Phase Noise)
Options:
-h --help Show this screen.
"""
import os
import sys
import numpy
import pdb
import matplotlib.pyplot as plt
plt.rcParams['lines.linewidth'] = 1
############################################################################
def check_traces(waveform_data):
"""
This function checks for the consistency of the captured waveform.
waveform_data -- <type 'dict'> waveform_data (as returned by function "file_to_waveform")
returns: number of points (of the first waveform found)
"""
first = True
for ch in waveform_data.keys():
if first== True:
first = False
channel = ch
points = waveform_data[ch]["preamble"]["points"]
print("Info: Record Length is", points,"samples")
count = waveform_data[ch]["preamble"]["count"]
x_inc = waveform_data[ch]["preamble"]["x_increment"]
print("Info: Sample Period is", x_inc)
timebase = waveform_data[ch]["preamble"]["x_display_range"]
else:
if waveform_data[ch]["preamble"]["points"] != points:
print("### WARNING! Different array length!")
print(" Channel:",channel,points)
print(" Channel:",ch,waveform_data[ch]["preamble"]["points"])
if waveform_data[ch]["preamble"]["count"] != count:
print("### WARNING! Different sweep counts per acquisition!")
print(" Channel:",channel,count)
print(" Channel:",ch,waveform_data[ch]["preamble"]["count"])
if waveform_data[ch]["preamble"]["x_increment"] != x_inc:
print("### WARNING! Different time base sample interval!")
print(" Channel:",channel,x_inc)
print(" Channel:",ch,waveform_data[ch]["preamble"]["x_increment"])
print(" You may want to check the 'Interpolation' setting in the 'pre-Processing' tab of the oscilloscopes channel setup")
if waveform_data[ch]["preamble"]["x_display_range"] != timebase:
print("### WARNING! Different time base!")
print(" Channel:",channel,timebase)
print(" Channel:",ch,waveform_data[ch]["preamble"]["x_display_range"])
return(points)
############################################################################
def file_to_phaseplot(filename):
"""
Retrieve the phaseplot from a bytestring which is normally read from file.
filename -- source file from which to retrieve data.
returns: <type 'dict'> phase noise trace data with keys to
"range" <type 'dict'> with two items
["start"] <float> start freqency
["stop"] <float> stop freqency
"trace" <type 'dict'> with 1 to 6 items
[1] <numpy.ndarray> ([x.y])
x <float> n elements Hz
y <float> n elements dBc/Hz
:
[6] <numpy.ndarray> ([x.y])
x <float> n elements Hz
y <float> n elements dBc/Hz
"points" <type 'dict'> with 1 to 6 items
[1] <int> number of data points for trace [1]
:
[6] <int> number of data points for trace [6]
"""
# Open data file for "read", in "binary" format
# Then readline() returns type <bytes> that can be
# decoded using "utf-8" into string
data_file = open(filename,"rb")
# create an empty wavefrom_data dictionairy
phasenoise_data = {}
line = data_file.readline().decode("utf-8")
if "FSWP-26" not in line:
print("Exception: " + filename + " is not a Rohde&Schwarz FSWP-26 file.")
Exception(filename + " is not a Rohde&Schwarz FSWP-26 file.")
data_file.close()
return
line = data_file.readline().decode("utf-8")
version = line.strip().split(";")
if not(("Version" in version[0]) and ("1.60" in version[1])):
# if version[0]=="#version" and version[1]=="0.1":
print("Exception: " + filename + " has a wrong version.")
Exception(filename + " has a wrong version.")
data_file.close()
return
search = []
freq_range = {}
trace = {}
points = {}
# Skip reading until Start and Stop frequencies
while not(line != "" and len(search) >= 2 and ("Start" in search[0]) and ("Hz" in search[2])):
line = data_file.readline().decode("utf-8")
search = line.strip().split(";")
freq_range["start"] = (float(search[1]))
while not(line != "" and len(search) >= 2 and ("Stop" in search[0]) and ("Hz" in search[2])):
line = data_file.readline().decode("utf-8")
search = line.strip().split(";")
freq_range["stop"] = (float(search[1]))
print(filename + " Start: " + str(freq_range["start"]) + "; Stop: " + str(freq_range["stop"]))
phasenoise_data["range"] = freq_range
# Read traces 1 to 6
for trace_nr in range(1,7):
# Skip reading until "Trace" with specific trace_nr
while (line != "" and not(len(search) >= 2 and ("Trace" in search[0]) and (str(trace_nr) in search[1]))):
line = data_file.readline().decode("utf-8")
search = line.strip().split(";")
# Skip reading lines towards "Values"
while (line != "" and not(len(search) >= 2 and ("Values" in search[0]))):
line = data_file.readline().decode("utf-8")
search = line.strip().split(";")
# No more "Values" found?
if line == "":
break
else:
nr_of_points = int(search[1])
print("Trace: " + str(trace_nr) + " Read points: " + str(nr_of_points))
x = []
y = []
for i in range(nr_of_points):
line = data_file.readline().decode("utf-8")
if line == "": break
search = line.strip().split(";")
x.append(float(search[0]))
y.append(float(search[1]))
trace[trace_nr] = numpy.array([x,y])
points[trace_nr] = nr_of_points
phasenoise_data["trace"] = trace
phasenoise_data["points"] = points
data_file.close()
return phasenoise_data
############################################################################
##
## If run from commandline, we can test the library
##
"""
Usage:
RohdeSchwarz_FSWP.py
RohdeSchwarz_FSWP.py -h | --help
-d <dir> directory that contains the FSWP Phase Noise "DAT" files to be plotted
-a print all traces
-t <string> custom title (default = Phase Noise)
Options:
-h --help Show this screen.
"""
if __name__ == "__main__":
import argparse
parser = argparse.ArgumentParser()
parser.add_argument("data_dir", help="the directory that contains FSWP-26 Phase Noise DAT files")
parser.add_argument("-a", help="Plot all traces in each file. Delault is plot only Trace-1", action='store_true')
parser.add_argument("-t", help="Custom tile for the plot", default="Phase Noise", type=str)
args = parser.parse_args()
data_dir = args.data_dir
only_trace1 = not(args.a)
plot_title = args.t
if not(os.path.isdir(data_dir)):
print(data_dir+": is not a directory.")
print("Provide the directory that contains FSWP-26 Phase Noise DAT files")
sys.exit()
lns = []
traces = plt.figure("data files in directory: "+ data_dir)
ax = traces.add_subplot(111)
ax.set_title(plot_title)
ax.set_xlabel("Offset [Hz]")
ax.set_ylabel(r"$\mathscr{L}$(f) [dBc/Hz]")
ax.grid(visible = True, which = 'major', axis = 'both', color = 'gray')
ax.grid(visible = True, which = 'minor', axis = 'both', color = 'gainsboro')
for filename in os.listdir(data_dir):
phasenoise_data = file_to_phaseplot(os.path.join(data_dir,filename))
if only_trace1:
x = phasenoise_data["trace"][1][0]
y = phasenoise_data["trace"][1][1]
trace = ax.semilogx(x, y, label = filename + str(" [1]"))
lns = lns + trace
else:
for trace_nr in phasenoise_data["trace"].keys():
x = phasenoise_data["trace"][trace_nr][0]
y = phasenoise_data["trace"][trace_nr][1]
trace = ax.semilogx(x, y, label = filename + str(" [") + str(trace_nr) + ("]"))
lns = lns + trace
labels=[l.get_label() for l in lns]
ax.legend(lns, labels, loc='upper right', fontsize='small')
plt.show()
sys.exit()
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment