Побудова графіка імпедансу мікросмужки залежно від ширини за допомогою UliEngineering

Наведені нижче графіки надають простий спосіб візуалізації залежності між шириною мікросмужки та імпедансом для різних висот та товщин підложки.

Див. Prepreg and core thickness for different standard stackups для отримання додаткової інформації про стандартні товщини стеків друкованих плат та значення $\epsilon_r$.

Microstrip Impedance.svg

Інший спосіб поглянути на ті самі дані — побудувати графік товщини діелектрика залежно від ширини доріжки для заданих значень імпедансу:

Microstrip Width mm.svg

або у mil:

Microstrip Width Mil.svg

Базовий приклад

Цей скрипт на Python показує, як обчислити імпеданс мікросмужки для одного набору параметрів за допомогою бібліотеки UliEngineering.

example_microstrip_width_calculation.py
#!/usr/bin/env python3
from UliEngineering.Electronics.Microstrip import microstrip_width
from UliEngineering.EngineerIO import format_value
from UliEngineering.EngineerIO.Length import convert_length_to_unit

# Обчислити ширину для 50 Ω мікросмужки, використовуючи рядкові аргументи з одиницями
w = microstrip_width("50 Ω", h="150 um", t="35 um", e_r="4.4")
# Перетворити результат (метри) у mils та вивести
w_mil = convert_length_to_unit(w, "m", "mil")
print(format_value(w_mil, "mil"))

Вивід скрипта прикладу

output.txt
10.2 mil

Вихідний код графіка

Цей скрипт використовується для створення графіка імпедансу залежно від ширини, показаного вище.

plot_microstrip_impedance_vs_width.py
#!/usr/bin/env python3
# SPDX-License-Identifier: CC0-1.0
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.ticker import FuncFormatter, AutoMinorLocator

# Імпорти UliEngineering
from UliEngineering.Electronics.Microstrip import microstrip_impedance
from UliEngineering.EngineerIO import format_value
from UliEngineering.EngineerIO.Length import normalize_length

# Константи
e_r = 4.4
thicknesses = ["100um", "150um", "200um", "1550um"]
# Товщина міді (за замовчуванням 35um, оскільки не була вказана, але потрібна для обчислення)
t_copper = "35um"

# Вісь ширини: від 1mil до 10mm, лінійна
w_min = normalize_length("1mil")
w_max = normalize_length("10mm")
# Для логарифмічної осі X потрібні ширини, розташовані логарифмічно; використовуємо np.logspace
widths = np.logspace(np.log10(w_min), np.log10(w_max), 1000)

# Побудова графіка
plt.style.use("ggplot")
fig, ax = plt.subplots(figsize=(10, 12))

# Згенерувати кольори
colors = plt.cm.viridis(np.linspace(0, 1, len(thicknesses)))

for h_str, c in zip(thicknesses, colors):
    # Обчислити імпеданс для кожної ширини
    # microstrip_impedance використовує математичні функції, тому не підтримує numpy-масиви безпосередньо
    zs = [microstrip_impedance(w, h=h_str, t=t_copper, e_r=e_r) for w in widths]
    
    ax.plot(widths, zs, color=c, lw=2, label=f"h={h_str}")

# Додати легенду
legend = ax.legend(loc='upper right', fontsize='small')

ax.set_xscale("log")
ax.set_xlabel("Ширина (логарифмічна шкала)")
ax.set_ylabel("Імпеданс")
ax.set_title(rf"Імпеданс мікросмужки залежно від ширини ($\epsilon_r={e_r}$)")
ax.grid(True, which="both", ls="--")

# Форматтери осей з використанням EngineerIO format_value
ax.xaxis.set_major_formatter(FuncFormatter(lambda x, pos: format_value(x, "m")))
ax.yaxis.set_major_formatter(FuncFormatter(lambda y, pos: format_value(y, "Ω")))

# Увімкнути другорядні поділки та додати мітки другорядних поділок на осі Y
ax.minorticks_on()
# Для лінійної шкали AutoMinorLocator зазвичай підходить, або можна дозволити matplotlib обробити це.
# Шаблон використовував LogLocator, але ми маємо лінійну шкалу.
ax.yaxis.set_minor_locator(AutoMinorLocator())
ax.yaxis.set_minor_formatter(FuncFormatter(lambda y, pos: format_value(y, "Ω")))

# Стилізувати мітки другорядних поділок: на 30% менші та 70% сірого
maj_ylabels = ax.yaxis.get_ticklabels(which='major')
if len(maj_ylabels) > 0:
    base_size = maj_ylabels[0].get_size()
else:
    base_size = plt.rcParams.get('ytick.labelsize', plt.rcParams.get('font.size', 10))
ax.tick_params(axis='y', which='minor', labelsize=base_size * 0.7, labelcolor='0.7', colors='0.7')

# Також встановити другорядні поділки осі X та стилізувати їх меншими повернутими мітками
from matplotlib.ticker import LogLocator
ax.xaxis.set_minor_locator(LogLocator(base=10.0, subs=(2, 3, 4, 5, 6, 7, 8, 9)))
ax.xaxis.set_minor_formatter(FuncFormatter(lambda x, pos: format_value(x, "m")))

maj_xlabels = ax.xaxis.get_ticklabels(which='major')
if len(maj_xlabels) > 0:
    base_x_size = maj_xlabels[0].get_size()
else:
    base_x_size = plt.rcParams.get('xtick.labelsize', plt.rcParams.get('font.size', 10))
ax.tick_params(axis='x', which='minor', labelsize=base_x_size * 0.7, labelcolor='0.7', colors='0.7')

for tl in ax.get_xminorticklabels():
    tl.set_rotation(90)
for tl in ax.get_xmajorticklabels():
    tl.set_rotation(90)

fig.tight_layout()
plt.show()
plt.savefig("Microstrip-Impedance.svg")

З іншого боку, наступний скрипт створює графік товщини діелектрика для заданих значень імпедансу (другий і третій графіки, показані вище, причому третій згенеровано за допомогою --y-unit mil).

plot_microstrip_width_vs_thickness.py
#!/usr/bin/env python3
# SPDX-License-Identifier: CC0-1.0
import argparse
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.ticker import FuncFormatter, LogLocator

# Імпорти UliEngineering
from UliEngineering.Electronics.Microstrip import microstrip_width
from UliEngineering.EngineerIO import format_value
from UliEngineering.EngineerIO.Length import normalize_length, convert_length_to_unit

# Командний рядок: вибір одиниць осі Y (метри за замовчуванням, або "mil")
parser = argparse.ArgumentParser(description="Побудова графіка ширини мікросмужки залежно від товщини діелектрика")
parser.add_argument("--y-unit", choices=("m", "mil"), default="m",
                    help="Одиниці для осі Y: 'm' для метрів (за замовчуванням) або 'mil' для mils")
args = parser.parse_args()
y_unit = args.y_unit

# Константи
e_r = 4.4
impedances = [50, 75, 100]
# Товщина міді
t_copper = "35um"

# Вісь товщини: від 70um до 2.0mm (логарифмічна вибірка, щоб значення були рівномірно розташовані на логарифмічній осі X)
h_min = normalize_length("70um")
h_max = normalize_length("2mm")
thicknesses = np.logspace(np.log10(h_min), np.log10(h_max), num=1000)

# Побудова графіка
plt.style.use("ggplot")
fig, ax = plt.subplots(figsize=(10, 8))

# Згенерувати кольори за допомогою viridis для приємніших, перцептивно-рівномірних кольорів
colors = plt.cm.viridis(np.linspace(0, 1, len(impedances)))

# Обчислити криві ширини та зібрати їх для визначення меж осі Y за необхідності
all_ws = []
for z0, c in zip(impedances, colors):
    ws = [microstrip_width(Z0=z0, h=h, t=t_copper, e_r=e_r) for h in thicknesses]
    ax.plot(thicknesses, ws, color=c, lw=2, label=f"Z0={z0} Ω")
    all_ws.append(np.array(ws))

# Додати легенду
legend = ax.legend(loc='upper left', fontsize='medium')

ax.set_xlabel("Товщина діелектрика")
ax.set_ylabel(f"Ширина доріжки ({'m' if y_unit == 'm' else 'mil'})")
ax.set_title(rf"Ширина мікросмужки залежно від товщини діелектрика ($\epsilon_r={e_r}$, $t_{{Cu}}={t_copper}$)")
ax.grid(True, which="both", ls="--")

# Логарифмічне масштабування на обох осях
ax.set_xscale("log")
ax.set_yscale("log")

# Встановити межі X точно від 70 µm до 2.0 mm
ax.set_xlim(h_min, h_max)

# Налаштувати основні та другорядні локатори для логарифмічних шкал
major_x_locator = LogLocator(base=10.0)
minor_x_locator = LogLocator(base=10.0, subs=(2, 3, 4, 5, 6, 7, 8, 9))
major_y_locator = LogLocator(base=10.0)
minor_y_locator = LogLocator(base=10.0, subs=(2, 3, 4, 5, 6, 7, 8, 9))
ax.xaxis.set_major_locator(major_x_locator)
ax.xaxis.set_minor_locator(minor_x_locator)
ax.yaxis.set_major_locator(major_y_locator)
ax.yaxis.set_minor_locator(minor_y_locator)

# Форматтери осей з використанням EngineerIO format_value
ax.xaxis.set_major_formatter(FuncFormatter(lambda x, pos: format_value(x, "m")))

# Форматтер осі Y залежить від запитаних одиниць.
if y_unit == "m":
    ax.yaxis.set_major_formatter(FuncFormatter(lambda y, pos: format_value(y, "m")))
    ax.yaxis.set_minor_formatter(FuncFormatter(lambda y, pos: format_value(y, "m")))
else:
    # Форматувати значення у mils без SI-префіксів за допомогою допоміжної функції
    def format_mil_no_prefix(y, pos):
        mils = convert_length_to_unit(y, "m", "mil")
        if mils >= 100:
            s = f"{int(round(mils))} mil"
        elif mils >= 10:
            s = f"{mils:.1f} mil"
        elif mils >= 1:
            s = f"{mils:.2f} mil"
        else:
            s = f"{mils:.3f} mil"
        return s

    ax.yaxis.set_major_formatter(FuncFormatter(format_mil_no_prefix))
    ax.yaxis.set_minor_formatter(FuncFormatter(format_mil_no_prefix))

# Увімкнути другорядні поділки
ax.minorticks_on()
ax.xaxis.set_minor_formatter(FuncFormatter(lambda x, pos: format_value(x, "m")))

# Обчислити та встановити поділки X, обмежені запитаним діапазоном X
major_locs = major_x_locator.tick_values(h_min, h_max)
minor_locs = minor_x_locator.tick_values(h_min, h_max)
major_locs = np.array([m for m in major_locs if m >= h_min and m <= h_max])
minor_locs = np.array([m for m in minor_locs if m >= h_min and m <= h_max])

# Додати спеціальну другорядну поділку при X = 1.55 mm
special_tick = normalize_length("1.55mm")
if special_tick not in minor_locs:
    minor_locs = np.sort(np.concatenate((minor_locs, [special_tick])))

# Встановити поділки явно
ax.set_xticks(major_locs, minor=False)
ax.set_xticks(minor_locs, minor=True)

# Також намалювати тонку вертикальну напрямну при 1.55 mm, щоб підкреслити спеціальну точку
ax.axvline(special_tick, color='0.5', linestyle=':', linewidth=1)

# обчислити діапазон Y з даних та встановити відповідні поділки у режимі mil
if len(all_ws) > 0:
    y_all = np.concatenate(all_ws)
    y_all = y_all[y_all > 0]
    if len(y_all) > 0:
        y_min = np.min(y_all)
        y_max = np.max(y_all)
        # відступи
        y_pad = (np.log10(y_max) - np.log10(y_min)) * 0.05 if y_max > y_min else 0.1
        ax.set_ylim(10 ** (np.log10(y_min) - y_pad), 10 ** (np.log10(y_max) + y_pad))

        if y_unit == 'mil':
            yl, yu = ax.get_ylim()
            yl_mil = convert_length_to_unit(yl, 'm', 'mil')
            yu_mil = convert_length_to_unit(yu, 'm', 'mil')
            # вибрати основні декади у mils (10, 100, 1000, ...)
            dmin = int(np.floor(np.log10(max(yl_mil, 1e-12))))
            dmax = int(np.ceil(np.log10(max(yu_mil, 1e-12))))
            # Забезпечити включення 1 mil (10^0) як основної поділки за можливості
            dstart = max(0, dmin)
            majors_mil = [10 ** d for d in range(dstart, dmax + 1)]
            majors_m = [convert_length_to_unit(m, 'mil', 'm') for m in majors_mil]
            minors_m = []
            for d in range(dstart, dmax + 1):
                for s in (2, 3, 4, 5, 6, 7, 8, 9):
                    v_mil = s * (10 ** d)
                    minors_m.append(convert_length_to_unit(v_mil, 'mil', 'm'))
            majors_m = np.array([m for m in majors_m if m >= yl and m <= yu])
            minors_m = np.array([m for m in minors_m if m >= yl and m <= yu])
            ax.set_yticks(majors_m, minor=False)
            ax.set_yticks(minors_m, minor=True)

# Повернути мітки за потреби
for tl in ax.get_xminorticklabels():
    tl.set_rotation(90)
for tl in ax.get_xmajorticklabels():
    tl.set_rotation(90)

# Стилізувати мітки другорядних поділок: на 30% менші та 70% сірого (обидві осі)
maj_ylabels = ax.yaxis.get_ticklabels(which='major')
if len(maj_ylabels) > 0:
    base_y_size = maj_ylabels[0].get_size()
else:
    base_y_size = plt.rcParams.get('ytick.labelsize', plt.rcParams.get('font.size', 10))
maj_xlabels = ax.xaxis.get_ticklabels(which='major')
if len(maj_xlabels) > 0:
    base_x_size = maj_xlabels[0].get_size()
else:
    base_x_size = plt.rcParams.get('xtick.labelsize', plt.rcParams.get('font.size', 10))
ax.tick_params(axis='y', which='minor', labelsize=base_y_size * 0.7, labelcolor='0.7', colors='0.7')
ax.tick_params(axis='x', which='minor', labelsize=base_x_size * 0.7, labelcolor='0.7', colors='0.7')

fig.tight_layout()
plt.savefig("Microstrip-Width.svg", dpi=300, bbox_inches='tight')
# Показувати інтерактивно лише коли не запущено у headless CI
try:
    plt.show()
except Exception:
    pass

Дивіться схожі статті за категоріями: Electronics, RF