6

我正在尝试实现生成正弦波的 Python 方法,该方法在两个频率之间呈指数增长。使用以下 Python 代码在[this question]中解决了线性变化:

from math import pi, sin

def sweep(f_start, f_end, interval, n_steps):    
    for i in range(n_steps):
        delta = i / float(n_steps)
        t = interval * delta
        phase = 2 * pi * t * (f_start + (f_end - f_start) * delta / 2)
        print t, phase * 180 / pi, 3 * sin(phase)

sweep(1, 10, 5, 1000)

如何将这种线性累积相位/增量方法更改为指数频率扫描并使其对人耳平滑。

4

3 回答 3

8

这类问题的诀窍是了解频率调制相位调制之间的关系,这两者密切相关。具有恒定频率f和幅度的正弦A可以描述为(公式,而不是 python 代码):

x(t) = A sin(2 * pi * f * t)

但是另一种写法是首先将阶段定义phi为时间的函数:

phi(t) = 2 * pi * f * t
x(t) = A sin(phi(t))

这里要注意的是,频率f是相位的导数,除以 2*pi: f = d/dt(phi(t)) / (2*pi)

对于频率随时间变化的信号,您可以类似地定义瞬时频率 f_inst

x(t) = A sin(phi(t))
f_inst = d/dt(phi(t)) / (2*pi)

您想要做的是与此相反,您有一个给定的瞬时频率(您的对数扫描),您需要将其转换为相位。由于推导的反面是积分,因此您可以像这样计算适当的相位(仍然是公式):

phi(t) = 2 * pi * Integral_0_to_t {f_inst(t) dt}
x(t) = A sin(phi(t))

您在这里所做的是对信号(零频率)进行某种相位调制,以获得所需的瞬时频率。这在 numpy 中很容易做到:

from pylab import *
n = 1000 # number of points
f1, f2 = 10, 30 # frequency sweep range in Hertz

t = linspace(0,1,1000)
dt = t[1] - t[0] # needed for integration

# define desired logarithmic frequency sweep
f_inst = logspace(log10(f1), log10(f2), n)
phi = 2 * pi * cumsum(f_inst) * dt # integrate to get phase

# make plot
plot(t, sin(phi))
xlabel('Time (s)')
ylim([-1.2, 1.2])
grid()
show()

结果图像:

对数频率扫描

但是(正如戴夫提到的欺骗中所指出的那样),您可能不想要对数扫描,而是指数扫描。你的耳朵对频率有对数感知,因此平滑/线性的音阶(想想钢琴上的琴键)因此呈指数间隔。这可以通过简单地重新定义您的瞬时频率来实现f_inst(t) = f1 * exp(k * t),选择的位置k是这样的f_inst(t2) = f2

如果您想同时使用幅度调制,您可以在公式中简单地更改A为时间相关。A(t)

于 2013-11-04T20:59:42.283 回答
5

巴斯的回答很棒,但实际上并没有给出解析解决方案,所以这就是那部分......

据我所知,您想要诸如sin(Aexp(Bt))whereABare 常量之类的东西。我会假设时间开始于0并继续C(如果它开始于其他时间,则从两者中减去)。

然后,正如巴斯所说,我认为,如果我们有sin(g(t))频率f是这样的2 * pi * f = dg / dt。我们希望那是f0在时间0fC时间C

如果你通过数学,这很容易(真的是 - 学校的最后一年),你会得到:

B = 1/C * log(fC/f0)
A = 2 * pi * f0 / B

这是一些使用 1000 个样本在 5 秒内从 1 到 10Hz 的代码:

from math import pi, sin, log, exp

def sweep(f_start, f_end, interval, n_steps):
    b = log(f_end/f_start) / interval
    a = 2 * pi * f_start / b
    for i in range(n_steps):
        delta = i / float(n_steps)
        t = interval * delta
        g_t = a * exp(b * t)
        print t, 3 * sin(g_t)

sweep(1, 10, 5, 1000)

这使:

一个漂亮的情节

(并且您可以添加一个常量 -sin(g_t + k)以在任何您想要的地方获得起始阶段)。

更新

为了表明您看到的问题是采样的人工制品,这里有一个过采样的版本(如果您将其设置为参数):

from math import pi, sin, log, exp

def sweep(f_start, f_end, interval, n_steps, n_oversample=1):
    b = log(f_end/f_start) / interval
    a = 2 * pi * f_start / b
    for i in range(n_steps):
        for oversample in range(n_oversample):
            fractional_step = oversample / float(n_oversample)
            delta = (i + fractional_step) / float(n_steps)
            t = interval * delta
            g_t = a * exp(b * t)
            print t, 3 * sin(g_t)

sweep(16000.0, 16500.0, 256.0/48000.0, 256)      # looks strange
sweep(16000.0, 16500.0, 256.0/48000.0, 256, 4)   # looks fine with better resolution

如果您检查代码,您会看到所有设置n_oversample为 4(第二次调用)是为时间步添加更高的分辨率。特别是,代码 when oversample = 0(ie fractional_step = 0)之前相同,因此第二个图包括第一个图中的点,以及“填充”缺失数据并使一切看起来不那么令人惊讶的额外点。

这是原始曲线和开始附近的过采样曲线的特写,详细显示了正在发生的事情:

在此处输入图像描述

最后,这种事情是完全正常的,并不表示任何错误。当从数字波形生成模拟信号时,您将获得“正确”的结果(假设硬件工作正常)。 如果不清楚,这个出色的视频将解释事情。

于 2013-11-04T21:26:56.060 回答
2

当这个老问题出现在相关问题列表中另一个问题的右边距时,它引起了我的注意。Bas Swinckels 和 andrew Cooke 的回答很棒。我添加这个答案是为了指出 SciPy 具有scipy.signal.chirp生成这种信号的功能。对于频率的指数变化,使用method="logarithmic"。(参数method也可以是"linear","quadratic""hyperbolic"。对于使用任意多项式的啁啾声,您可以使用scipy.signal.sweep_poly。)

以下是如何chirp在 5 秒内以 1000 个样本生成从 1 Hz 到 10 Hz 的扫描:

import numpy as np
from scipy.signal import chirp
import matplotlib.pyplot as plt

T = 5
n = 1000
t = np.linspace(0, T, n, endpoint=False)
f0 = 1
f1 = 10
y = chirp(t, f0, T, f1, method='logarithmic')

plt.plot(t, y)
plt.grid(alpha=0.25)
plt.xlabel('t (seconds)')
plt.show()

阴谋

于 2017-08-08T04:58:34.613 回答