分享一下我用Python模拟的AM,PM,FM调制,起因就是刚开始了解调制的时候,有点理解不了,那模拟一下呗,然后就写了一个代码。
代码地址
先放一个AM的演示图
然后看一个程序调用,我不喜欢奇奇怪怪的操作,程序调用要符合操作逻辑:创建信息波和载波,然后生成调制信号波,最后画图。
fig = plt.figure() # 创建画布
# 初始化波的参数:频率和幅度
A = 2 # carrier wave amplitude
m = 1 # m is the amplitude sensitivity
mes_fre = 1 # message wave frequence
car_fre = 10 # carrier wave frequence
# 创建信息波
signal = Signal()
mes_wave = SinWave(fre=mes_fre, Amp=A*m, Phi=0)
information_signal = signal + mes_wave
# 创建载波
carrier_signal = CosWave(fre=car_fre, Amp=A, Phi=0)
# 创建时间轴
time = np.arange(0, 2*np.pi, 0.01)
# 生成调制信号
am_sig = AM(information_signal, carrier_signal, A=A)
# 画出动图
ud = UpdateDist(fig, time, information_signal, carrier_signal, am_sig)
ani = animation.FuncAnimation(fig=fig,
func=ud,
frames=20,
interval=100,
blit=True)
plt.show()
我比较喜欢模块化应用,我在网上也调研了一部分的模拟代码,发现大多是一次性的工作,基本没有移植性。所以我就按照我的思想写了一个脚本,虽然代码会显得有点臃肿,但是移植性还是蛮不错的。接下来就分享一下我的思路,我不会讲调制的原理,就讲代码的实现方式。
创建大体的框架
1. 构建基本类变量
模拟的是调制,调制也就是两个信号的相互作用。信号也就是波的叠加,简化一下,载波(carrier wave)用单一频率的波表示,信号波(message wave)用波实现简单运算得到。这个时候还有点复杂,波的种类繁多,所以我就只考虑Sin和Cos波。对应到代码构造,我比较喜欢用类编程,于是就有了下面的结构
class Wave:
def __init__(self, fre, Amp, Phi):
# 定义波的公共属性:幅度、频率、相位
pass
def __str__(self):
# 打印波的信息,方便后期调试
pass
class SinWave(Wave):
# 继承 Wave 类,子类实现 波与波的乘法运算
def __init__(self, fre, Amp, Phi, modu_wave=None):
super().__init__(fre, Amp, Phi, modu_wave, info='Sin')
def __call__(self, t):
# 定义调用接口,实现将t带入运算
pass
def __mul__(self, scalar):
# 定义 波与波 之间的乘法运算法则
pass
def __rmul__(self, scalar):
# 也是 实现 波与波的运算
pass
class CosWave(Wave):
# 构造与 SinWave 类似,不同的只是乘法运算法则不同
class Signal:
def __init__(self, sig=None):
# 初始化 "Signal", 可以是 Wave 也可以是 实数,内部用 list 保存
pass
def max_fre(self):
# 获取 Signal 的最大频率
pass
def max_Amp(self):
# 获取 Signal 的最大幅值
pass
def __iter__(self):
# 使得 Signal 变为可迭代对象,满足 Signal*Wave的运算
pass
def __add__(self, sig):
# Signal 满足加法运算
pass
def __radd__(self, sig):
# Signal 满足加法运算
pass
def __call__(self, t):
# Signal 满足可调用
pass
def __str__(self):
# 打印 Signal 的信息,方便以后调试。
pass
2. 完善调制函数
信号Signal
和波Wave
初始化完成后,需要考虑调制的函数。这里用3个函数表示三种调制就行了
def AM(information_signal, carrier_signal, A):
# AM 幅度调制,A 是调幅的参数,参考AM调制的原理
pass
def PM(information_signal, carrier_signal):
# PM 相位调制
pass
def FM(information_signal, carrier_signal, h=1):
# FM 频率调制,h是调频参数 参考FM调制的原理
pass
3. 构建作图函数
创建画图类 UpdateDist
class UpdateDist:
# 画动图时候推荐采用类编程,比函数好管理
def __init__(self, fig, x, *func_y):
# 初始化刚开始的图像显示,我这里初始化了三个波
pass
def __call__(self, i):
# 调用函数,这里的 i 也就是 t=i 时刻函数的图像,这样达到了动态显示的效果
pass
代码的完善
对代码的完善就是对上面框架的缝缝补补,有时候可能会修改框架,上面的框架是我推翻了一次原框架后的结果。不过上面的框架还是适用于 AM 调制,因为 AM 调制本质就是几个波的乘法运算,而 PM 和 FM 是修改了波的相位,于是我就加了一个 modu_wave
变量
class CosWave(Wave):
def __init__(self, fre, Amp, Phi, modu_wave=None):
# modu_wave 默认为 None ,如果 PM 和 FM 时需要传入
super().__init__(fre, Amp, Phi, modu_wave, info='Cos')
self.modu_wave = modu_wave
class SinWave(Wave):
def __init__(self, fre, Amp, Phi, modu_wave=None):
super().__init__(fre, Amp, Phi, modu_wave, info='Sin')
self.modu_wave = modu_wave
代码的具体实现,点击获取原文可以查看。
关于代码中的一些函数的解释
我在代码中用了几个 __mul__
、__add__
等以__
开头结尾的函数,这是python的魔法函数,它是python的一种协议。比如如果实现了函数 __add__
,就可以使用 +
运算。
-
__add__
和__radd__
# 比如这里有两个波 test1_wave = CosWave(fre= 2, Amp=0.5, Phi=0) test2_wave = CosWave(fre= 3, Amp=0.5, Phi=0) # 实现了 __add__ 函数后 载波信号可以这么写 signal = Signal() information_signal = signal + test1_wave + test2_wave # 实现了 __radd__ 函数后,前后式子可以调换位置 information_signal = test1_wave + signal + test2_wave # tip: 我这里只在 Signal 中实现了 + 法运算,所以 下面的写法会报错 # test_wave = test1_wave + test2_wave # 报错 # 原因嘛,我觉得 Signal 实现 + 就够使用了
可以看看波的样子
-
__mul__
和__rmul__
# __mul__ 表示乘法运算 # 还是上面的两个波,我可以直接这么写 information_signal = signal + test1_wave * test2_wave # 同理 实现了 __rmul__ 后可以换位置 information_signal = signal + test2_wave * test1_wave
看看波的样子
-
__call__
# 实现 # call 函数后,类变量可以和函数一样调用 # 上面的波举例,我要是想知道 t=1 时函数的幅值我就可以这么写 A1 = test1_wave(1) # 0.5 A2 = test2_wave(1) # 0.5
-
__str__
# 实现函数后 可以使用 str(func) 打印函数 # 接着上面的例子, 程序结果放在 '#' 后面 print(test1_wave) # CosWave(Frequency is 2,Amplitude is 0.5,Initial phase is 0),modulate wave is None print(test2_wave) # CosWave(Frequency is 2,Amplitude is 0.5,Initial phase is 0),modulate wave is None
总结
以上就是我分享的这次代码的细节部分,点击阅读全文获取代码。最后放几个 PM 和 FM 调制的图,我曾经对着这个图呆了一早上。
代码地址