Skip to content

Commit

Permalink
Fix TDR math again (#724)
Browse files Browse the repository at this point in the history
* Update TDR.py

* Update TDR.py
  • Loading branch information
EnPassant123 authored Nov 26, 2024
1 parent 56d6bf4 commit 561b1de
Show file tree
Hide file tree
Showing 2 changed files with 77 additions and 35 deletions.
18 changes: 15 additions & 3 deletions src/NanoVNASaver/Charts/TDR.py
Original file line number Diff line number Diff line change
Expand Up @@ -175,21 +175,33 @@ def isPlotable(self, x, y):

def _configureGraphFromFormat(self):
TDR_format = self.tdrWindow.format_dropdown.currentText()
if TDR_format == "|Z|":
if TDR_format == "|Z| (lowpass)":
self.minYlim = MIN_IMPEDANCE
self.maxYlim = MAX_IMPEDANCE
self.formatString = "impedance (\N{OHM SIGN})"
self.decimals = 1
elif TDR_format == "S11":
elif TDR_format == "S11 (lowpass)":
self.minYlim = MIN_S11
self.maxYlim = MAX_S11
self.formatString = "S11 (dB)"
self.decimals = 1
elif TDR_format == "VSWR":
elif TDR_format == "VSWR (lowpass)":
self.minYlim = MIN_VSWR
self.maxYlim = MAX_VSWR
self.formatString = "VSWR"
self.decimals = 2
elif TDR_format == "Refl (lowpass)":
self.minYlim = -1
self.maxYlim = 1
self.formatString = "U"
self.decimals = 2
elif TDR_format == "Refl (bandpass)":
self.minYlim = 0
self.maxYlim = 1
self.formatString = "U"
self.decimals = 2



def resetDisplayLimits(self):
self._configureGraphFromFormat()
Expand Down
94 changes: 62 additions & 32 deletions src/NanoVNASaver/Windows/TDR.py
Original file line number Diff line number Diff line change
Expand Up @@ -104,9 +104,11 @@ def __init__(self, app: QtWidgets.QWidget):
dropdown_layout.addWidget(self.tdr_velocity_dropdown)

self.format_dropdown = QtWidgets.QComboBox()
self.format_dropdown.addItem("|Z|")
self.format_dropdown.addItem("S11")
self.format_dropdown.addItem("VSWR")
self.format_dropdown.addItem("|Z| (lowpass)")
self.format_dropdown.addItem("S11 (lowpass)")
self.format_dropdown.addItem("VSWR (lowpass)")
self.format_dropdown.addItem("Refl (lowpass)")
self.format_dropdown.addItem("Refl (bandpass)")

self.format_dropdown.currentIndexChanged.connect(self.updateFormat)

Expand All @@ -132,6 +134,7 @@ def updateFormat(self):
def updateTDR(self):
# TODO: Let the user select whether to use high or low resolution TDR?
FFT_POINTS = 2**14
TDR_format = self.format_dropdown.currentText()

if len(self.app.data.s11) < 2:
return
Expand All @@ -158,37 +161,64 @@ def updateTDR(self):
s11 = [complex(d.re, d.im) for d in self.app.data.s11]

s11 = np.array(s11)
s11 = np.concatenate(
[s11, np.conj(s11[-1:0:-1])]
) # Include negative frequencies
s11 = np.fft.fftshift(s11)

# In lowpass mode, the frequency is measured down to DC. Because the
# impulse response is real, we can flip over the frequency data so
# the output of the IFFT is a real signal.
#
# In bandpass mode, the low frequency information is missing, so we
# can't flip the frequency data. We need to keep everything complex.
# We are only able to determine the magnitude of the impulse
# response in this mode.


if "lowpass" in TDR_format:
s11 = np.concatenate(
[s11, np.conj(s11[-1:0:-1])]
) # Include negative frequencies
s11 = np.fft.fftshift(s11)

window = np.blackman(len(s11))
windowed_s11 = (
window * s11
) # Now windowing eliminates higher frequencies while leaving low frequencies untouched

pad_points = (FFT_POINTS - len(windowed_s11)) // 2
windowed_s11 = np.pad(
windowed_s11, [pad_points + 1, pad_points]
) # Pad array to length FFT_POINTS
windowed_s11 = np.fft.ifftshift(windowed_s11)

td = np.fft.ifft(windowed_s11)
step = np.ones(FFT_POINTS)
step_response = convolve(td, step)
# calculate step response based on the format that the user selected
TDR_format = self.format_dropdown.currentText()
step_Z = 50 * (1 + step_response) / (1 - step_response)
step_refl_coefficient = np.abs((step_Z - 50) / (step_Z + 50))
if TDR_format == "|Z|":
self.step_response_Z = np.abs(step_Z)
elif TDR_format == "S11":
self.step_response_Z = 20 * np.log10(step_refl_coefficient)
elif TDR_format == "VSWR":
self.step_response_Z = np.abs(
(1 + step_refl_coefficient) / (1 - step_refl_coefficient)
)
windowed_s11 = (window * s11)

if "lowpass" in TDR_format:
pad_points = (FFT_POINTS - len(windowed_s11)) // 2
windowed_s11 = np.pad(
windowed_s11, [pad_points + 1, pad_points]
) # Pad array to length FFT_POINTS
windowed_s11 = np.fft.ifftshift(windowed_s11)

td = np.fft.ifft(windowed_s11)
step = np.ones(FFT_POINTS)
step_response = convolve(td, step)
step_response_rev = convolve(td[::-1], step)

#This fixes the issue with the impedance being wrong when the length is zero
step_response = step_response + step_response_rev

# calculate step response based on the format that the user selected
step_Z = 50 * (1 + step_response) / (1 - step_response)
step_refl_coefficient = np.abs((step_Z - 50) / (step_Z + 50))
if TDR_format == "|Z| (lowpass)":
self.step_response_Z = np.abs(step_Z)
elif TDR_format == "S11 (lowpass)":
self.step_response_Z = 20 * np.log10(step_refl_coefficient)
elif TDR_format == "VSWR (lowpass)":
self.step_response_Z = np.abs(
(1 + step_refl_coefficient) / (1 - step_refl_coefficient)
)
elif TDR_format == "Refl (lowpass)":
# The 1/0.42 is the Amplitude Correction Factor for the
# Blackman window. 0.42 is the average amplitude of the
# window across its range.
self.step_response_Z = np.real(td * FFT_POINTS / len(s11) * 1/0.42)
else:
td = np.abs(np.fft.ifft(windowed_s11, FFT_POINTS))
# Convolving with a step function is unnecessary, we can only get
# the magnitude of impulse response
if TDR_format == "Refl (bandpass)":
self.step_response_Z = td * FFT_POINTS / len(s11) * 1/0.42

time_axis = np.linspace(0, 1 / step_size, FFT_POINTS)
self.distance_axis = time_axis * v * speed_of_light
# peak = np.max(td)
Expand Down

0 comments on commit 561b1de

Please sign in to comment.