@@ -36,6 +36,23 @@ def get_spectrum(data, fs=1, nfft=2**12, single_sided=True):
36
36
return (freq_ds , psd_ds * fs / nfft )
37
37
38
38
39
+ def window_data (data , window = "rectangular" ):
40
+ """Applies a window to the time-domain data."""
41
+ wsize = data .size
42
+ windows = {
43
+ "rectangular" : (np .ones (wsize ), 1.0 ),
44
+ "hanning" : (np .hanning (wsize ), 1.633 )
45
+ }
46
+
47
+ if window not in windows :
48
+ print (f"WARNING: { window } not implemented. Defaulting to 'rectangular'." )
49
+ window = "rectangular"
50
+
51
+ wscale = windows [window ][1 ]
52
+
53
+ return data * windows [window ][0 ] * wscale
54
+
55
+
39
56
def plot_spectrum (
40
57
data ,
41
58
fs = 1 ,
@@ -50,11 +67,6 @@ def plot_spectrum(
50
67
fscale = "MHz" ,
51
68
):
52
69
"""Plot Power Spectrum for input signal."""
53
- wsize = data .size
54
- windows = {
55
- "rectangular" : np .ones (wsize ),
56
- "hanning" : np .hanning (wsize ),
57
- }
58
70
59
71
fscalar = {
60
72
"uHz" : 1e-6 ,
@@ -65,25 +77,18 @@ def plot_spectrum(
65
77
"GHz" : 1e9 ,
66
78
"THz" : 1e12 ,
67
79
}
68
-
69
- if window not in windows :
70
- print (f"WARNING: { window } not implemented. Defaulting to 'rectangular'." )
71
- window = "rectangular"
72
-
73
80
if fscale not in fscalar :
74
81
print (f"WARNING: { fscale } not implemented. Defaulting to 'MHz'." )
75
82
fscale = "MHz"
76
83
77
- wscale = {
78
- "rectangular" : 1.0 ,
79
- "hanning" : 1.633 ,
80
- }[window ]
81
-
82
- (freq , pwr ) = get_spectrum (
83
- data * windows [window ] * wscale , fs = fs , nfft = nfft , single_sided = single_sided
84
- )
84
+ # Window the data and get the single or dual-sided spectrum
85
+ wdata = window_data (data , window = window )
86
+ (freq , pwr ) = get_spectrum (wdata , fs = fs , nfft = nfft , single_sided = single_sided )
87
+
88
+ # Calculate the fullscale range of the spectrum in Watts
85
89
full_scale = calc .dBW (dr ** 2 / 8 )
86
90
91
+ # Determine what y-axis scaling to use
87
92
yaxis_lut = {
88
93
"power" : [0 , "dB" ],
89
94
"fullscale" : [full_scale , "dBFS" ],
@@ -102,10 +107,14 @@ def plot_spectrum(
102
107
)
103
108
print (" Defaulting to Hz." )
104
109
110
+ # Convert to dBW and perform scalar based on y-axis scaling input
105
111
psd_out = calc .dBW (pwr , places = 3 ) - scalar
112
+
113
+ # Use Watts if magnitude y-axis scaling is desired
106
114
if lut_key in ["magnitude" ]:
107
115
psd_out = pwr
108
116
117
+ # Get single-sided spectrum for consistent SNDR and harmonic calculation behavior
109
118
f_ss = freq
110
119
psd_ss = pwr
111
120
if not single_sided :
@@ -115,7 +124,6 @@ def plot_spectrum(
115
124
)
116
125
117
126
sndr_stats = calc .sndr_sfdr (psd_ss , f_ss , fs , nfft , leak = leak , full_scale = full_scale )
118
-
119
127
harm_stats = calc .find_harmonics (
120
128
psd_ss ,
121
129
f_ss ,
@@ -127,10 +135,13 @@ def plot_spectrum(
127
135
fscale = xscale ,
128
136
)
129
137
138
+ # Merge the two stat dictionaries into one for convenient access
130
139
stats = {** sndr_stats , ** harm_stats }
131
140
141
+ # Change the x-axis minimum value based on single or dual-sided selection
132
142
xmin = 0 if single_sided else - fs / 2e6
133
143
144
+ # If plotting, prep plot and generate all required axis strings
134
145
if not no_plot :
135
146
plt_str = calc .get_plot_string (stats , full_scale , fs , nfft , window , xscale , fscale )
136
147
fig , ax = plt .subplots (figsize = (15 , 8 ))
0 commit comments