|
1 | | -# 5 вариант |
| 1 | +from typing import TYPE_CHECKING |
| 2 | +if TYPE_CHECKING: |
| 3 | + from icecream import ic # noqa: F401 |
2 | 4 |
|
3 | | -import matplotlib.pyplot as plt |
4 | | -from scipy.fftpack import fftfreq, fft |
5 | 5 | import numpy as np |
6 | | -from numpy import cos, sin, pi |
| 6 | +import utils.debug # noqa: F401 |
| 7 | +from matplotlib.axes import Axes # noqa: F401 |
7 | 8 |
|
8 | | -f = 3 |
9 | | -y = lambda t: cos(2*pi*f*t + pi/3) + sin(4+pi*f*t) |
| 9 | +from utils.complex_numbers import to_polar, to_rect # noqa: F401 |
| 10 | +from utils.harmonic import ( |
| 11 | + get_harmonic_oscillation, |
| 12 | + get_period, # noqa: F401 |
| 13 | + get_phase_time_shift, # noqa: F401 |
| 14 | + get_phase_at, # noqa: F401 |
| 15 | +) |
| 16 | +from utils.fourier import discrete_fourier_transform # noqa: F401 |
| 17 | +from utils.plotting import show_multi_plot, show_plot, show_subplots # noqa: F401 |
| 18 | +from utils.sampling import get_linspace |
10 | 19 |
|
11 | | -x = np.linspace(0, 10, 1000) |
12 | 20 |
|
13 | | -y_x = y(x) |
| 21 | +def task_1_2() -> None: |
| 22 | + sampling_frequency = 1024 |
| 23 | + start = 0 |
| 24 | + stop = 1 |
| 25 | + t = get_linspace(start=start, stop=stop, fs=sampling_frequency) |
| 26 | + |
| 27 | + # <------------------------------------> |
| 28 | + # 1) Необходимо сгенерировать и визуализировать непрерывный сигнал |
| 29 | + frequency = 4 |
| 30 | + |
| 31 | + ic( |
| 32 | + frequency, |
| 33 | + sampling_frequency, |
| 34 | + ) |
| 35 | + |
| 36 | + y_1 = get_harmonic_oscillation(1, frequency, np.pi/3, wave=np.cos)(t) |
| 37 | + y_2 = get_harmonic_oscillation(1, 2*frequency, 0, wave=np.sin)(t) |
| 38 | + y = y_1 + y_2 # 5 вариант |
14 | 39 |
|
15 | | -fig, ax = plt.subplots() |
16 | | -ax.plot(x, y_x) |
17 | | -ax.set(xlabel='t', ylabel='y', title='y = cos(2*pi*f*t + pi/3) + sin(4+pi*f*t)') |
| 40 | + show_plot( |
| 41 | + t=t, |
| 42 | + func_val=y, |
| 43 | + title="cos(2*pi*f*t + pi/3) + sin(4*pi*f*t)", |
| 44 | + ) |
| 45 | + # >------------------------------------< |
| 46 | + |
18 | 47 |
|
19 | | -plt.show() |
| 48 | + # <------------------------------------> |
| 49 | + # 2) Определить максимальную частоту в спектре данного сигнала. |
| 50 | + max_freq = frequency * 2 |
| 51 | + # >------------------------------------< |
| 52 | + |
| 53 | + |
| 54 | + # <------------------------------------> |
| 55 | + # 3) Определить минимальную необходимую частоту дискретизации |
| 56 | + # полученного сигнала (теорема Котельникова) |
| 57 | + min_freq_kotelnikov = max_freq * 2 |
| 58 | + |
| 59 | + ic( |
| 60 | + max_freq, |
| 61 | + min_freq_kotelnikov, |
| 62 | + ) |
| 63 | + # >------------------------------------< |
| 64 | + |
| 65 | + |
| 66 | + # <------------------------------------> |
| 67 | + # 4) Оцифровать сигнал с полученной частотой дискретизации, выбрав |
| 68 | + # требуемое число отсчетов сигнала на длительности 1 секунда, сохранить |
| 69 | + # полученные значения в массив, пока не озадачиваясь разрядностью АЦП, |
| 70 | + # просто выбранные с частотой дискретизации значения с теми уровнями, |
| 71 | + # которые имеет функция в полученных точках. |
| 72 | + step = len(y) // int(min_freq_kotelnikov * (stop - start)) |
| 73 | + y_sampled = y[::step] |
| 74 | + t_sampled = t[::step] |
| 75 | + |
| 76 | + ic( |
| 77 | + y_sampled, |
| 78 | + t_sampled, |
| 79 | + ) |
| 80 | + # >------------------------------------< |
| 81 | + |
| 82 | + |
| 83 | + # <------------------------------------> |
| 84 | + # 5) Выполнить прямое дискретное преобразование Фурье для массива |
| 85 | + # временных отсчетов сигнала и оценить ширину данного спектра. А также |
| 86 | + # объем памяти, требуемый для хранения данного массива (тип переменных в |
| 87 | + # массиве – на ваше усмотрение, float, int и пр.). |
| 88 | + y_dft = discrete_fourier_transform(y_sampled) |
| 89 | + N = len(y_dft) |
| 90 | + freqs = np.arange(N) * min_freq_kotelnikov / N |
| 91 | + amp = np.abs(y_dft) / N |
| 92 | + |
| 93 | + ic( |
| 94 | + freqs, |
| 95 | + amp, |
| 96 | + ) |
20 | 97 |
|
21 | | -max_freq = f*2 |
| 98 | + threshold = 1e-6 |
| 99 | + half = N // 2 + 1 # без зеркальной части |
| 100 | + significant = np.array([freqs[k] for k in range(half) if amp[k] > threshold]) |
| 101 | + spectrum_width = significant.max() |
| 102 | + |
| 103 | + ic( |
| 104 | + spectrum_width |
| 105 | + ) |
22 | 106 |
|
23 | | -min_freq_kotelnikov = max_freq * 2 |
| 107 | + def draw_signal(ax: Axes) -> None: |
| 108 | + ax.plot(t, y) |
| 109 | + ax.stem(t_sampled, y_sampled, linefmt="r-", markerfmt="ro", basefmt=" ") |
24 | 110 |
|
| 111 | + def draw_full_spectrum(ax: Axes) -> None: |
| 112 | + ax.stem(freqs, amp) |
| 113 | + |
| 114 | + def draw_half_spectrum(ax: Axes) -> None: |
| 115 | + ax.stem(freqs[:half], amp[:half]) |
| 116 | + ax.axhline(threshold, color="gray", linestyle="--", label="порог") |
| 117 | + ax.axvline(spectrum_width, color="r", linestyle=":", label="ширина спектра") |
| 118 | + ax.legend() |
| 119 | + |
| 120 | + show_subplots( |
| 121 | + draw_funcs=[draw_signal, draw_full_spectrum, draw_half_spectrum], |
| 122 | + titles=[ |
| 123 | + "Сигнал и отсчеты", |
| 124 | + "Амплитудный спектр, все N гармоник (зеркальная часть)", |
| 125 | + "Амплитудный спектр без зеркальной части", |
| 126 | + ], |
| 127 | + figsize=(9, 10), |
| 128 | + ) |
| 129 | + # >------------------------------------< |
| 130 | + |
| 131 | + |
| 132 | +def main() -> None: |
| 133 | + task_1_2() |
| 134 | + |
| 135 | + |
| 136 | +if __name__ == "__main__": |
| 137 | + try: |
| 138 | + main() |
| 139 | + except KeyboardInterrupt: |
| 140 | + pass |
0 commit comments