Загрузка данных
import numpy as np
import matplotlib.pyplot as plt
from pathlib import Path
def fourier(signal, fs):
"""Находит частоты, амплитуды и фазы гармоник."""
n = len(signal)
coefficients = np.fft.rfft(signal)
frequencies = np.fft.rfftfreq(n, d=1 / fs)
# Амплитуды одностороннего спектра
amplitudes = np.abs(coefficients) / n
amplitudes[1:] *= 2
# Частоту Найквиста не удваиваем
if n % 2 == 0:
amplitudes[-1] /= 2
phases = np.angle(coefficients)
# Выделяем значимые гармоники
selected = amplitudes > amplitudes.max() * 1e-6
return (
frequencies[selected],
amplitudes[selected],
phases[selected]
)
def restore(t, frequencies, amplitudes, phases):
"""Восстанавливает сигнал как сумму гармоник."""
result = np.zeros_like(t)
for f, amplitude, phase in zip(
frequencies, amplitudes, phases
):
result += amplitude * np.cos(
2 * np.pi * f * t + phase
)
return result
def save_figure(fig, filename):
"""Сохраняет график и выводит путь к изображению."""
fig.tight_layout()
fig.savefig(filename, dpi=300)
plt.close(fig)
print("Сохранено:", Path(filename).resolve())
# ==================================================
# ЗАДАНИЕ 1. СИНУС 500 ГЦ
# ==================================================
f = 500
fs1 = 4 * f # Частота дискретизации: 2000 Гц
duration1 = 5 / f # Пять периодов: 0,01 секунды
# Для Фурье берём интервал без повторения конечной точки
t1 = np.arange(round(duration1 * fs1)) / fs1
signal1 = np.sin(2 * np.pi * f * t1)
# Для графика добавляем конечный отсчёт
plot_t1 = np.append(t1, duration1)
plot_signal1 = np.sin(2 * np.pi * f * plot_t1)
# Плотная сетка для отображения исходного синуса
fine_t1 = np.linspace(0, duration1, 2001)
fine_signal1 = np.sin(2 * np.pi * f * fine_t1)
fig1, ax = plt.subplots(figsize=(10, 4))
ax.plot(
fine_t1 * 1000,
fine_signal1,
label="Исходный синус"
)
ax.plot(
plot_t1 * 1000,
plot_signal1,
"o--",
label=f"Отсчёты: {fs1} Гц"
)
ax.set_title("Задание 1. Синус 500 Гц")
ax.set_xlabel("Время, мс")
ax.set_ylabel("Амплитуда, усл. ед.")
ax.grid()
ax.legend()
save_figure(fig1, "task1_sinus.png")
# ==================================================
# ЗАДАНИЕ 2. АМПЛИТУДНАЯ МОДУЛЯЦИЯ
# ==================================================
fc = 1_000_000 # Частота несущей: 1 МГц
fm = 10_000 # Частота модулирующего сигнала: 10 кГц
A = 1 # Амплитуда несущей
m = 0.5 # Глубина модуляции: 50%
fs2 = 20_000_000 # Частота дискретизации: 20 МГц
duration2 = 3 / fm # Три периода модулирующего сигнала
t2 = np.arange(round(duration2 * fs2)) / fs2
message = np.sin(2 * np.pi * fm * t2)
carrier = np.cos(2 * np.pi * fc * t2)
envelope = A * (1 + m * message)
signal2 = envelope * carrier
t_us = t2 * 1e6 # Время в микросекундах
fig2, axes = plt.subplots(3, 1, figsize=(11, 9))
# Модулирующий сигнал
axes[0].plot(t_us, message)
axes[0].set_title("Модулирующий сигнал: 10 кГц")
# АМ-сигнал и огибающие
axes[1].plot(
t_us, signal2,
linewidth=0.6,
label="АМ-сигнал"
)
axes[1].plot(t_us, envelope, "r--", label="Огибающие")
axes[1].plot(t_us, -envelope, "r--")
axes[1].set_title(
"Задание 2. Несущая 1 МГц, глубина модуляции 50%"
)
axes[1].legend()
# Увеличенный фрагмент
fragment = t2 <= 10e-6
axes[2].plot(
t_us[fragment],
signal2[fragment],
"o-",
markersize=2
)
axes[2].plot(
t_us[fragment], envelope[fragment], "r--"
)
axes[2].plot(
t_us[fragment], -envelope[fragment], "r--"
)
axes[2].set_title("Увеличенный фрагмент: первые 10 мкс")
for ax in axes:
ax.set_xlabel("Время, мкс")
ax.set_ylabel("Амплитуда, усл. ед.")
ax.grid()
save_figure(fig2, "task2_modulation.png")
# ==================================================
# ЗАДАНИЕ 3. РАЗЛОЖЕНИЕ ФУРЬЕ
# ==================================================
# Находим гармоники обоих сигналов
frequencies1, amplitudes1, phases1 = fourier(signal1, fs1)
frequencies2, amplitudes2, phases2 = fourier(signal2, fs2)
# Восстанавливаем сигналы суммированием гармоник
restored1 = restore(
fine_t1, frequencies1, amplitudes1, phases1
)
restored2 = restore(
t2, frequencies2, amplitudes2, phases2
)
fig3, axes = plt.subplots(2, 2, figsize=(13, 8))
# Восстановление синуса
axes[0, 0].plot(
plot_t1 * 1000,
plot_signal1,
"o",
label="Исходные отсчёты"
)
axes[0, 0].plot(
fine_t1 * 1000,
restored1,
label="Сумма гармоник"
)
axes[0, 0].set_title("Восстановление синуса")
axes[0, 0].set_xlabel("Время, мс")
axes[0, 0].legend()
# Спектр синуса
axes[0, 1].stem(
frequencies1, amplitudes1, basefmt="k-"
)
axes[0, 1].set_title("Гармоники синуса")
axes[0, 1].set_xlabel("Частота, Гц")
axes[0, 1].set_xlim(0, fs1 / 2)
# Восстановление АМ-сигнала
axes[1, 0].plot(
t_us, signal2,
label="Исходный АМ-сигнал"
)
axes[1, 0].plot(
t_us, restored2,
"--",
label="Сумма гармоник"
)
axes[1, 0].set_title("Восстановление АМ-сигнала")
axes[1, 0].set_xlabel("Время, мкс")
axes[1, 0].set_xlim(0, 10)
axes[1, 0].legend()
# Спектр АМ-сигнала
axes[1, 1].stem(
frequencies2 / 1000,
amplitudes2,
basefmt="k-"
)
axes[1, 1].set_title("Гармоники АМ-сигнала")
axes[1, 1].set_xlabel("Частота, кГц")
axes[1, 1].set_xlim(980, 1020)
for ax in axes.flat:
ax.set_ylabel("Амплитуда, усл. ед.")
ax.grid()
fig3.suptitle("Задание 3. Разложение Фурье")
save_figure(fig3, "task3_fourier.png")
# ==================================================
# РЕЗУЛЬТАТЫ В КОНСОЛИ
# ==================================================
print("\nЗадание 1:")
print(f"Частота синуса: {f} Гц")
print(f"Частота дискретизации: {fs1} Гц")
print(f"Шаг дискретизации: {1 / fs1:.6f} с")
print("\nЗадание 2:")
print(f"Частота несущей: {fc} Гц")
print(f"Частота модулирующего сигнала: {fm} Гц")
print(f"Глубина модуляции: {m * 100:.0f}%")
print(f"Минимальная амплитуда: {A * (1 - m)}")
print(f"Максимальная амплитуда: {A * (1 + m)}")
print("\nЗадание 3:")
for name, frequencies, amplitudes, phases in [
("Синус", frequencies1, amplitudes1, phases1),
("АМ-сигнал", frequencies2, amplitudes2, phases2)
]:
print("\n" + name)
for frequency, amplitude, phase in zip(
frequencies, amplitudes, phases
):
print(
f"Частота: {frequency:.0f} Гц; "
f"амплитуда: {amplitude:.3f}; "
f"фаза: {phase:.3f} рад"
)
# Проверяем совпадение на исходных отсчётах
error1 = np.max(np.abs(
signal1 - restore(
t1, frequencies1, amplitudes1, phases1
)
))
error2 = np.max(np.abs(signal2 - restored2))
print(f"\nПогрешность восстановления синуса: {error1:.3e}")
print(f"Погрешность восстановления АМ-сигнала: {error2:.3e}")