Created
April 4, 2026 14:01
-
-
Save shaposhnikoff/0b6ebfbf8e1351ba2b08a0a1a13fa0d3 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| { | |
| "cells": [ | |
| { | |
| "cell_type": "markdown", | |
| "metadata": {}, | |
| "source": [ | |
| "# Калькулятор затухання кабелю RG-223/U\n", | |
| "\n", | |
| "Дані: затухання в дБ/100м по частотних точках (номінальні значення)." | |
| ] | |
| }, | |
| { | |
| "cell_type": "code", | |
| "execution_count": null, | |
| "metadata": {}, | |
| "outputs": [], | |
| "source": [ | |
| "# Встановлення залежностей (якщо потрібно)\n", | |
| "# !pip install numpy matplotlib ipywidgets scipy\n", | |
| "\n", | |
| "import numpy as np\n", | |
| "import matplotlib.pyplot as plt\n", | |
| "import matplotlib.ticker as ticker\n", | |
| "from scipy.interpolate import CubicSpline\n", | |
| "import ipywidgets as widgets\n", | |
| "from IPython.display import display, clear_output\n", | |
| "\n", | |
| "%matplotlib inline\n", | |
| "plt.rcParams.update({\n", | |
| " 'figure.dpi': 120,\n", | |
| " 'font.family': 'DejaVu Sans',\n", | |
| " 'axes.grid': True,\n", | |
| " 'grid.alpha': 0.3,\n", | |
| " 'axes.spines.top': False,\n", | |
| " 'axes.spines.right': False,\n", | |
| "})" | |
| ] | |
| }, | |
| { | |
| "cell_type": "code", | |
| "execution_count": null, | |
| "metadata": {}, | |
| "outputs": [], | |
| "source": [ | |
| "# ─── Таблиця втрат RG-223/U ───────────────────────────────────────────────────\n", | |
| "CABLE_DATA = {\n", | |
| " 'RG-223/U': {\n", | |
| " 'color': '#1f77b4',\n", | |
| " 'impedance': 50,\n", | |
| " 'freq_mhz': np.array([ 100, 400, 1000, 2600, 3800, 6000]),\n", | |
| " 'loss_db': np.array([13.9, 29.4, 49.4, 87.4, 110.8, 148.8]),\n", | |
| " },\n", | |
| "}\n", | |
| "\n", | |
| "# Побудова інтерполяційних сплайнів\n", | |
| "SPLINES = {}\n", | |
| "for name, d in CABLE_DATA.items():\n", | |
| " SPLINES[name] = CubicSpline(d['freq_mhz'], d['loss_db'], extrapolate=False)\n", | |
| "\n", | |
| "def interpolate_loss(cable: str, freq_mhz: float) -> float:\n", | |
| " \"\"\"Повертає втрати дБ/100м для заданої частоти (лінійна екстраполяція за межами даних).\"\"\"\n", | |
| " d = CABLE_DATA[cable]\n", | |
| " if freq_mhz <= d['freq_mhz'][0]:\n", | |
| " return float(d['loss_db'][0])\n", | |
| " if freq_mhz >= d['freq_mhz'][-1]:\n", | |
| " return float(d['loss_db'][-1])\n", | |
| " return float(SPLINES[cable](freq_mhz))\n", | |
| "\n", | |
| "def total_loss(cable: str, freq_mhz: float, length_m: float) -> float:\n", | |
| " \"\"\"Повні втрати в дБ для заданої довжини кабелю.\"\"\"\n", | |
| " return interpolate_loss(cable, freq_mhz) * length_m / 100.0\n", | |
| "\n", | |
| "def power_ratio(loss_db: float) -> float:\n", | |
| " \"\"\"Відношення вихідної потужності до вхідної (0..1).\"\"\"\n", | |
| " return 10 ** (-loss_db / 10)\n", | |
| "\n", | |
| "def output_power_w(input_w: float, loss_db: float) -> float:\n", | |
| " return input_w * power_ratio(loss_db)\n", | |
| "\n", | |
| "print('Дані завантажено. Готово до розрахунків.')" | |
| ] | |
| }, | |
| { | |
| "cell_type": "markdown", | |
| "metadata": {}, | |
| "source": [ | |
| "## Графік затухання vs частота" | |
| ] | |
| }, | |
| { | |
| "cell_type": "code", | |
| "execution_count": null, | |
| "metadata": {}, | |
| "outputs": [], | |
| "source": [ | |
| "fig, ax = plt.subplots(figsize=(10, 5))\n", | |
| "\n", | |
| "for name, d in CABLE_DATA.items():\n", | |
| " # Суцільна крива через сплайн\n", | |
| " f_fine = np.linspace(d['freq_mhz'][0], d['freq_mhz'][-1], 500)\n", | |
| " l_fine = SPLINES[name](f_fine)\n", | |
| " ax.plot(f_fine, l_fine, color=d['color'], lw=2, label=name)\n", | |
| " # Вузлові точки\n", | |
| " ax.scatter(d['freq_mhz'], d['loss_db'], color=d['color'], s=60, zorder=5)\n", | |
| "\n", | |
| " # Підписи до точок\n", | |
| " for f, l in zip(d['freq_mhz'], d['loss_db']):\n", | |
| " ax.annotate(f'{l}', xy=(f, l), xytext=(0, 8),\n", | |
| " textcoords='offset points', ha='center', fontsize=8,\n", | |
| " color=d['color'])\n", | |
| "\n", | |
| "ax.set_xlabel('Частота, МГц', fontsize=12)\n", | |
| "ax.set_ylabel('Затухання, дБ/100 м', fontsize=12)\n", | |
| "ax.set_title('Затухання кабелю RG-223/U', fontsize=13, fontweight='bold')\n", | |
| "ax.legend(fontsize=11)\n", | |
| "ax.xaxis.set_major_formatter(ticker.FuncFormatter(lambda x, _: f'{int(x):,}'))\n", | |
| "plt.tight_layout()\n", | |
| "plt.show()" | |
| ] | |
| }, | |
| { | |
| "cell_type": "markdown", | |
| "metadata": {}, | |
| "source": [ | |
| "## Інтерактивний калькулятор" | |
| ] | |
| }, | |
| { | |
| "cell_type": "code", | |
| "execution_count": null, | |
| "metadata": {}, | |
| "outputs": [], | |
| "source": [ | |
| "# ─── Widgets ──────────────────────────────────────────────────────────────────\n", | |
| "style = {'description_width': '200px'}\n", | |
| "layout = widgets.Layout(width='420px')\n", | |
| "\n", | |
| "w_cable = widgets.Dropdown(\n", | |
| " options=list(CABLE_DATA.keys()), value='RG-223/U',\n", | |
| " description='Кабель:', style=style, layout=layout)\n", | |
| "\n", | |
| "w_freq = widgets.FloatSlider(\n", | |
| " value=435, min=50, max=6000, step=1,\n", | |
| " description='Частота, МГц:', readout_format='.0f',\n", | |
| " style=style, layout=layout)\n", | |
| "\n", | |
| "w_length = widgets.FloatSlider(\n", | |
| " value=10, min=0.5, max=200, step=0.5,\n", | |
| " description='Довжина кабелю, м:', readout_format='.1f',\n", | |
| " style=style, layout=layout)\n", | |
| "\n", | |
| "w_power = widgets.FloatSlider(\n", | |
| " value=100, min=1, max=1500, step=1,\n", | |
| " description='Потужність TX, Вт:', readout_format='.0f',\n", | |
| " style=style, layout=layout)\n", | |
| "\n", | |
| "out = widgets.Output()\n", | |
| "\n", | |
| "# ─── Callback ─────────────────────────────────────────────────────────────────\n", | |
| "def update(_change=None):\n", | |
| " cable = w_cable.value\n", | |
| " freq = w_freq.value\n", | |
| " length = w_length.value\n", | |
| " pwr_in = w_power.value\n", | |
| "\n", | |
| " loss_per_100 = interpolate_loss(cable, freq)\n", | |
| " loss_total = total_loss(cable, freq, length)\n", | |
| " pwr_out = output_power_w(pwr_in, loss_total)\n", | |
| " eff = pwr_out / pwr_in * 100\n", | |
| "\n", | |
| " with out:\n", | |
| " clear_output(wait=True)\n", | |
| "\n", | |
| " # ── текстовий результат ──\n", | |
| " print('─' * 52)\n", | |
| " print(f' Кабель : {cable}')\n", | |
| " print(f' Частота : {freq:.0f} МГц')\n", | |
| " print(f' Довжина : {length:.1f} м')\n", | |
| " print(f' Потужність TX : {pwr_in:.0f} Вт')\n", | |
| " print('─' * 52)\n", | |
| " print(f' Втрати /100м : {loss_per_100:.2f} дБ')\n", | |
| " print(f' Втрати всього : {loss_total:.3f} дБ')\n", | |
| " print(f' Потужність на антені: {pwr_out:.2f} Вт ({eff:.1f}%)')\n", | |
| " print('─' * 52)\n", | |
| "\n", | |
| " # ── графік з маркером ──\n", | |
| " fig, axes = plt.subplots(1, 2, figsize=(12, 4))\n", | |
| "\n", | |
| " # Ліво: затухання vs частота\n", | |
| " ax1 = axes[0]\n", | |
| " d = CABLE_DATA[cable]\n", | |
| " f_fine = np.linspace(d['freq_mhz'][0], d['freq_mhz'][-1], 500)\n", | |
| " ax1.plot(f_fine, SPLINES[cable](f_fine),\n", | |
| " color=d['color'], lw=2, label=cable)\n", | |
| " ax1.scatter(d['freq_mhz'], d['loss_db'],\n", | |
| " color=d['color'], s=50, zorder=5)\n", | |
| " # Маркер поточної частоти\n", | |
| " ax1.scatter([freq], [loss_per_100], color='#d62728',\n", | |
| " s=120, zorder=10, label=f'{freq:.0f} МГц → {loss_per_100:.1f} дБ/100м')\n", | |
| " ax1.axvline(freq, color='#d62728', lw=1, ls='--', alpha=0.5)\n", | |
| " ax1.axhline(loss_per_100, color='#d62728', lw=1, ls='--', alpha=0.5)\n", | |
| " ax1.set_xlabel('Частота, МГц')\n", | |
| " ax1.set_ylabel('дБ / 100 м')\n", | |
| " ax1.set_title('Втрати vs частота')\n", | |
| " ax1.legend(fontsize=9)\n", | |
| " ax1.grid(True, alpha=0.3)\n", | |
| " ax1.spines[['top', 'right']].set_visible(False)\n", | |
| "\n", | |
| " # Право: потужність vs довжина\n", | |
| " ax2 = axes[1]\n", | |
| " lengths = np.linspace(0.5, max(200, length * 1.3), 300)\n", | |
| " pwr_curve = [output_power_w(pwr_in, total_loss(cable, freq, l)) for l in lengths]\n", | |
| " ax2.plot(lengths, pwr_curve, color='#2ca02c', lw=2)\n", | |
| " ax2.scatter([length], [pwr_out], color='#d62728', s=120, zorder=10,\n", | |
| " label=f'{length:.1f} м → {pwr_out:.1f} Вт')\n", | |
| " ax2.axvline(length, color='#d62728', lw=1, ls='--', alpha=0.5)\n", | |
| " ax2.axhline(pwr_out, color='#d62728', lw=1, ls='--', alpha=0.5)\n", | |
| " ax2.set_xlabel('Довжина кабелю, м')\n", | |
| " ax2.set_ylabel('Потужність на антені, Вт')\n", | |
| " ax2.set_title(f'Потужність на виході ({freq:.0f} МГц, вхід {pwr_in:.0f} Вт)')\n", | |
| " ax2.legend(fontsize=9)\n", | |
| " ax2.grid(True, alpha=0.3)\n", | |
| " ax2.spines[['top', 'right']].set_visible(False)\n", | |
| "\n", | |
| " plt.tight_layout()\n", | |
| " plt.show()\n", | |
| "\n", | |
| "# Підписка на зміни\n", | |
| "for w in [w_cable, w_freq, w_length, w_power]:\n", | |
| " w.observe(update, names='value')\n", | |
| "\n", | |
| "# Початковий рендер\n", | |
| "display(widgets.VBox([w_cable, w_freq, w_length, w_power]))\n", | |
| "display(out)\n", | |
| "update()" | |
| ] | |
| }, | |
| { | |
| "cell_type": "markdown", | |
| "metadata": {}, | |
| "source": [ | |
| "## Швидкий розрахунок без слайдерів" | |
| ] | |
| }, | |
| { | |
| "cell_type": "code", | |
| "execution_count": null, | |
| "metadata": {}, | |
| "outputs": [], | |
| "source": [ | |
| "# Ручний розрахунок: змінюй параметри нижче\n", | |
| "FREQ_MHZ = 435.0 # МГц\n", | |
| "LENGTH_M = 15.0 # метрів\n", | |
| "POWER_W = 100.0 # Вт TX\n", | |
| "CABLE = 'RG-223/U'\n", | |
| "\n", | |
| "l_per_100 = interpolate_loss(CABLE, FREQ_MHZ)\n", | |
| "l_total = total_loss(CABLE, FREQ_MHZ, LENGTH_M)\n", | |
| "p_out = output_power_w(POWER_W, l_total)\n", | |
| "\n", | |
| "print(f'Кабель : {CABLE}')\n", | |
| "print(f'Частота : {FREQ_MHZ} МГц')\n", | |
| "print(f'Довжина : {LENGTH_M} м')\n", | |
| "print(f'Потужність TX: {POWER_W} Вт')\n", | |
| "print()\n", | |
| "print(f'Втрати/100м : {l_per_100:.2f} дБ')\n", | |
| "print(f'Втрати разом : {l_total:.3f} дБ')\n", | |
| "print(f'P на антені : {p_out:.2f} Вт ({p_out/POWER_W*100:.1f}%)')" | |
| ] | |
| }, | |
| { | |
| "cell_type": "markdown", | |
| "metadata": {}, | |
| "source": [ | |
| "## Порівняльна таблиця по аматорським діапазонам" | |
| ] | |
| }, | |
| { | |
| "cell_type": "code", | |
| "execution_count": null, | |
| "metadata": {}, | |
| "outputs": [], | |
| "source": [ | |
| "import pandas as pd\n", | |
| "\n", | |
| "BANDS = [\n", | |
| " ('160м', 1.85),\n", | |
| " ('80м', 3.65),\n", | |
| " ('40м', 7.1),\n", | |
| " ('20м', 14.2),\n", | |
| " ('17м', 18.1),\n", | |
| " ('15м', 21.2),\n", | |
| " ('12м', 24.9),\n", | |
| " ('10м', 28.5),\n", | |
| " ('6м', 50.1),\n", | |
| " ('2м', 144.5),\n", | |
| " ('70см', 432.0),\n", | |
| " ('23см', 1296.0),\n", | |
| "]\n", | |
| "\n", | |
| "LENGTHS = [5, 10, 20, 50]\n", | |
| "POWER_W = 100\n", | |
| "\n", | |
| "rows = []\n", | |
| "for band, freq in BANDS:\n", | |
| " row = {'Діапазон': band, 'Частота, МГц': freq,\n", | |
| " 'дБ/100м': f\"{interpolate_loss('RG-223/U', freq):.1f}\"}\n", | |
| " for l in LENGTHS:\n", | |
| " loss = total_loss('RG-223/U', freq, l)\n", | |
| " pout = output_power_w(POWER_W, loss)\n", | |
| " row[f'{l}м: дБ / Вт'] = f\"{loss:.2f} / {pout:.1f}\"\n", | |
| " rows.append(row)\n", | |
| "\n", | |
| "df = pd.DataFrame(rows)\n", | |
| "df.index = df.index + 1\n", | |
| "print(f'Вхідна потужність: {POWER_W} Вт | Кабель: RG-223/U')\n", | |
| "display(df)" | |
| ] | |
| } | |
| ], | |
| "metadata": { | |
| "kernelspec": { | |
| "display_name": "Python 3", | |
| "language": "python", | |
| "name": "python3" | |
| }, | |
| "language_info": { | |
| "name": "python", | |
| "version": "3.10.0" | |
| } | |
| }, | |
| "nbformat": 4, | |
| "nbformat_minor": 5 | |
| } | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment