عدد ۶۴ کیلوبیت‌برثانیه برای صوت کدشده، به‌تنهایی نرخ بسته‌های IP را مشخص نمی‌کند. مدت صدای داخل هر بسته و سرآیندهای افزوده‌شده نیز لازم‌اند. این درس برای صوت تک‌کانالهٔ PCMU یک حساب دقیق و یک مسئلهٔ کوچکِ انتخاب گزینه می‌سازد. برنامه رمزگذار، فایل صوتی یا فرستندهٔ شبکه نیست؛ اندازه و نرخِ دوره‌ای طبق فرض‌های اعلام‌شده محاسبه می‌شوند.

۱. صوت کدشده را از سربار بسته جدا کنیم

محمولهٔ صوت در لایه‌های سرآیند شبکه قرار می‌گیرد
تصویر مفهومی: محمولهٔ صوت در لایه‌های سرآیند شبکه قرار می‌گیرد؛ بدون مقیاس عددی و مستقل از نتایج اجرای کد.

پروفایل ثابتِ تمرین، ۸۰۰۰ نمونه در ثانیه و یک بایت برای هر نمونهٔ از قبل کدشده دارد. بخش PCMU در RFC3551 هشت بیت پس از مقیاس‌گذاری لگاریتمی و جدول همان سند، نرخ ساعت هشت‌هزار و یک کانال را مشخص می‌کنند. این محموله، PCM خطیِ هشت‌بیتی نیست؛ تابع تبدیل دامنه یا رمزگشاییِ قانون مو در کد نداریم.

نرخ محموله برابر هشت‌هزار ضرب‌در هشت، یعنی ۶۴۰۰۰ بیت‌برثانیه است. همهٔ عددهای کیلوبیت در این مقاله پایهٔ هزار دارند. ارسال را پیوسته و بدون حذف سکوت فرض می‌کنیم؛ بسته‌ها مدت ثابت دارند و فقط یک جهتِ جریان حساب می‌شود. رفت‌وبرگشت واقعی، کنترل تماس و بسته‌های کنترلی از این فرض‌ها به‌طور خودکار به دست نمی‌آیند.

در درس PCM16 و ظرف WAV، نمونهٔ خطی شانزده‌بیتی دو بایت می‌گرفت و سربار شبکه جدا بود. اینجا نمونهٔ کدشده یک بایت می‌گیرد، اما ظرف فایل WAV نمی‌سازیم. مقایسهٔ تعداد بایت‌ها، ادعای برابری کیفیت دو نمایش یا نتیجهٔ تبدیل میان آن‌ها نیست.

۲. مدت بسته را به نمونه و بایت تبدیل کنیم

نمونه‌های صوت برای مدت مشخص در یک بسته جمع می‌شوند
تصویر مفهومی: نمونه‌های صوت برای مدت مشخص در یک بسته جمع می‌شوند؛ بدون مقیاس عددی و مستقل از نتایج اجرای کد.

در هر میلی‌ثانیه هشت نمونه داریم؛ پس برای مدت بیست، ۱۶۰ نمونه و ۱۶۰ بایت محموله جمع می‌شود. با پروفایل ثابتِ بدون افزونه، پایهٔ سرآیند RTP دوازده بایت است. بنابراین بخش RTP، ۱۷۲ بایت می‌شود. اینجا CSRC، افزونه و padding نداریم؛ دوازده را اندازهٔ همیشگیِ کل سرآیند همهٔ بسته‌ها معرفی نمی‌کنیم.

قالب UDP هشت بایت سرآیند دارد؛ طول دیتاگرام UDP نمونه ۱۸۰ بایت می‌شود. برای IPv4 بدون گزینه، حداقل IHL پنج کلمهٔ سی‌ودوبیتی، یعنی بیست بایت است. در نتیجه کل دیتاگرام IP دقیقاً دویست بایت است؛ عددهای هر لایه را به‌جای هم به کار نمی‌بریم.

جمع سربارِ ثابت، چهل بایت به ازای هر بسته است. برای ده میلی‌ثانیه محموله هشتاد و کل IP صدوبیست بایت می‌شود؛ برای چهل میلی‌ثانیه، محموله ۳۲۰ و کل ۳۶۰ بایت است. تغییر مدت، سرآیند ثابت را بزرگ نمی‌کند؛ تعداد دفعات فرستادنِ آن در مدل دوره‌ای تغییر می‌کند.

۳. نرخ IP و محدودیت‌های قراردادی را کنار هم بسنجیم

اندازه و نرخ بسته با قیود فرضی مقایسه می‌شوند
تصویر مفهومی: اندازه و نرخ بسته با قیود فرضی مقایسه می‌شوند؛ بدون مقیاس عددی و مستقل از نتایج اجرای کد.

مدت بیست میلی‌ثانیه یعنی نرخ دوره‌ای پنجاه بسته در ثانیه. دویست بایت ضرب‌در هشت بیت و پنجاه، برابر ۸۰۰۰۰ بیت‌برثانیه در لایهٔ IP است. سهم سرآیند ۱۶۰۰۰ و سهم محموله همان ۶۴۰۰۰ است. نسبت چهل به دویست، یک‌پنجمِ کل است؛ نسبت چهل به ۱۶۰ سؤال دیگری دربارهٔ سربار نسبت به محموله است.

برای مدت‌های ده و چهل، نرخ‌های IP به‌ترتیب ۹۶۰۰۰ و ۷۲۰۰۰ بیت‌برثانیه‌اند. طولانی‌ترشدن بسته در همین پروفایل، دفعات سربار ثابت را کم می‌کند؛ این حساب، آزمایش کیفیت یا تأخیر تماس نیست. حتی نرخ دوره‌ای می‌تواند کسری باشد: مدت سه، هزارسوم بسته در ثانیه است، نه اینکه در یک پنجرهٔ یک‌ثانیه‌ای واقعاً کسری از بسته بفرستیم.

قیود ساختگیِ مسئله را دویست بایت برای سقف دیتاگرام IP، ۸۵۰۰۰ بیت‌برثانیه برای بودجه و حداکثر بیست‌وپنج میلی‌ثانیه برای مدت می‌گیریم. گزینهٔ ده، بودجهٔ نرخ را نقض می‌کند. گزینهٔ چهل از سقف اندازه و مدت می‌گذرد. فقط بیست با هر سه سازگار است؛ برنامه آن را بهترین کیفیت یا تنظیم پیشنهادی شبکه نمی‌نامد.

۴. جدول دستی و کد را با مرجع کسری بررسی کنیم

حساب صحیح بسته با مرجع کسری مستقل سنجیده می‌شود
تصویر مفهومی: حساب صحیح بسته با مرجع کسری مستقل سنجیده می‌شود؛ بدون مقیاس عددی و مستقل از نتایج اجرای کد.

فایل g711_packet_budget.py با Python 3.14.5 و کتابخانهٔ استاندارد اجرا شده است. کسرها با Fraction نگه داشته می‌شوند؛ صورت و مخرج در خروجی ثبت می‌شوند تا گردکردن زودهنگام، مرز بودجه را جابه‌جا نکند. هیچ بستهٔ قابل ارسال و هیچ دادهٔ صوتی تولید نمی‌شود.

"""One-way continuous PCMU packet budget; no codec or network execution."""
from fractions import Fraction
from math import lcm
import json
import platform


def _integer(value, low, high, name):
    if type(value) is not int or not low <= value <= high:
        raise ValueError(name + " violates the bounded integer contract")
    return value


def _ratio(value):
    return {"numerator": value.numerator, "denominator": value.denominator}


def packet_metrics(ptime_ms):
    """Fixed PCMU 8000 samples/s, mono, one octet/sample; fixed headers."""
    _integer(ptime_ms, 1, 200, "ptime_ms")
    sample_count = 8 * ptime_ms
    payload = sample_count  # Already encoded PCMU: one octet per sample.
    rtp = 12 + payload
    udp = 8 + rtp
    ip = 20 + udp
    pps = Fraction(1000, ptime_ms)
    return {"ptime_ms": ptime_ms, "samples_per_packet": sample_count,
            "payload_bytes": payload, "rtp_bytes": rtp, "udp_datagram_bytes": udp,
            "ip_datagram_bytes": ip, "fixed_ip_overhead_bytes": 40,
            "packets_per_second": _ratio(pps),
            "payload_bps": _ratio(payload * 8 * pps),
            "ip_bps": _ratio(ip * 8 * pps),
            "header_bps": _ratio(40 * 8 * pps),
            "header_fraction_of_ip_bytes": _ratio(Fraction(40, ip))}


def plan_options(ptimes_ms, mtu_bytes, rate_budget_bps, max_ptime_ms):
    """Return feasible options; no best-quality preference or call admission."""
    _integer(mtu_bytes, 68, 65535, "mtu_bytes")
    _integer(rate_budget_bps, 1, 1000000, "rate_budget_bps")
    _integer(max_ptime_ms, 1, 200, "max_ptime_ms")
    if type(ptimes_ms) not in (list, tuple) or not 1 <= len(ptimes_ms) <= 16:
        raise ValueError("one to sixteen candidate packet durations")
    # Validate the entire input before computing results; do not repair it.
    for p in ptimes_ms:
        _integer(p, 1, 200, "candidate ptime")
    if len(set(ptimes_ms)) != len(ptimes_ms):
        raise ValueError("duplicate candidate duration")
    rows = []
    for p in ptimes_ms:
        metrics = packet_metrics(p)
        ip_bps = Fraction(**metrics["ip_bps"])
        violations = []
        if metrics["ip_datagram_bytes"] > mtu_bytes:
            violations.append("mtu")
        if ip_bps > rate_budget_bps:
            violations.append("rate_budget")
        if p > max_ptime_ms:
            violations.append("packet_duration")
        rows.append({**metrics, "violations": violations, "feasible": not violations})
    return {"limits": {"mtu_bytes": mtu_bytes, "rate_budget_bps": rate_budget_bps,
                       "max_ptime_ms": max_ptime_ms}, "options": rows,
            "feasible_ptimes_ms": [r["ptime_ms"] for r in rows if r["feasible"]]}


def length_counting_reference(ptime_ms):
    """Independent sample-tick count and byte lengths over a whole-period horizon.

    The zero-byte blocks are length counters, NOT valid encoded IP/RTP packets.
    """
    sample_slots = range(0, ptime_ms * 1000, 125)  # 125 microseconds at 8000 Hz.
    payload_counter = bytes(1 for _ in sample_slots)
    length_counter = bytes(20) + bytes(8) + bytes(12) + payload_counter
    horizon_ms = lcm(1000, ptime_ms)
    complete_packets = len(range(0, horizon_ms, ptime_ms))
    return len(payload_counter), len(length_counter), Fraction(
        len(length_counter) * 8 * complete_packets * 1000, horizon_ms)


def _rejects(function):
    try:
        function()
    except ValueError:
        return True
    return False


def run_example():
    example = plan_options([10, 20, 40], 200, 85000, 25)
    m10, m20, m40 = example["options"]
    checks = {}
    checks["hand_twenty_ms_layer_lengths"] = [m20[k] for k in (
        "samples_per_packet", "payload_bytes", "rtp_bytes", "udp_datagram_bytes", "ip_datagram_bytes")] == [160, 160, 172, 180, 200]
    checks["hand_three_one_way_ip_rates"] = [Fraction(**m["ip_bps"]) for m in (m10, m20, m40)] == [96000, 80000, 72000]
    checks["encoded_payload_rate_stays_64000_bps"] = all(Fraction(**m["payload_bps"]) == 64000 for m in (m10, m20, m40))
    checks["hand_header_share_of_total_ip_not_payload"] = Fraction(**m20["header_fraction_of_ip_bytes"]) == Fraction(1, 5) and Fraction(**m20["header_bps"]) == 16000
    checks["hand_only_twenty_ms_meets_all_three_limits"] = example["feasible_ptimes_ms"] == [20] and [m["violations"] for m in (m10, m20, m40)] == [["rate_budget"], [], ["mtu", "packet_duration"]]
    checks["exact_limit_equalities_are_feasible"] = plan_options([20], 200, 80000, 20)["feasible_ptimes_ms"] == [20]
    checks["one_unit_below_each_limit_reports_own_reason"] = (
        plan_options([20], 199, 80000, 20)["options"][0]["violations"] == ["mtu"] and
        plan_options([20], 200, 79999, 20)["options"][0]["violations"] == ["rate_budget"] and
        plan_options([20], 200, 80000, 19)["options"][0]["violations"] == ["packet_duration"])
    three = packet_metrics(3)
    checks["fractional_periodic_rate_is_not_rounded_packet_count"] = Fraction(**three["packets_per_second"]) == Fraction(1000, 3) and Fraction(**three["ip_bps"]) == Fraction(512000, 3)
    rates = [Fraction(**packet_metrics(p)["ip_bps"]) for p in range(1, 201)]
    checks["fixed_overhead_rate_decreases_with_longer_duration"] = all(a > b for a, b in zip(rates, rates[1:]))
    oracle_ok = True
    for p in range(1, 201):
        payload, ip_length, rate = length_counting_reference(p)
        got = packet_metrics(p)
        oracle_ok &= (got["payload_bytes"], got["ip_datagram_bytes"], Fraction(**got["ip_bps"])) == (payload, ip_length, rate)
    checks["independent_sample_tick_byte_length_reference"] = bool(oracle_ok)
    ordered = plan_options([40, 10, 20], 1500, 1000000, 200)
    checks["candidate_order_preserved_without_quality_selection"] = ordered["feasible_ptimes_ms"] == [40, 10, 20]
    checks["invalid_duration_type_and_bounds_rejected"] = all(_rejects(lambda p=p: packet_metrics(p)) for p in [True, 0, 201, 20.0, "20"])
    checks["invalid_limit_types_and_bounds_rejected"] = all(_rejects(lambda args=args: plan_options([20], *args)) for args in [
        (True, 80000, 20), (67, 80000, 20), (65536, 80000, 20),
        (200, True, 20), (200, 0, 20), (200, 1000001, 20), (200, 80000, 0), (200, 80000, 201), (200, 80000, 20.0)])
    candidates = [10, 20, 40]
    plan_options(candidates, 200, 85000, 25)
    checks["candidate_contract_and_input_not_mutated"] = candidates == [10, 20, 40] and all(_rejects(lambda x=x: plan_options(x, 200, 85000, 25)) for x in [[], [20, 20], [20, True], "20", list(range(1, 18))])
    none = plan_options([40], 68, 1, 1)
    checks["no_feasible_option_returns_reasons_not_invented_choice"] = none["feasible_ptimes_ms"] == [] and none["options"][0]["violations"] == ["mtu", "rate_budget", "packet_duration"]
    assert all(type(v) is bool and v for v in checks.values()), checks
    return {"python": platform.python_version(), "dependencies": {"standard_library_only": True},
            "profile": {"codec": "PCMU", "sample_rate_hz": 8000, "channels": 1,
                        "encoded_bits_per_sample": 8, "rtp_header_bytes": 12,
                        "udp_header_bytes": 8, "ipv4_header_bytes_no_options": 20,
                        "direction_count": 1, "continuous_fixed_size_packets": True},
            "example": example, "oracle_durations_inside_one_group": 200, "checks": checks,
            "limitations": ["No mu-law encoder/decoder, audio or actual datagrams generated",
                "No CSRC, RTP extension/padding, IPv4 options, RTCP, SRTP, tunnels or link-layer overhead",
                "Periodic one-way IP rate, no measured bandwidth, total latency, speech quality or call-capacity guarantee"]}


if __name__ == "__main__":
    print(json.dumps(run_example(), ensure_ascii=False, indent=2))

مدتِ ورودی عدد صحیح یک تا دویست میلی‌ثانیه است. فهرستِ یک تا شانزده گزینه باید مدت‌های یکتا داشته باشد و ترتیبش حفظ می‌شود. سقف IP از ۶۸ تا ۶۵۵۳۵ بایت، بودجه از یک تا یک‌میلیون بیت‌برثانیه و بیشینهٔ مدت از یک تا دویست است. این‌ها دامنهٔ تمرین‌اند؛ برنامه MTU مسیر یا ظرفیت واقعی لینک را کشف نمی‌کند.

پانزده گروه بررسی اجرا شده‌اند. مرجع مستقل، فاصله‌های ۱۲۵ میکروثانیه‌ای نمونه‌ها را می‌شمارد و طول سه بلوکِ بایتیِ سرآیند را به شمار محموله اضافه می‌کند. بلوک‌ها فقط شمارندهٔ طول‌اند، نه سرآیند معتبر شبکه. روی بازه‌ای با تعداد صحیحِ بسته‌های کامل، نرخ کسری محاسبه و دویست مدت در یک گروه با تابع اصلی مقایسه می‌شوند.

مرز تساوی نیز جداست: گزینهٔ بیست با سقف دویست بایت، نرخ ۸۰۰۰۰ و مدت بیست پذیرفته می‌شود. یک بایت کمتر، یک بیت‌برثانیه کمتر یا یک میلی‌ثانیه کمتر، همان قید مربوط را نقض می‌کند. اگر هیچ گزینه‌ای جا نشود، خروجی فهرست تهی و دلیل‌های نقض است؛ انتخابی اختراع یا ورودی بی‌صدا اصلاح نمی‌شود.

۵. محاسبهٔ بودجه را کیفیت یا تأخیر واقعی ننامیم

حساب بودجهٔ IP سنجش کیفیت تماس واقعی نیست
تصویر مفهومی: حساب بودجهٔ IP سنجش کیفیت تماس واقعی نیست؛ بدون مقیاس عددی و مستقل از نتایج اجرای کد.

سقف اندازه به کل دیتاگرام IP اعمال می‌شود، نه فقط محموله یا UDP. کد فرایند تکه‌تکه‌شدن، کشف MTU و رفتار روتر را اجرا نمی‌کند. سرآیندِ گزینه‌دار IPv4، تونل، رمزنگاری، RTCP و لایهٔ پیوند حساب نشده‌اند. پس ۸۰ کیلوبیت را نرخ روی سیم اترنت، کل هزینهٔ تماس یا اندازهٔ لازم برای سرویس واقعی معرفی نمی‌کنیم.

{
  "python": "3.14.5",
  "dependencies": {
    "standard_library_only": true
  },
  "profile": {
    "codec": "PCMU",
    "sample_rate_hz": 8000,
    "channels": 1,
    "encoded_bits_per_sample": 8,
    "rtp_header_bytes": 12,
    "udp_header_bytes": 8,
    "ipv4_header_bytes_no_options": 20,
    "direction_count": 1,
    "continuous_fixed_size_packets": true
  },
  "example": {
    "limits": {
      "mtu_bytes": 200,
      "rate_budget_bps": 85000,
      "max_ptime_ms": 25
    },
    "options": [
      {
        "ptime_ms": 10,
        "samples_per_packet": 80,
        "payload_bytes": 80,
        "rtp_bytes": 92,
        "udp_datagram_bytes": 100,
        "ip_datagram_bytes": 120,
        "fixed_ip_overhead_bytes": 40,
        "packets_per_second": {
          "numerator": 100,
          "denominator": 1
        },
        "payload_bps": {
          "numerator": 64000,
          "denominator": 1
        },
        "ip_bps": {
          "numerator": 96000,
          "denominator": 1
        },
        "header_bps": {
          "numerator": 32000,
          "denominator": 1
        },
        "header_fraction_of_ip_bytes": {
          "numerator": 1,
          "denominator": 3
        },
        "violations": [
          "rate_budget"
        ],
        "feasible": false
      },
      {
        "ptime_ms": 20,
        "samples_per_packet": 160,
        "payload_bytes": 160,
        "rtp_bytes": 172,
        "udp_datagram_bytes": 180,
        "ip_datagram_bytes": 200,
        "fixed_ip_overhead_bytes": 40,
        "packets_per_second": {
          "numerator": 50,
          "denominator": 1
        },
        "payload_bps": {
          "numerator": 64000,
          "denominator": 1
        },
        "ip_bps": {
          "numerator": 80000,
          "denominator": 1
        },
        "header_bps": {
          "numerator": 16000,
          "denominator": 1
        },
        "header_fraction_of_ip_bytes": {
          "numerator": 1,
          "denominator": 5
        },
        "violations": [],
        "feasible": true
      },
      {
        "ptime_ms": 40,
        "samples_per_packet": 320,
        "payload_bytes": 320,
        "rtp_bytes": 332,
        "udp_datagram_bytes": 340,
        "ip_datagram_bytes": 360,
        "fixed_ip_overhead_bytes": 40,
        "packets_per_second": {
          "numerator": 25,
          "denominator": 1
        },
        "payload_bps": {
          "numerator": 64000,
          "denominator": 1
        },
        "ip_bps": {
          "numerator": 72000,
          "denominator": 1
        },
        "header_bps": {
          "numerator": 8000,
          "denominator": 1
        },
        "header_fraction_of_ip_bytes": {
          "numerator": 1,
          "denominator": 9
        },
        "violations": [
          "mtu",
          "packet_duration"
        ],
        "feasible": false
      }
    ],
    "feasible_ptimes_ms": [
      20
    ]
  },
  "oracle_durations_inside_one_group": 200,
  "checks": {
    "hand_twenty_ms_layer_lengths": true,
    "hand_three_one_way_ip_rates": true,
    "encoded_payload_rate_stays_64000_bps": true,
    "hand_header_share_of_total_ip_not_payload": true,
    "hand_only_twenty_ms_meets_all_three_limits": true,
    "exact_limit_equalities_are_feasible": true,
    "one_unit_below_each_limit_reports_own_reason": true,
    "fractional_periodic_rate_is_not_rounded_packet_count": true,
    "fixed_overhead_rate_decreases_with_longer_duration": true,
    "independent_sample_tick_byte_length_reference": true,
    "candidate_order_preserved_without_quality_selection": true,
    "invalid_duration_type_and_bounds_rejected": true,
    "invalid_limit_types_and_bounds_rejected": true,
    "candidate_contract_and_input_not_mutated": true,
    "no_feasible_option_returns_reasons_not_invented_choice": true
  },
  "limitations": [
    "No mu-law encoder/decoder, audio or actual datagrams generated",
    "No CSRC, RTP extension/padding, IPv4 options, RTCP, SRTP, tunnels or link-layer overhead",
    "Periodic one-way IP rate, no measured bandwidth, total latency, speech quality or call-capacity guarantee"
  ]
}

خواندن مرز سرآیند RTP نشان می‌دهد چرا وجود افزونه یا CSRC می‌تواند این پروفایل ثابت را تغییر دهد. در کاربرد واقعی، قالبِ توافق‌شده و مسیر باید جدا بررسی شوند. این حساب نه کیفیت گفتار، نه موفقیت تشخیص فارسی و نه تأخیر انتهابه‌انتها را می‌سنجد؛ مدت بسته فقط یکی از اجزای مسئلهٔ زمانی است.

منابع در ۲۵ مهر ۱۴۰۵ خوانده شدند. اسناد RTP مربوط به ژوئیهٔ ۲۰۰۳، سند UDP مربوط به اوت ۱۹۸۰ و سند IPv4 مربوط به سپتامبر ۱۹۸۱ هستند. تاریخ دسترسی، انتشار استاندارد تازه نیست. اعداد مسئلهٔ انتخاب هم فرض‌های تألیفیِ همین آموزش‌اند و از تجربهٔ مشتری، محصول یا تماس زنده نیامده‌اند.

پرسش اول: چرا محمولهٔ ۶۴ کیلوبیتی، نرخ IP هشتاد می‌دهد؟ پاسخ: در مدت بیست، پنجاه سرآیند چهل‌بایتی در ثانیه، شانزده کیلوبیت اضافه می‌کنند.

پرسش دوم: چرا گزینهٔ چهل با نرخ کمتر پذیرفته نشد؟ پاسخ: هم اندازهٔ ۳۶۰ از دویست بیشتر است و هم مدت چهل از سقف بیست‌وپنج می‌گذرد؛ یک قید کافی نیست.

پرسش سوم: یک‌پنجمِ سربار نسبت به چیست؟ پاسخ: چهل بایت از کل دویست بایت IP؛ نسبت به محمولهٔ ۱۶۰بایتی، یک‌چهارم می‌شود.

پرسش چهارم: آیا بیست، بهترین مدت برای هر تماس است؟ پاسخ: خیر؛ فقط تنها گزینهٔ سازگار با قیود ساختگیِ این فهرست است. ارزیابی واقعی و سیاست شبکه در مثال وجود ندارند.

منابع و مطالعهٔ بیشتر

بررسی منابع: ۲۵ مهر ۱۴۰۵. تحلیل‌ها و پیشنهادهای اجرایی، جمع‌بندی نویسنده‌اند.