def __mul__(self,other): """overloads * @param other instance of class WaveformProcessor or float, int, complex to multiply by. @return instance of class Waveform with other multiplied by self @note does not affect self @note Waveform multiplication is an abstraction in some cases. The result for types of other is: - WaveformProcessor - returns self processed by the instance of WaveformProcessor. - float,int,complex - returns the Waveform produced by multiplying all of the values in self multiplied by the constant value supplied. @note The most obvious type of WaveformProcessor is a FirFilter, but there are others like WaveformTrimmer and WaveformDecimator. @throw SignalIntegrityExceptionWaveform if other cannot be multiplied. """ # pragma: silent exclude from SignalIntegrity.Lib.TimeDomain.Filters.WaveformProcessor import WaveformProcessor # pragma: include if isinstance(other,WaveformProcessor): return other.ProcessWaveform(self) elif isinstance(other,(float,int,complex)): return Waveform(self.td,[v*other.real for v in self]) elif isinstance(other,Waveform): [s,o]=AdaptedWaveforms([self,other]) return Waveform(s.td,[s[k]*o[k] for k in range(len(s))]) # pragma: silent exclude else: raise SignalIntegrityExceptionWaveform('cannot multiply waveform by type '+str(other.__class__.__name__))
def __add__(self,other): """overloads + @param other instance of class Waveform or float, int, complex to add. @return instance of class Waveform with other added to self @note does not affect self @note valid types of other to add are: - Waveform - if the other waveform has the same time descriptor, returns the waveform with self and others values added together, otherwise adapts other to self and then adds them. - float,int,complex - adds the constant value to all values in self. @throw SignalIntegrityExceptionWaveform if other cannot be added. @see AdaptedWaveforms """ if isinstance(other,Waveform): if self.td == other.td: return Waveform(self.td,[self[k]+other[k] for k in range(len(self))]) else: [s,o]=AdaptedWaveforms([self,other]) return Waveform(s.td,[s[k]+o[k] for k in range(len(s))]) #return awf[0]+awf[1] elif isinstance(other,(float,int,complex)): return Waveform(self.td,[v+other.real for v in self]) # pragma: silent exclude else: raise SignalIntegrityExceptionWaveform('cannot add waveform to type '+str(other.__class__.__name__))
def __sub__(self,other): """overloads - @param other instance of class Waveform or float, int, complex to subtract. @return instance of class Waveform with other subtracted from self @note does not affect self @note valid types of other to subtract are: - Waveform - if the other waveform has the same time descriptor, returns the waveform with self and others values subtracted, otherwise adapts other to self and then subtracts them. - float,int,complex - subtracts the constant value from all values in self. @throw SignalIntegrityExceptionWaveform if other cannot be subtracted. @see AdaptedWaveforms """ if isinstance(other,Waveform): if self.td == other.td: return Waveform(self.td,[self[k]-other[k] for k in range(len(self))]) else: [s,o]=AdaptedWaveforms([self,other]) return Waveform(s.td,[s[k]-o[k] for k in range(len(s))]) elif isinstance(other,(float,int,complex)): return Waveform(self.td,[v-other.real for v in self]) # pragma: silent exclude else: raise SignalIntegrityExceptionWaveform('cannot subtract type' + str(other.__class__.__name__) + ' from waveform')
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 Adapt(self,td): """adapts waveform to time descriptor Waveform adaption is performed using upsampling, decimation, fractional delay, and waveform point trimming. @param td instance of class TimeDescriptor to adapt waveform to @return instance of class Waveform containing self adapted to the time descriptor @note does not affect self. @note the static member variable adaptionStrategy determines how to interpolate. 'SinX' means to use sinx/x interpolation, 'Linear' means to use linear interpolation. @see InterpolatorSinX @see SignalIntegrity.TimeDomain.Filters.InterpolatorSinX.FractionalDelayFilterSinX @see SignalIntegrity.TimeDomain.Filters.InterpolatorSinX.FractionalDelayFilterSinX @see SignalIntegrity.TimeDomain.Filters.InterpolatorLinear.InterpolatorLinear @see SignalIntegrity.TimeDomain.Filters.InterpolatorLinear.FractionalDelayFilterLinear @see SignalIntegrity.TimeDomain.Filters.WaveformTrimmer.WaveformTrimmer @see SignalIntegrity.TimeDomain.Filters.WaveformDecimator.WaveformDecimator @see SignalIntegrity.Rat.Rat """ # pragma: silent exclude from SignalIntegrity.Lib.TimeDomain.Filters.InterpolatorSinX import InterpolatorSinX from SignalIntegrity.Lib.TimeDomain.Filters.InterpolatorSinX import FractionalDelayFilterSinX from SignalIntegrity.Lib.TimeDomain.Filters.InterpolatorLinear import InterpolatorLinear from SignalIntegrity.Lib.TimeDomain.Filters.InterpolatorLinear import FractionalDelayFilterLinear from SignalIntegrity.Lib.TimeDomain.Filters.WaveformTrimmer import WaveformTrimmer from SignalIntegrity.Lib.TimeDomain.Filters.WaveformDecimator import WaveformDecimator from SignalIntegrity.Lib.Rat import Rat # pragma: include wf=self (upsampleFactor,decimationFactor)=Rat(td.Fs/wf.td.Fs) if upsampleFactor>1: # pragma: silent exclude if wf.td.K*upsampleFactor > self.maximumWaveformSize: raise SignalIntegrityExceptionWaveform('waveform too large to process') # pragma: include wf=wf*(InterpolatorSinX(upsampleFactor) if wf.adaptionStrategy=='SinX' else InterpolatorLinear(upsampleFactor)) ad=td/wf.td f=ad.D-int(math.floor(ad.D)) if not ((f<self.epsilon) or ((1-f)<self.epsilon)): wf=wf*(FractionalDelayFilterSinX(f,True) if wf.adaptionStrategy=='SinX' else FractionalDelayFilterLinear(f,True)) ad=td/wf.td if decimationFactor>1: decimationPhase=int(round(ad.TrimLeft())) % decimationFactor wf=wf*WaveformDecimator(decimationFactor,decimationPhase) ad=td/wf.td tr=WaveformTrimmer(max(0,int(round(ad.TrimLeft()))), max(0,int(round(ad.TrimRight())))) wf=wf*tr return wf
def __truediv__(self,other): """overloads / @param other instance of float, int, complex to divide by. @return instance of class Waveform with other divided into it. @note only handles float, int, complex where the constant values are divided into the values in self. @note should consider allowing a two waveforms to be divided, but frankly never came upon the need for that. @throw SignalIntegrityExceptionWaveform if other cannot be multiplied. """ if isinstance(other,(float,int,complex)): return Waveform(self.td,[v/other.real for v in self]) # pragma: silent exclude else: raise SignalIntegrityExceptionWaveform('cannot divide waveform by type '+str(other.__class__.__name__))
def __init__(self,polynomial,bitrate,amplitude=1.0,risetime=0.,delay=0.,tdOrFs=None): """constructor @param polynomial integer polynomial number @param bitrate, amplitude, risetime, delay, tdOrFs all pertain to the derived SerialDataWaveform class @see SerialDataWaveform @return self, a waveform. @throw SignalIntegrityWaveform exception is raised if the polynomial number cannot be found @see PseudoRandomPolynomial """ try: if isinstance(tdOrFs,TimeDescriptor): td=tdOrFs SerialDataWaveform.__init__(self,PseudoRandomPolynomial(polynomial).Pattern(math.ceil(td.K*bitrate/td.Fs)),bitrate,amplitude,risetime,delay,tdOrFs) else: SerialDataWaveform.__init__(self,PseudoRandomPolynomial(polynomial).Pattern(),bitrate,amplitude,risetime,delay,tdOrFs) except SignalIntegrityException as e: raise SignalIntegrityExceptionWaveform(e.message)
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