def _Pad(self, P):
        """Pads the impulse response

        The impulse response is padded to P points with an equal number of points before and after
        time zero.

        @param P integer number of points to pad the impulse response to.
        @return an instance of class ImpulseResponse with the zero padded points.
        @attention P must be even - not checked - it must add equal points to the left
            and right of the impulse response.
        @note
        if K is the number of points in the selfs frequency response then:

        | P and K  | outcome
        |:--------:|:--------------------------------------------:|
        | P==K     | the original response is returned            |
        | P<K      | the response is truncated to P time points   |
        | P>K      | the response is zero padded to P time points |
        """
        K = len(self)
        if P == K: x = self.Values()
        elif P < K:
            x = [self[k] for k in range((K - P) // 2, K - (K - P) // 2)]
        else:
            x = [0 for p in range((P - K) // 2)]
            x = x + self.Values() + x
        td = self.td
        return ImpulseResponse(
            TimeDescriptor(td.H - (P - K) / 2. / td.Fs, P, td.Fs), x)
    def TrimToThreshold(self, threshold):
        """truncates an impulse response, keeping only the values above or equal to specified threshold.

        This is useful for reducing computational complexity in processing.

        @param threshold float threshold to apply to the values in the waveform.
        @attention the threshold specified is a fraction of the maximum absolute value of the values
        in the waveform.
        @note that absolute values are utilized in value/threshold comparison.
        @note the trimming is performed by finding the lowest and highest time value above or equal
        to the threshold - all values between these times are retained.
        """
        x = self.Values()
        td = self.td
        maxabsx = max(self.Values('abs'))
        minv = maxabsx * threshold
        for k in range(len(x)):
            if abs(x[k]) >= minv:
                startidx = k
                break
        for k in range(len(x)):
            ki = len(x) - 1 - k
            if abs(x[ki]) >= minv:
                endidx = ki
                break
        if (endidx - startidx + 1) // 2 * 2 != endidx - startidx + 1:
            # the result would not have an even number of points
            if endidx < len(x) - 1:
                # keep a point at the end if possible
                endidx = endidx + 1
            elif startidx > 0:
                # keep a point at the beginning if possible
                startidx = startidx - 1
            else:
                # append a zero to the end and calculate number of
                # points with endidx+1
                return ImpulseResponse(
                    TimeDescriptor(td[startidx], (endidx + 1) - startidx + 1,
                                   td.Fs),
                    [x[k] for k in range(startidx, endidx + 1)] + [0.])
        return ImpulseResponse(
            TimeDescriptor(td[startidx], endidx - startidx + 1, td.Fs),
            [x[k] for k in range(startidx, endidx + 1)])
Example #3
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)
 def TimeDescriptor(self, Keven=True):
     """associated time descriptor
     @param Keven boolean (optional)
     Whether N is from an even K points in the time domain (i.e. K/2) or from an odd K points in the time-domain
     (i.e. (K+1)/2).  Defaults to True.
     @return an instance of class TimeDescriptor that corresponds to the time descriptor that would
     generate this frequency descriptor.
     @note this is assumed to be a time descriptor that would produce a frequency descriptor with self's
     end frequency and number of points.  It does not check whether the list is evenly spaced."""
     # pragma: silent exclude
     from SignalIntegrity.Lib.TimeDomain.Waveform.TimeDescriptor import TimeDescriptor
     # pragma: include
     N = self.N
     K = 2 * N
     if not Keven: K = K + 1
     Fs = self.Fe * K / N
     return TimeDescriptor(-K / 2. / Fs, K, Fs)
Example #5
0
    def __init__(self, wf, fd=None):
        """Constructor
        @param wf in instance of class Waveform
        @param fd (optional) an instance of class FrequencyList (defaults to None)
        @remark
        initializes itself internally by computing the frequency content of the waveform.

        If fd is None then the frequency descriptor is simply the frequency descriptor corresponding to the time
        descriptor of the waveform and the frequency content is computed from the DFT.

        Otherwise, the CZT is used to compute the frequency content and the time descriptor corresponds to the
        frequency descriptor.

        the time descriptor and frequency descriptor are retained so a waveform can be obtained from the frequency content.

        @note the frequency content is scaled differently from the raw DFT or CZT outputs in that the absolute value of each
        complex number in the frequency content represents the amplitude of a cosine wave.  This is not true with the raw
        DFT output and scaling things this way helps in the proper interpretation of the frequency content without having
        to think about the vagaries of the DFT.

        @see TimeDescriptor
        @see FrequencyList
        @see ChirpZTransform
        """
        td = wf.td
        if fd is None:
            X = fft.fft(wf.Values())
            K = int(td.K)
            Keven = (K // 2) * 2 == K
            fd = td.FrequencyList()
        else:
            # pragma: silent exclude
            if not fd.EvenlySpaced():
                raise SignalIntegrityExceptionWaveform(
                    'cannot generate frequency content')
            # pragma: include
            K = fd.N * 2
            Keven = True
            X = CZT(wf.Values(), td.Fs, 0, fd.Fe, fd.N, True)
            td = TimeDescriptor(td.H, fd.N * 2, fd.Fe * 2.)
        FrequencyDomain.__init__(self,fd,[X[n]/K*\
            (1. if (n==0 or ((n==fd.N) and Keven)) else 2.)*\
            cmath.exp(-1j*2.*math.pi*fd[n]*td.H) for n in range(fd.N+1)])
        self.td = td
Example #6
0
 def __init__(self,fileName,td=None):
     if not td is None:
         HorOffset=td.H
         NumPts=td.K
         SampleRate=td.Fs
     else:
         HorOffset=0.0
         NumPts=0
         SampleRate=1.
     with open(fileName,'rb') as f:
         wf = [float(line) for line in f]
     if NumPts==0:
         NumPts=len(wf)
     else:
         if len(wf) > NumPts:
             wf = [wf[k] for k in range(NumPts)]
         else:
             NumPts=len(wf)
     Waveform.__init__(self,TimeDescriptor(HorOffset,NumPts,SampleRate),wf)
Example #7
0
 def ReadFromFile(self,fileName):
     """reads a waveform from a file
     @param fileName string name of file to read
     @return self
     @note this DOES affect self
     @note the normal waveform format is one number per line starting with the
     horizontal offset followed by the number of points followed by the sample
     rate.  The remaining lines contain one waveform point per line.  This is
     the format output by SignalIntegrity.  However, if the data is all on one line, then
     the format is assumed to be LeCroy MathPack format with the first point
     being the number of points, and the remaining points being time and value.
     @note if the file extension is '.trc', then LeCroy waveform format is assumed
     """
     # pragma: silent exclude
     _, file_extension = os.path.splitext(fileName)
     if file_extension == '.trc':
         self.ReadLeCroyWaveform(fileName)
         return self
     try:
     # pragma: include outdent
         with open(fileName,'rU' if sys.version_info.major < 3 else 'r') as f:
             data=f.readlines()
             # pragma: silent exclude
             if len(data)==1:
                 data=data[0].split()
                 NumPts=int(float(data[0])+0.5)
                 HorOffset=float(data[1])
                 SampleRate=1./(float(data[3])-HorOffset)
                 Values=[float(data[k*2+2]) for k in range(NumPts)]
             else:
                 # pragma: silent include outdent
                 HorOffset=float(data[0])
                 NumPts=int(float(data[1])+0.5)
                 SampleRate=float(data[2])
                 Values=[float(data[k+3]) for k in range(NumPts)]
                 # pragma: silent indent
         self.td=TimeDescriptor(HorOffset,NumPts,SampleRate)
         list.__init__(self,Values)
     # pragma: silent exclude indent
     except IOError:
         raise SignalIntegrityExceptionWaveformFile(fileName+' not found')
     # pragma: include
     return self
Example #8
0
    def ImpulseResponse(self,td=None,adjustDelay=True):
        """the time-domain impulse response
        @param td (optional) instance of class TimeDescriptor.
        @param adjustDelay (optional) bool whether to adjust the delay.
        @return instance of class ImpulseResponse corresponding to the frequency response.
        @remark
        If the optional time descriptor is supplied, the resulting impulse response is resampled
        onto that time descriptor.
        @note
        internally, the frequency response is either evenly spaced or not.

        whether evenly spaced, whether a time descriptor is specified and
        whether to adjust delay determines all possibilities.

        | evenly spaced | time descriptor | adjust delay | Situation                                         |
        |:------------: |:---------------:|:------------:|:---------------------------------------------------                                          |
        |  False        | False           | X            | Cannot be done                                    |
        |  False        | True            | X            | Spline resamples to time descriptor               |
        |  True         | False           | False        | generic impulse response                          |
        |  True         | False           | True         | impulse response with delay adjusted              |
        |  True         | True            | X            | CZT resamples to td - ad forced to T              |

        Much of these options are meant for internal use.  Mostly you should simply use ImpulseResponse()
        with the default arguments.
        @remark td can also be supplied as a float or int.  In this situation, the TimeDescriptor corresponding
        to the internal FrequencyList is used, but the td supplied supplants the sample rate of the TimeDescriptor.
        In this way, only the sample rate can be specified in the resampling, and all processing as shown in the
        table above will assume that a time descriptor has been supplied of this calculated type.
        """
        fd = self.FrequencyList()
        if isinstance(td,float) or isinstance(td,int):
            Fs=float(td)
            td=fd.TimeDescriptor()
            td = TimeDescriptor(0.,2*int(math.ceil(Fs*td.K/2./td.Fs)),Fs)
        evenlySpaced = fd.CheckEvenlySpaced()
        if not evenlySpaced and td is None: return None
        if not evenlySpaced and not td is None:
            newfd = td.FrequencyList()
            oldfd = fd
            Poly=Spline(oldfd,self.Response())
            newresp=[Poly.Evaluate(f) if f <= oldfd[-1] else 0.0001 for f in newfd]
            newfr=FrequencyResponse(newfd,newresp)
            return newfr.ImpulseResponse(None,adjustDelay)
        if evenlySpaced and td is None and not adjustDelay:
            yfp=self.Response()
            ynp=[yfp[fd.N-nn].conjugate() for nn in range(1,fd.N)]
            y=yfp+ynp
            y[0]=y[0].real
            y[fd.N]=y[fd.N].real
            Y=fft.ifft(y)
            td=fd.TimeDescriptor()
            tp=[Y[k].real for k in range(td.K//2)]
            tn=[Y[k].real for k in range(td.K//2,td.K)]
            Y=tn+tp
            return ImpulseResponse(td,Y)
        if evenlySpaced and td is None and adjustDelay:
            TD=self._FractionalDelayTime()
            return self._DelayBy(-TD).ImpulseResponse(None,False).DelayBy(TD)
        if evenlySpaced and not td is None:
            # if td is a float and not a time descriptor, it's assumed to be a
            # sample rate.  In this case, the number of points in a
            # time descriptor are filled in representing the time content of self
            return self.Resample(td.FrequencyList()).ImpulseResponse()
 def TimeDescriptor(bitRate, sampleRate, patternLength):
     timeLength = patternLength / bitRate
     numPoints = int(math.floor(timeLength * sampleRate + 0.5))
     return TimeDescriptor(0., numPoints, sampleRate)
 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)