Пример #1
0
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
Пример #2
0
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
Пример #3
0
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
Пример #4
0
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()
Пример #5
0
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
Пример #6
0
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
Пример #7
0
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()
Пример #8
0
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
Пример #9
0
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
Пример #10
0
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