def ap2fft(amplitude, phase_rad=None, phase_deg=None, samplingfreq_hz=2.0, axis=0, freq_hz=None, fullfreq_hz=None, nsamp=None): """ Keyword arguments match the fields of the dict output by that fft2ap() . The inverse of d=fft2ap(X) is X = ap2fft(**d) """### fs = getfs(samplingfreq_hz) if nsamp == None: if fullfreq_hz != None: nsamp = len(fullfreq_hz) elif freq_hz != None: nsamp = len(freq_hz) * 2 - 2 else: nsamp = amplitude.shape[axis] * 2 - 2 amplitude = project(numpy.array(amplitude, dtype='float'), axis) if phase_rad == None and phase_deg == None: phase_rad = numpy.zeros(shape=amplitude.shape, dtype='float') if phase_rad != None: if not isnumpyarray(phase_rad) or phase_rad.dtype != 'float': phase_rad = numpy.array(phase_rad, dtype='float') phase_rad = project(phase_rad, axis) if phase_deg != None: if not isnumpyarray(phase_deg) or phase_deg.dtype != 'float': phase_deg = numpy.array(phase_deg, dtype='float') phase_deg = project(phase_deg, axis) if phase_rad != None and phase_deg != None: if phase_rad.shape != phase_deg.shape: raise ArgConflictError, "conflicting phase_rad and phase_deg arguments" if numpy.max( numpy.abs(phase_rad * (180.0 / numpy.pi) - phase_deg) > 1e-10): raise ArgConflictError, "conflicting phase_rad and phase_deg arguments" if phase_rad == None: phase_rad = phase_deg * (numpy.pi / 180.0) f = phase_rad * 1j f = numpy.exp(f) f = f * amplitude f *= float(nsamp) / 2.0 sub = [slice(None)] * max(axis + 1, len(f.shape)) if nsamp % 2 == 0: sub[axis] = -1 f[sub] *= 2.0 sub[axis] = slice((nsamp % 2) - 2, 0, -1) f = numpy.concatenate((f, numpy.conj(f[sub])), axis=axis) return f
def window(w, func=hanning, axis=0): """ Return a copy of <w> (a numpy.ndarray or a WavTools.wav object) multiplied by the specified window function, along the specified time <axis>. """### if isnumpyarray(w): y = w elif hasattr(w, 'y'): w = w.copy(); y = w.y else: raise TypeError, "don't know how to handle this kind of carrier object" envelope = func(y.shape[0]) envelope.shape = [{True:envelope.size, False:1}[dim==axis] for dim in range(y.ndim)] y = y * envelope if isnumpyarray(w): w = y else: w.y = y return w
def ampmod(w, freq_hz=1.0, phase_rad=None, phase_deg=None, amplitude=0.5, dc=0.5, samplingfreq_hz=None, duration_msec=None, duration_samples=None, axis=None, waveform=numpy.sin, **kwargs): """ Return a copy of <w> (a numpy.ndarray or a WavTools.wav object) in which the amplitude is modulated sinusoidally along the specified time <axis>. Default phase is such that amplitude is 0 at time 0, which corresponds to phase_deg=-90 if <waveform> follows sine phase, since the modulator is a raised waveform. To change this, specify either <phase_rad> or <phase_deg>. Uses wavegen() """### if isnumpyarray(w): y = w elif hasattr(w, 'y'): w = w.copy() y = w.y else: raise TypeError, "don't know how to handle this kind of carrier object" if samplingfreq_hz == None: samplingfreq_hz = getfs(w) if phase_rad == None and phase_deg == None: phase_deg = -90.0 if duration_samples == None and duration_msec == None: duration_samples = project(y, 0).shape[0] envelope = wavegen(freq_hz=freq_hz, phase_rad=phase_rad, phase_deg=phase_deg, amplitude=amplitude, dc=dc, samplingfreq_hz=samplingfreq_hz, duration_msec=duration_msec, duration_samples=duration_samples, axis=axis, waveform=waveform, **kwargs) envelope = project(envelope, len(y.shape) - 1) y = y * envelope if isnumpyarray(w): w = y else: w.y = y return w
def plotsig(x, samplingfreq_hz=None, hold=False, axis=0, welch=0, **kwargs): """ Makes two subplots, showing time-series <x> in the upper panel and its amplitude spectrum in the lower panel. Set <hold> in order to re-use a previous figure. Any additional keyword arguments are passed through to pylab.plot for both subplots. """### fs = getfs(samplingfreq_hz) if fs == None: fs = getfs(x, 2.0) if hasattr(x, 'x'): x = x.x elif hasattr(x, 'y'): x = x.y if not isnumpyarray(x): axis = 0 if isinstance(x[0], list) or isinstance(x[0], tuple): axis = 1 x = numpy.array(x, dtype='float') xwin = x = project(x, axis).swapaxes(0, axis) nsamp = x.shape[0] class Unfinished(Exception): pass if welch == 1: xwin = x * project(hanning(nsamp), len(x.shape) - 1) elif welch > 0: raise Unfinished, "Welch periodogram not yet implemented" t = numpy.arange(0, nsamp) / float(fs) ap = fft2ap(fft(xwin, axis=0), samplingfreq_hz=fs, axis=0) f = ap['freq_hz'] a = 20.0 * numpy.log10(ap['amplitude']) pylab = load_pylab() if not hold: pylab.clf() pylab.subplot(2, 1, 1) h1 = pylab.plot(t, x, **kwargs) ax = pylab.gca() ax.set_xlim(t[0], t[-1]) ax.xaxis.grid(True) ax.yaxis.grid(True) pylab.subplot(2, 1, 2) a[numpy.isinf( a )] = numpy.nan # crude workaround---pylab.plot can't cope with infinite values h2 = pylab.plot(f, a, **kwargs) ax = pylab.gca() ax.set_xlim(f[0], f[-1]) ax.xaxis.grid(True) ax.yaxis.grid(True) pylab.draw()
def window(w, func=hanning, axis=0): """ Return a copy of <w> (a numpy.ndarray or a WavTools.wav object) multiplied by the specified window function, along the specified time <axis>. """### if isnumpyarray(w): y = w elif hasattr(w, 'y'): w = w.copy() y = w.y else: raise TypeError, "don't know how to handle this kind of carrier object" envelope = func(y.shape[0]) envelope.shape = [{ True: envelope.size, False: 1 }[dim == axis] for dim in range(y.ndim)] y = y * envelope if isnumpyarray(w): w = y else: w.y = y return w
def ap2fft(amplitude,phase_rad=None,phase_deg=None,samplingfreq_hz=2.0,axis=0,freq_hz=None,fullfreq_hz=None,nsamp=None): """ Keyword arguments match the fields of the dict output by that fft2ap() . The inverse of d=fft2ap(X) is X = ap2fft(**d) """### fs = getfs(samplingfreq_hz) if nsamp==None: if fullfreq_hz != None: nsamp = len(fullfreq_hz) elif freq_hz != None: nsamp = len(freq_hz) * 2 - 2 else: nsamp = amplitude.shape[axis] * 2 - 2 amplitude = project(numpy.array(amplitude,dtype='float'), axis) if phase_rad == None and phase_deg == None: phase_rad = numpy.zeros(shape=amplitude.shape,dtype='float') if phase_rad != None: if not isnumpyarray(phase_rad) or phase_rad.dtype != 'float': phase_rad = numpy.array(phase_rad,dtype='float') phase_rad = project(phase_rad, axis) if phase_deg != None: if not isnumpyarray(phase_deg) or phase_deg.dtype != 'float': phase_deg = numpy.array(phase_deg,dtype='float') phase_deg = project(phase_deg, axis) if phase_rad != None and phase_deg != None: if phase_rad.shape != phase_deg.shape: raise ArgConflictError, "conflicting phase_rad and phase_deg arguments" if numpy.max(numpy.abs(phase_rad * (180.0/numpy.pi) - phase_deg) > 1e-10): raise ArgConflictError, "conflicting phase_rad and phase_deg arguments" if phase_rad == None: phase_rad = phase_deg * (numpy.pi/180.0) f = phase_rad * 1j f = numpy.exp(f) f = f * amplitude f *= float(nsamp)/2.0 sub = [slice(None)] * max(axis+1, len(f.shape)) if nsamp%2 == 0: sub[axis] = -1 f[sub] *= 2.0 sub[axis] = slice((nsamp%2)-2, 0, -1) f = numpy.concatenate((f, numpy.conj(f[sub])), axis=axis) return f
def plotsig(x, samplingfreq_hz=None, hold=False, axis=0, welch=0, **kwargs): """ Makes two subplots, showing time-series <x> in the upper panel and its amplitude spectrum in the lower panel. Set <hold> in order to re-use a previous figure. Any additional keyword arguments are passed through to pylab.plot for both subplots. """### fs = getfs(samplingfreq_hz) if fs==None: fs = getfs(x,2.0) if hasattr(x, 'x'): x = x.x elif hasattr(x, 'y'): x = x.y if not isnumpyarray(x): axis = 0 if isinstance(x[0], list) or isinstance(x[0], tuple): axis = 1 x = numpy.array(x,dtype='float') xwin = x = project(x,axis).swapaxes(0, axis) nsamp = x.shape[0] class Unfinished(Exception): pass if welch==1: xwin = x * project(hanning(nsamp),len(x.shape)-1) elif welch > 0: raise Unfinished, "Welch periodogram not yet implemented" t = numpy.arange(0, nsamp) / float(fs) ap = fft2ap(fft(xwin,axis=0),samplingfreq_hz=fs,axis=0) f = ap['freq_hz'] a = 20.0 * numpy.log10(ap['amplitude']) pylab = load_pylab() if not hold: pylab.clf() pylab.subplot(2,1,1) h1 = pylab.plot(t,x,**kwargs) ax = pylab.gca() ax.set_xlim(t[0], t[-1]) ax.xaxis.grid(True) ax.yaxis.grid(True) pylab.subplot(2,1,2) a[numpy.isinf(a)] = numpy.nan # crude workaround---pylab.plot can't cope with infinite values h2 = pylab.plot(f,a,**kwargs) ax = pylab.gca() ax.set_xlim(f[0], f[-1]) ax.xaxis.grid(True) ax.yaxis.grid(True) pylab.draw()
def ampmod(w, freq_hz=1.0,phase_rad=None,phase_deg=None,amplitude=0.5,dc=0.5,samplingfreq_hz=None,duration_msec=None,duration_samples=None,axis=None,waveform=numpy.sin,**kwargs): """ Return a copy of <w> (a numpy.ndarray or a WavTools.wav object) in which the amplitude is modulated sinusoidally along the specified time <axis>. Default phase is such that amplitude is 0 at time 0, which corresponds to phase_deg=-90 if <waveform> follows sine phase, since the modulator is a raised waveform. To change this, specify either <phase_rad> or <phase_deg>. Uses wavegen() """### if isnumpyarray(w): y = w elif hasattr(w, 'y'): w = w.copy(); y = w.y else: raise TypeError, "don't know how to handle this kind of carrier object" if samplingfreq_hz==None: samplingfreq_hz = getfs(w) if phase_rad==None and phase_deg==None: phase_deg = -90.0 if duration_samples==None and duration_msec==None: duration_samples = project(y,0).shape[0] envelope = wavegen(freq_hz=freq_hz,phase_rad=phase_rad,phase_deg=phase_deg,amplitude=amplitude,dc=dc,samplingfreq_hz=samplingfreq_hz,duration_msec=duration_msec,duration_samples=duration_samples,axis=axis,waveform=waveform,**kwargs) envelope = project(envelope, len(y.shape)-1) y = y * envelope if isnumpyarray(w): w = y else: w.y = y return w
def wavegen(freq_hz=1.0,phase_rad=None,phase_deg=None,amplitude=1.0,dc=0.0,samplingfreq_hz=None,duration_msec=None,duration_samples=None,axis=None,waveform=numpy.cos,container=None,**kwargs): """ Create a signal (or multiple signals, if the input arguments are arrays) which is a sine function of time (time being defined along the specified <axis>). Default phase is 0, but may be changed by either <phase_deg> or <phase_rad> (or both, as long as the values are consistent). Default duration is 1000 msec, but may be changed by either <duration_samples> or <duration_msec> (or both, as long as the values are consistent). A <container> object may be supplied: if so, it should be a WavTools.wav object. <axis> is set then set to 0, and the container object's duration (if non-zero), sampling frequency, and number of channels are used as fallback values if these are not specified elsewhere. The resulting signal is put into container.y and the pointer to the container is returned. If <duration_samples> is specified and <samplingfreq_hz> is not, then the sampling frequency is chosen such that the duration is 1 second, so <freq_hz> can be interpreted as cycles per signal. The default <waveform> function is numpy.cos which means that amplitude, phase and frequency arguments can be taken straight from the kind of dictionary returned by fft2ap() for an accurate reconstruction. """### fs = getfs(samplingfreq_hz) default_duration_msec = 1000.0 nrep = 1 if container != None: if fs == None: fs = getfs(container) if hasattr(container,'duration') and container.duration(): default_duration_msec = container.duration() * 1000.0 if hasattr(container,'channels') and container.channels() and container.y.size: nrep = container.channels() for j in range(0,2): for i in range(0,2): if duration_msec==None: duration_msec = samples2msec(duration_samples, fs) if duration_samples==None: duration_samples = msec2samples(duration_msec, fs) if duration_samples != None: duration_msec = samples2msec(duration_samples, fs) if fs==None and duration_samples!=None and duration_msec!=None: fs = 1000.0 * float(duration_samples) / float(duration_msec) if fs==None and duration_samples!=None: fs = float(duration_samples) if fs==None and duration_msec!=None: fs = float(duration_msec) if duration_msec==None: duration_msec = default_duration_msec duration_sec = duration_msec / 1000.0 duration_samples = float(round(duration_samples)) if duration_msec != samples2msec(duration_samples,fs) or duration_samples != msec2samples(duration_msec,fs): raise ArgConflictError, "conflicting duration_samples and duration_msec arguments" x = numpy.arange(0.0,duration_samples) * (2.0 * numpy.pi / duration_samples) freq_hz = trimtrailingdims(numpy.array(freq_hz,dtype='float')) if phase_rad == None and phase_deg == None: phase_rad = [0.0] if phase_rad != None: if not isnumpyarray(phase_rad) or phase_rad.dtype != 'float': phase_rad = numpy.array(phase_rad,dtype='float') phase_rad = trimtrailingdims(phase_rad) if phase_deg != None: if not isnumpyarray(phase_deg) or phase_deg.dtype != 'float': phase_deg = numpy.array(phase_deg,dtype='float') phase_deg = trimtrailingdims(phase_deg) if phase_rad != None and phase_deg != None: if phase_rad.shape != phase_deg.shape: raise ArgConflictError, "conflicting phase_rad and phase_deg arguments" if numpy.max(numpy.abs(phase_rad * (180.0/numpy.pi) - phase_deg) > 1e-10): raise ArgConflictError, "conflicting phase_rad and phase_deg arguments" if phase_rad == None: phase_rad = numpy.array(phase_deg * (numpy.pi/180.0)) amplitude = trimtrailingdims(numpy.array(amplitude,dtype='float')) dc = trimtrailingdims(numpy.array(dc,dtype='float')) maxaxis = max(len(freq_hz.shape), len(phase_rad.shape), len(amplitude.shape), len(dc.shape)) - 1 if axis==None: if project(freq_hz,0).shape[0]==1 and project(phase_rad,0).shape[0]==1 and project(amplitude,0).shape[0]==1 and project(dc,0).shape[0]==1: axis=0 else: axis = maxaxis + 1 maxaxis = max(axis, maxaxis) x = project(x,maxaxis).swapaxes(0,axis) x = x * (project(freq_hz,maxaxis) * duration_sec) # *= won't work for broadcasting here # if you get an error here, try setting axis=1 and transposing the return value ;-) x = x + (project(phase_rad,maxaxis)) # += won't work for broadcasting here x = waveform(x, **kwargs) x = x * project(amplitude,maxaxis) # *= won't work for broadcasting here if numpy.any(dc.flatten()): x = x + project(dc,maxaxis) # += won't work for broadcasting here if container != None: across_channels = 1 x = project(x, across_channels) if x.shape[across_channels] == 1 and nrep > 1: x = x.repeat(nrep, across_channels) container.y = x container.fs = int(round(fs)) x = container return x
def wavegen(freq_hz=1.0, phase_rad=None, phase_deg=None, amplitude=1.0, dc=0.0, samplingfreq_hz=None, duration_msec=None, duration_samples=None, axis=None, waveform=numpy.cos, container=None, **kwargs): """ Create a signal (or multiple signals, if the input arguments are arrays) which is a sine function of time (time being defined along the specified <axis>). Default phase is 0, but may be changed by either <phase_deg> or <phase_rad> (or both, as long as the values are consistent). Default duration is 1000 msec, but may be changed by either <duration_samples> or <duration_msec> (or both, as long as the values are consistent). A <container> object may be supplied: if so, it should be a WavTools.wav object. <axis> is set then set to 0, and the container object's duration (if non-zero), sampling frequency, and number of channels are used as fallback values if these are not specified elsewhere. The resulting signal is put into container.y and the pointer to the container is returned. If <duration_samples> is specified and <samplingfreq_hz> is not, then the sampling frequency is chosen such that the duration is 1 second, so <freq_hz> can be interpreted as cycles per signal. The default <waveform> function is numpy.cos which means that amplitude, phase and frequency arguments can be taken straight from the kind of dictionary returned by fft2ap() for an accurate reconstruction. """### fs = getfs(samplingfreq_hz) default_duration_msec = 1000.0 nrep = 1 if container != None: if fs == None: fs = getfs(container) if hasattr(container, 'duration') and container.duration(): default_duration_msec = container.duration() * 1000.0 if hasattr(container, 'channels') and container.channels() and container.y.size: nrep = container.channels() for j in range(0, 2): for i in range(0, 2): if duration_msec == None: duration_msec = samples2msec(duration_samples, fs) if duration_samples == None: duration_samples = msec2samples(duration_msec, fs) if duration_samples != None: duration_msec = samples2msec(duration_samples, fs) if fs == None and duration_samples != None and duration_msec != None: fs = 1000.0 * float(duration_samples) / float(duration_msec) if fs == None and duration_samples != None: fs = float(duration_samples) if fs == None and duration_msec != None: fs = float(duration_msec) if duration_msec == None: duration_msec = default_duration_msec duration_sec = duration_msec / 1000.0 duration_samples = float(round(duration_samples)) if duration_msec != samples2msec(duration_samples, fs) or duration_samples != msec2samples( duration_msec, fs): raise ArgConflictError, "conflicting duration_samples and duration_msec arguments" x = numpy.arange(0.0, duration_samples) * (2.0 * numpy.pi / duration_samples) freq_hz = trimtrailingdims(numpy.array(freq_hz, dtype='float')) if phase_rad == None and phase_deg == None: phase_rad = [0.0] if phase_rad != None: if not isnumpyarray(phase_rad) or phase_rad.dtype != 'float': phase_rad = numpy.array(phase_rad, dtype='float') phase_rad = trimtrailingdims(phase_rad) if phase_deg != None: if not isnumpyarray(phase_deg) or phase_deg.dtype != 'float': phase_deg = numpy.array(phase_deg, dtype='float') phase_deg = trimtrailingdims(phase_deg) if phase_rad != None and phase_deg != None: if phase_rad.shape != phase_deg.shape: raise ArgConflictError, "conflicting phase_rad and phase_deg arguments" if numpy.max( numpy.abs(phase_rad * (180.0 / numpy.pi) - phase_deg) > 1e-10): raise ArgConflictError, "conflicting phase_rad and phase_deg arguments" if phase_rad == None: phase_rad = numpy.array(phase_deg * (numpy.pi / 180.0)) amplitude = trimtrailingdims(numpy.array(amplitude, dtype='float')) dc = trimtrailingdims(numpy.array(dc, dtype='float')) maxaxis = max(len(freq_hz.shape), len(phase_rad.shape), len( amplitude.shape), len(dc.shape)) - 1 if axis == None: if project(freq_hz, 0).shape[0] == 1 and project( phase_rad, 0).shape[0] == 1 and project( amplitude, 0).shape[0] == 1 and project(dc, 0).shape[0] == 1: axis = 0 else: axis = maxaxis + 1 maxaxis = max(axis, maxaxis) x = project(x, maxaxis).swapaxes(0, axis) x = x * (project(freq_hz, maxaxis) * duration_sec ) # *= won't work for broadcasting here # if you get an error here, try setting axis=1 and transposing the return value ;-) x = x + (project(phase_rad, maxaxis) ) # += won't work for broadcasting here x = waveform(x, **kwargs) x = x * project(amplitude, maxaxis) # *= won't work for broadcasting here if numpy.any(dc.flatten()): x = x + project(dc, maxaxis) # += won't work for broadcasting here if container != None: across_channels = 1 x = project(x, across_channels) if x.shape[across_channels] == 1 and nrep > 1: x = x.repeat(nrep, across_channels) container.y = x container.fs = int(round(fs)) x = container return x