def CheckWaveformResult(self, wf, fileName, text):
        #path=os.getcwd()
        #os.chdir(os.path.dirname(os.path.realpath(__file__)))
        if not os.path.exists(fileName):
            wf.WriteToFile(fileName)
            self.assertTrue(False, fileName + ' not found')
        regression = Waveform().ReadFromFile(fileName)
        wfsAreEqual = (regression == wf)
        if not wfsAreEqual:
            if ResponseTesterHelper.plotErrors:
                import matplotlib.pyplot as plt
                plt.clf()
                plt.title(fileName)
                plt.xlabel('time (s)')
                plt.ylabel('amplitude')
                plt.semilogy(regression.Times(), [
                    abs(wf[k] - regression[k]) for k in range(len(regression))
                ])
                plt.grid(True)
                plt.show()

                plt.clf()
                plt.title(fileName)
                plt.xlabel('time (s)')
                plt.ylabel('amplitude')
                plt.plot(wf.Times(), wf.Values(), label='calculated')
                plt.plot(regression.Times(),
                         regression.Values(),
                         label='regression')
                plt.legend(loc='upper right')
                plt.grid(True)
                plt.show()
        #os.chdir(path)
        self.assertTrue(wfsAreEqual, text + ' incorrect')
 def __init__(self,
              td,
              Amplitude=1.,
              StartTime=0.,
              PulseWidth=0.,
              Risetime=0.):
     """Constructor  
     constructs a waveform with a single pulse.
     @param td instance of class TimeDescriptor containing time axis of waveform.
     @param Amplitude (optional) float amplitude of pulse (defaults to unity).
     @param StartTime (optional) float starting time of the pulse (defaults to zero).
     @param PulseWidth (optional) float the width of the pulse (defaults to zero).
     @param Risetime (optional) float risetime in seconds (defaults to 0.)
     @note The amplitude can be positive or negative, with negative providing a negative
     pulse.
     @note if the pulse appears entirely within the samples, then the waveform will be all zero.
     @note actual risetime used is the sample period if less than that is specified
     @note risetime is applied on the rising and falling edge.  risetimes greater than
     1.72 times the pulse width will diminish the pulse amplitude.
     @note the risetime is applied such that the pulse reaches half amplitude at the start
     time specified.  Please note the expected non-causality.
     """
     StopTime = StartTime + PulseWidth
     stepup = StepWaveform(td, Amplitude, StartTime, Risetime)
     stepdown = StepWaveform(td, Amplitude, StopTime, Risetime)
     Waveform.__init__(
         self, td, [stepup[k] - stepdown[k] for k in range(len(stepup))])
示例#3
0
 def __init__(self, td, Amplitude=1., StartTime=0., risetime=0.):
     """Constructor  
     constructs a step waveform.
     @param td instance of class TimeDescriptor containing time axis of waveform.
     @param Amplitude (optional) float amplitude of step (defaults to unity).
     @param StartTime (optional) float starting time of the pulse (defaults to zero).
     @param risetime (optional) float risetime in seconds (defaults to 0.)
     @note The amplitude can be positive or negative, with negative providing a negative
     pulse.
     @note The step starts at the first sample point after the start time specified.
     @note actual risetime used is the sample period if less than that is specified
     @note the risetime is applied such that the step reaches half amplitude at the start
     time specified.  Please note the expected non-causality.
     """
     x = [0 if t < StartTime else Amplitude for t in td.Times()]
     T = risetime / self.rtvsT
     rcStart = max(0, td.IndexOfTime(StartTime - T / 2.))
     if td.TimeOfPoint(rcStart) < StartTime - T / 2:
         rcStart = min(rcStart + 1, len(td) - 1)
     rcEnd = min(len(td) - 1, td.IndexOfTime(StartTime + T / 2.))
     if td.TimeOfPoint(rcEnd) > StartTime + T / 2: rcEnd = max(rcEnd - 1, 0)
     for i in range(rcStart, rcEnd + 1):
         try:
             x[i]=Amplitude*\
                 (math.sin((td.TimeOfPoint(i)-StartTime)/T*math.pi)+1.)/2.
         except ZeroDivisionError:
             pass
     Waveform.__init__(self, td, x)
示例#4
0
    def __init__(self,
                 td,
                 Amplitude=1.,
                 Frequency=1e6,
                 Phase=0.,
                 StartTime=-100.,
                 StopTime=100.):
        """Constructor

        constructs a sinewave waveform.

        @param td instance of class TimeDescriptor containing time axis of waveform.
        @param Amplitude (optional) float amplitude of sine wave (defaults to unity).
        @param Frequency (optional) float frequency of sine wave (defaults to 1 MHz).
        @param Phase (optional) float phase of sine wave in degrees (defaults to zero).
        @param StartTime (optional) float start time of sine wave (defaults to -100 s).
        @param StopTime (optional) float stop time of the sine wave (defaults to 100 s).
        """
        x = [
            Amplitude *
            math.sin(2. * math.pi * Frequency * t + Phase / 180. * math.pi)
            for t in td.Times()
        ]
        sw = Waveform(td, x) * PulseWaveform(
            td,
            Amplitude=1.,
            StartTime=StartTime,
            PulseWidth=max(StartTime, StopTime) - min(StartTime, StopTime),
            Risetime=0.)
        Waveform.__init__(self, sw.td, sw.Values())
示例#5
0
 def Waveform(self, td=None):
     """Computes the time-domain waveform using IDFT methods
     @param td (optional) instance of class TimeDescriptor declaring the time descriptor of the waveform to produce.
     @return wf instance of class Waveform corresponding to the frequency content.
     @note
     If td is None then the time descriptor corresponding to the frequency descriptor is used.\n
     The waveform produced is essentially the inverse process of class initialization.\n
     @see WaveformFromDefinition()
     """
     Keven = (self.td.K // 2) * 2 == self.td.K
     X = self.Values()
     X=[X[n]*self.td.K*\
         (1. if (n==0 or ((n==self.m_f.N) and Keven)) else 0.5)*\
         cmath.exp(1j*2.*math.pi*self.m_f[n]*self.td.H)
         for n in range(self.m_f.N+1)]
     if Keven:
         X2 = [X[self.m_f.N - n].conjugate() for n in range(1, self.m_f.N)]
     else:
         X2 = [
             X[self.m_f.N - n + 1].conjugate()
             for n in range(1, self.m_f.N + 1)
         ]
     X.extend(X2)
     x = [xk.real for xk in fft.ifft(X).tolist()]
     wf = Waveform(self.td, x)
     if not td is None:
         wf = wf.Adapt(td)
     return wf
 def __init__(self,pattern,bitRate,amplitude=1.0,risetime=0.,delay=0.,tdOrFs=None):
     """constructor
     @param pattern list of bits in pattern which must be 0 or 1
     @param bitrate float bitrate in bits/s
     @param amplitude (optional, defaults to 1.0) float amplitude of zero centered serial data waveform (pk-pk amplitude is twice this value)
     @param risetime (optional, defaults to 0.0) float risetime of a 1/2 cosine response.
     @param delay (optional, defaults to 0,0) float delay in time into the pattern.
     @param tdOrFs (optional, defaults to None) time descriptor or float sample rate.  If None is specified, then
     the descriptor is generated for one pattern length with a sample rate of 10x the bitrate.  Otherwise, if a float
     is specified, then a time descriptor for one pattern length is generated with the sample rate specified.  Otherwise
     the time descriptor specified is used.
     @return self, a waveform.
     @throw SignalIntegrityExceptionWaveform exception is raised if the risetime exceeds 59% of the unit interval as this causes
     the calculation to fail.
     @todo the failure for a given risetime is due to the simplicity of the algorithm which should be improved at some point.
     this simplicity is that it only looks at adjacent bits to determine the intersymbol effect of the risetime.
     @note the left edge of the first bit in the pattern is at the delay time.  But because a cosine is used for
     the risetime emulation, the 50% point of the cosine is reached at this edge.
     @note the risetime is adjusted if it is too low relative to the sample period.
     """
     if tdOrFs is None:
         sampleRate=10.*bitRate
         td=self.TimeDescriptor(bitRate, sampleRate, len(pattern))
     elif isinstance(tdOrFs,float):
         sampleRate=tdOrFs
         td=self.TimeDescriptor(bitRate, sampleRate, len(pattern))
     else:
         td=tdOrFs
     T=max(risetime/self.rtvsT,1/td.Fs)
     patternTimeLength=len(pattern)/bitRate
     unitInterval=1./bitRate
     if T>unitInterval:
         # todo: would like to handle this case in the future
         raise SignalIntegrityExceptionWaveform('PRBS risetime too high\nfor waveform generation. '+\
                'Limit is '+ToSI(unitInterval*self.rtvsT,'s')+' for given bitrate')
     v=[0. for _ in range(len(td))]
     for k in range(len(td)):
         t=td[k]
         timeInPattern=((t-delay)%patternTimeLength+patternTimeLength)%patternTimeLength
         thisBit=int(math.floor(timeInPattern/unitInterval))
         timeInBit=timeInPattern-thisBit*unitInterval
         thisBitValue=pattern[thisBit]
         if timeInBit>=T/2. and (timeInBit-unitInterval)<=-T/2.:
             v[k]=2.*amplitude*(thisBitValue-0.5)
         elif timeInBit<T/2.:
             previousBit=(thisBit-1+len(pattern))%len(pattern)
             previousBitTransition=thisBitValue-pattern[previousBit]
             if previousBitTransition==0:
                 v[k]=2.*amplitude*(thisBitValue-0.5)
             else:
                 v[k]=amplitude*previousBitTransition*math.cos(math.pi*(timeInBit/T+3./2.))
         elif (timeInBit-unitInterval)>-T/2.:
             nextBit=(thisBit+1)%len(pattern)
             nextBitTransition=pattern[nextBit]-thisBitValue
             if nextBitTransition==0:
                 v[k]=2.*amplitude*(thisBitValue-0.5)
             else:
                 v[k]=amplitude*nextBitTransition*math.cos(math.pi*((timeInBit-unitInterval)/T+3./2.))
     Waveform.__init__(self,Waveform(td,v))
 def __init__(self, td, sigma, mean=0.0):
     """Constructor  
     constructs a waveform with mean and normally distributed noise.
     @param td instance of class TimeDescriptor containing time axis of waveform.
     @param sigma float non-zero value of the rms value of the noise
     @param mean (optional) float containing the mean value of the waveform
     """
     Waveform.__init__(self, td,
                       numpy.random.normal(mean, sigma, int(td.K)).tolist())
 def __init__(self, t=None, td=None):
     """constructor
     @param t instance of class TimeDescriptor describing time-axis of impulse response
     @param td list of float representing time-domain values
     @remark note that t can be an instance of waveform and td None which causes the impulse response
     to be instantiated dircectly from a waveform.
     """
     if isinstance(t, Waveform):
         Waveform.__init__(self, t.td, t)
     else:
         Waveform.__init__(self, t, td)
示例#9
0
    def __init__(self,td,Amplitude=1.,StartTime=0.):
        """Constructor

        constructs a step waveform.

        @param td instance of class TimeDescriptor containing time axis of waveform.
        @param Amplitude (optional) float amplitude of step (defaults to unity).
        @param StartTime (optional) float starting time of the pulse (defaults to zero).

        @note The amplitude can be positive or negative, with negative providing a negative
        pulse.
        @note The step starts at the first sample point after the start time specified.
        """
        x=[0 if t < StartTime else Amplitude for t in td.Times()]
        Waveform.__init__(self,td,x)
 def GetWaveformResult(self, fileName):
     #path=os.getcwd()
     #os.chdir(os.path.dirname(os.path.realpath(__file__)))
     if not os.path.exists(fileName):
         return None
     regression = Waveform().ReadFromFile(fileName)
     #os.chdir(path)
     return regression
示例#11
0
 def DecimateWaveform(self,wf):
     """decimates a waveform
     @param wf instance of class Waveform of waveform to be decimated
     @return intance of class Waveform the decimated waveform
     """
     # pragma: silent exclude
     from SignalIntegrity.Lib.TimeDomain.Waveform.Waveform import Waveform
     # pragma: include       
     td=wf.td*self
     return Waveform(td,[wf[k*self.df+self.dph] for k in range(td.K)])
示例#12
0
    def __init__(self, td, Amplitude=1., StartTime=0., PulseWidth=0):
        """Constructor

        constructs a waveform with mean and normally distributed noise.

        @param td instance of class TimeDescriptor containing time axis of waveform.
        @param Amplitude (optional) float amplitude of pulse (defaults to unity).
        @param StartTime (optional) float starting time of the pulse (defaults to zero).
        @param PulseWidth (optional) float the width of the pulse (defaults to zero).

        @note The amplitude can be positive or negative, with negative providing a negative
        pulse.
        @note if the pulse appears entirely within the samples, then the waveform will be all zero.
        """
        StopTime = StartTime + PulseWidth
        stepup = StepWaveform(td, Amplitude, StartTime)
        stepdown = StepWaveform(td, Amplitude, StopTime)
        Waveform.__init__(
            self, td, [stepup[k] - stepdown[k] for k in range(len(stepup))])
示例#13
0
def from_trc(filename):
    """Read a waveform in lecroy trc format
    @param filename String name of the filename to read.  Should have a .trc extension
    @return a waveform in SignalIntegrity format
    """
    try:
        fname, file_extension = os.path.splitext(filename)
        extensionCorrect = True
        if file_extension == '' or file_extension is None:
            filename = filename + '.trc'
        else:
            file_extension = file_extension.lower()
            if file_extension != '.trc':
                extensionCorrect = False
        if not extensionCorrect:
            raise SignalIntegrityExceptionWaveformFile(
                'incorrect extension LeCroy trace file name in ' + filename +
                '.  Should be .trc.')
    except:
        raise SignalIntegrityExceptionWaveformFile(
            'incorrect extension in LeCroy trace file name in ' + filename +
            '.  Should be .trc.')
    try:
        with open(filename, "rb") as f:
            header = f.read(11).decode()
            start = header[0:2]
            if start != '#9':
                raise SignalIntegrityExceptionWaveformFile(
                    'Incorrect LeCroy trace file header in: ' + filename)
            count = int(header[2:11])
            descData = np.frombuffer(f.read(346), np.dtype(WFM_DESC))
            wfBuffer = np.frombuffer(f.read(count - 346), 'int16')
            if descData["NOM_SUBARRAY_COUNT"] != 1:
                raise SignalIntegrityExceptionWaveformFile(
                    'Cannot read multi-segment waveform in: ' + filename)
            vertScale = descData['VERTICAL_GAIN'][0]
            vertOffset = -descData['VERTICAL_OFFSET'][0]
            horizontalOffset = descData['HORIZ_OFFSET'][0]
            sampleRate = 1. / descData['HORIZ_INTERVAL'][0]
            Exp = 10**round(math.log10(sampleRate), 6)
            sampleRate = sampleRate / Exp
            sampleRate = round(sampleRate, 6)
            sampleRate = sampleRate * Exp
            numPoints = descData['WAVE_ARRAY_COUNT'][0]
            from SignalIntegrity.Lib.TimeDomain.Waveform.Waveform import Waveform
            from SignalIntegrity.Lib.TimeDomain.Waveform.TimeDescriptor import TimeDescriptor
            wf = Waveform(
                TimeDescriptor(horizontalOffset, numPoints, sampleRate),
                [v * vertScale + vertOffset for v in wfBuffer])
            return wf
    except:
        raise SignalIntegrityExceptionWaveformFile(
            'LeCroy trace file could not be read: ' + filename)
示例#14
0
 def TrimWaveform(self, wf):
     """trim a waveform
     @param wf instance of class Waveform of waveform to trim
     @return instance of class Waveform containing trimmed waveform
     """
     # pragma: silent exclude
     from SignalIntegrity.Lib.TimeDomain.Waveform.Waveform import Waveform
     # pragma: include
     K = wf.td.K
     TL = self.TrimLeft()
     TT = self.TrimTotal()
     return Waveform(
         wf.td * self,
         [wf[k + TL] if 0 <= k + TL < K else 0. for k in range(K - TT)])
示例#15
0
 def WaveformFromDefinition(self, td=None):
     """Computes the time-domain waveform using sums of cosines
     @param td instance of class TimeDescriptor declaring the time descriptor of the waveform to produce.
     @return wf instance of class Waveform corresponding to the frequency content.
     @note
     If td is None then the time descriptor corresponding to the frequency descriptor is used.\n
     The waveform produced is essentially the inverse process of __init__().\n
     This function should produce the exact same result as the Waveform() method, and is slow, but clearly
     written out to see how the waveform is produced by summing sinusoids.  It used to essentially document
     the class.\n
     @see Waveform().
     """
     absX = self.Values('mag')
     theta = self.Values('deg')
     wf = Waveform(self.td)
     for n in range(self.m_f.N + 1):
         wf = wf + SineWaveform(self.td,
                                Frequency=self.m_f[n],
                                Amplitude=absX[n],
                                Phase=theta[n] + 90)
     if not td is None:
         wf = wf.Adapt(td)
     return wf
示例#16
0
 def FilterWaveform(self,wf):
     """filters a waveform
     @param wf instance of class Waveform
     @return instance of class Waveform containing this filter applied to wf
     """
     # pragma: silent exclude
     from SignalIntegrity.Lib.TimeDomain.Waveform.Waveform import Waveform
     # pragma: include
     td = wf.td*self.FilterDescriptor()
     # pragma: silent exclude
     #filteredwf=PySIConvolve(wf.Values(),self.FilterTaps())
     # pragma: include
     filteredwf=convolve(wf.Values(),self.FilterTaps(),'valid').tolist()
     return Waveform(td,filteredwf)
    def __init__(self,
                 filename,
                 wfProjName=None,
                 normalizedDCGain=None,
                 multiplyByTs=True,
                 derivative=False):
        """Constructor
        @param filename string file name of s-parameter file to read.
        @param normalizedDCGain (optional, defaults to None) DC gain to normalize response to.
        @param multiplyByTs (optional, defaults to True) whether to multiply the waveform by the sample period
        @param derivative (optional, defaults to False) whether to use the derivative of the waveform

        Reads the waveform file and produces SParameters.

        The s-parameters are for a two-port device that is "amplifier-like", meaning it has infinite
        input impedance, zero output impedance, the impulse response forms s21, and has infinite reverse
        isolation.

        If normalizedDCGain is 0 or None, the DC gain is not normalized, otherwise the filter is normalized by setting
        the sum of the values in the impulse response equal to this gain value.

        if multiplyByTs is True, it assumes that the impulse response waveform is an ordinary waveform, and the impulse
        response is formed by multiplying every value by the sample period.

        if derivative is True, the derivative of the waveform is taken.  This is useful when you are provided a step
        response.
        """
        ext = str.lower(filename).split('.')[-1]
        if ext == 'si':
            from SignalIntegrity.App.SignalIntegrityAppHeadless import ProjectWaveform
            wf = ProjectWaveform(filename, wfProjName)
            if wf == None:
                raise SignalIntegrityExceptionWaveformFile(
                    'waveform could not be produced by ' + filename)
        else:
            try:
                wf = Waveform().ReadFromFile(filename)
            except:
                raise SignalIntegrityExceptionWaveformFile(
                    'waveform could not be produced by ' + filename)
        if derivative:
            wf = wf.Derivative()
        if multiplyByTs:
            wf = wf * (1. / wf.TimeDescriptor().Fs)
        if not ((normalizedDCGain == None) or (normalizedDCGain == 0)):
            wf = wf * (1. / sum(wf))
        fr = ImpulseResponse(wf).FrequencyResponse()
        fl = fr.Frequencies()
        SParameters.__init__(self, fl, [[[1., 0], [2. * r, -1]] for r in fr])
示例#18
0
 def FilterWaveform(self, wf):
     """overloads base class FilterWaveform
     @param wf instance of class Waveform
     @return instance of class Waveform containing the upsampled, interpolated wf
     @remark
     This method first classically upsamples the waveform by inserting zeros
     between the samples and then passes the upsampled waveform through the sinx/x
     interpolation filter.
     """
     # pragma: silent exclude
     from SignalIntegrity.Lib.TimeDomain.Waveform.Waveform import Waveform
     # pragma: include
     fd = self.FilterDescriptor()
     us = [0. for k in range(len(wf) * fd.U)]
     for k in range(len(wf)):
         us[k * fd.U] = wf[k]
     return FirFilter.FilterWaveform(self, Waveform(wf.td, us))
 def DenoisedWaveform(wf, pct=30., mult=5., isDerivative=True):
     """
     Denoises a waveform.
     @details The noise is estimated by the amount of noise in the last pct percent of the
     last scale of the wavelet transform.  The threshold is set as a multiplier on
     the noise, generally 5 is used.  Thus wavelets more than 5 times the noise floor
     are kept, while all others are deleted (this is called hard-thresholding).\n
     Usually this algorithm is used to denoise TDR waveforms, which are usually steps
     and the denoising is performed on the derivative of the step waveform.  Therefore,
     the noise threshold needs to be shaped based on the effect of the derivative on
     the noise shape.  For impulsive waveforms, no derivative is performed and
     isDerivative is set to False, causing a white noise threshold to be assumed.
     @param wf instance of class Waveform to be denoised.
     @param pct (optional) float percentage (defaults to 30).
     @param mult (optional) float noise multiplier for threshold (defaults to 5).
     @param isDerivative (optional) bool whether the waveform is a derivative
     (defaults to True).
     @return the denoised waveform.
     @remark the algorithm assumes there is no signal in the last pct percent
     of the last scale of the dwt
     @see Wavelet
     """
     w = WaveletDenoiser.wavelet
     Ki = wf.td.K
     Kf = int(pow(2, math.ceil(log2(wf.td.K))))
     PadLeft = Kf - Ki
     pct = pct * Ki / Kf
     pwf = wf * WaveformTrimmer(-PadLeft, 0)
     X = w.DWT(pwf.Values())
     T = WaveletDenoiser.DerivativeThresholdCalc(X, w.h, pct, isDerivative)
     # pragma: silent exclude
     #         import matplotlib.pyplot as plt
     #         plt.clf()
     #         plt.title('denoising')
     #         plt.loglog([k for k in range(len(X))],[abs(x) for x in X],label='dwt')
     #         plt.loglog([k for k in range(len(X))],[t*mult for t in T],label='threshold')
     #         plt.xlabel('samples')
     #         plt.ylabel('amplitude')
     #         plt.legend(loc='upper right')
     #         plt.grid(True)
     #         plt.show()
     # pragma: include
     dwf = Waveform(
         pwf.td,
         w.IDWT([0 if abs(x) < t * mult else x for (x, t) in zip(X, T)]))
     return dwf * WaveformTrimmer(PadLeft, 0)
 def __init__(self,
              sp,
              port=1,
              method='exact',
              align='middle',
              includePortZ=True,
              adjustForDelay=True):
     """Constructor
     @param sp instance of class SParameters of the device
     @param port (optional) integer 1 based port number (defaults to port 1)
     @param method (optional) string method for computation (defaults to 'exact')
     @param align (optional) string alignment of impedancance in waveform (defaults to 'middle')
     @param includePortZ (optional) boolean whether to put the port reference impedance as the first point. (defaults to True)
     @param adjustForDelay (optional) boolean whether to adjust for the delay in the impulse response (defaults to True)
     @remark computation methods include:
     'exact' (default) - calculates  using reflection coefficient of first point computed from DFT and deembedding.
     (this method takes longer and can diverge due to buildup of numerical inaccuracies.)
     'estimated' - calculates the reflection coefficients directly from the step response.
     'approximate' - calculates the reflection coefficients from the step response using an approximation.
     @remark alignment methods include:
     'middle' (default) - shows the impedance in the middle of the sample period distance along the line.
     'front' - shows the impedance measured at the front of the line. 
     """
     tdsp = sp.m_f.TimeDescriptor()
     # assumes middle and no portZ
     tdip = TimeDescriptor(1. / (tdsp.Fs * 4), tdsp.K / 2, tdsp.Fs * 2)
     if not align == 'middle':
         tdip.H = tdip.H - 1. / (tdsp.Fs * 4)
     if method == 'exact':
         ip = ImpedanceProfile(sp, tdip.K, port)
         Z = ip.Z()
         delayAdjust = ip.m_fracD
     elif method == 'estimated' or method == 'approximate':
         fr = sp.FrequencyResponse(port, port)
         rho = fr.ImpulseResponse().Integral(addPoint=True, scale=False)
         delayAdjust = fr._FractionalDelayTime()
         finished = False
         for m in range(len(rho)):
             if finished:
                 rho[m] = rho[m - 1]
                 continue
             rho[m] = max(-self.rhoLimit, min(self.rhoLimit, rho[m]))
             if abs(rho[m]) == self.rhoLimit:
                 finished = True
         if method == 'estimated':
             Z = [
                 max(
                     0.,
                     min(
                         sp.m_Z0 * (1 + rho[tdsp.K // 2 + 1 + k]) /
                         (1 - rho[tdsp.K // 2 + 1 + k]), self.ZLimit))
                 for k in range(tdip.K)
             ]
         else:
             Z = [
                 max(
                     0.,
                     min(sp.m_Z0 + 2 * sp.m_Z0 * rho[tdsp.K // 2 + 1 + k],
                         self.ZLimit)) for k in range(tdip.K)
             ]
     if includePortZ:
         tdip.H = tdip.H - 1. / (tdsp.Fs * 2)
         tdip.K = tdip.K + 1
         Z = [sp.m_Z0] + Z
     if adjustForDelay: tdip.H = tdip.H + delayAdjust / 2
     Waveform.__init__(self, tdip, Z)