commkit 3.3.0__tar.gz → 3.5.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- commkit-3.5.0/.github/icon.svg +1 -0
- commkit-3.5.0/.github/logo.svg +1 -0
- {commkit-3.3.0 → commkit-3.5.0}/.github/workflows/ci.yml +2 -1
- commkit-3.5.0/AGENTS.md +288 -0
- {commkit-3.3.0 → commkit-3.5.0}/CHANGELOG.md +66 -1
- {commkit-3.3.0 → commkit-3.5.0}/PKG-INFO +11 -11
- {commkit-3.3.0 → commkit-3.5.0}/README.md +12 -12
- {commkit-3.3.0 → commkit-3.5.0}/benchmarks/bench_signal_pipeline.py +1 -1
- {commkit-3.3.0 → commkit-3.5.0}/benchmarks/workloads.py +4 -2
- {commkit-3.3.0 → commkit-3.5.0}/commkit/__init__.py +1 -1
- {commkit-3.3.0 → commkit-3.5.0}/commkit/_sequences.py +1 -1
- {commkit-3.3.0 → commkit-3.5.0}/commkit/analysis/drift.py +3 -3
- {commkit-3.3.0 → commkit-3.5.0}/commkit/analysis/fm_noise.py +6 -6
- {commkit-3.3.0 → commkit-3.5.0}/commkit/analysis/linewidth/dsh.py +10 -10
- {commkit-3.3.0 → commkit-3.5.0}/commkit/analysis/linewidth/phase.py +6 -6
- {commkit-3.3.0 → commkit-3.5.0}/commkit/analysis/linewidth/result.py +16 -16
- {commkit-3.3.0 → commkit-3.5.0}/commkit/analysis/trajectory.py +7 -7
- {commkit-3.3.0 → commkit-3.5.0}/commkit/coding/__init__.py +5 -1
- {commkit-3.3.0 → commkit-3.5.0}/commkit/coding/_numba_loops.py +73 -5
- {commkit-3.3.0 → commkit-3.5.0}/commkit/coding/algebraic.py +2 -0
- {commkit-3.3.0 → commkit-3.5.0}/commkit/coding/base.py +65 -11
- {commkit-3.3.0 → commkit-3.5.0}/commkit/coding/convolutional.py +1 -0
- {commkit-3.3.0 → commkit-3.5.0}/commkit/coding/crc.py +1 -0
- {commkit-3.3.0 → commkit-3.5.0}/commkit/coding/exit.py +19 -14
- {commkit-3.3.0 → commkit-3.5.0}/commkit/coding/ldpc.py +1 -0
- {commkit-3.3.0 → commkit-3.5.0}/commkit/coding/linear.py +134 -16
- commkit-3.5.0/commkit/coding/performance.py +371 -0
- {commkit-3.3.0 → commkit-3.5.0}/commkit/coding/polar.py +1 -0
- {commkit-3.3.0 → commkit-3.5.0}/commkit/coding/product.py +10 -2
- {commkit-3.3.0 → commkit-3.5.0}/commkit/coding/ratematch.py +2 -0
- {commkit-3.3.0 → commkit-3.5.0}/commkit/coding/shaping.py +2 -0
- {commkit-3.3.0 → commkit-3.5.0}/commkit/coding/turbo.py +8 -3
- {commkit-3.3.0 → commkit-3.5.0}/commkit/core/__init__.py +5 -2
- {commkit-3.3.0 → commkit-3.5.0}/commkit/core/frame.py +5 -154
- {commkit-3.3.0 → commkit-3.5.0}/commkit/core/generation.py +2 -2
- commkit-3.5.0/commkit/core/preamble.py +170 -0
- {commkit-3.3.0 → commkit-3.5.0}/commkit/equalization/__init__.py +2 -3
- {commkit-3.3.0 → commkit-3.5.0}/commkit/equalization/_block/__init__.py +7 -4
- commkit-3.3.0/commkit/equalization/blind.py → commkit-3.5.0/commkit/equalization/_block/_blind.py +206 -11
- {commkit-3.3.0 → commkit-3.5.0}/commkit/equalization/_block/_dd.py +13 -1
- {commkit-3.3.0 → commkit-3.5.0}/commkit/equalization/_numba_loops.py +4 -2
- commkit-3.5.0/commkit/equalization/_sequential/__init__.py +21 -0
- {commkit-3.3.0/commkit/equalization/sequential → commkit-3.5.0/commkit/equalization/_sequential}/_blind.py +18 -1
- {commkit-3.3.0/commkit/equalization/sequential → commkit-3.5.0/commkit/equalization/_sequential}/_dd.py +30 -5
- {commkit-3.3.0 → commkit-3.5.0}/commkit/equalization/linear.py +4 -4
- commkit-3.3.0/commkit/impairments/channel/linear.py → commkit-3.5.0/commkit/impairments/channel.py +186 -10
- {commkit-3.3.0 → commkit-3.5.0}/commkit/impairments/noise.py +66 -29
- {commkit-3.3.0 → commkit-3.5.0}/commkit/mapping/constellation.py +10 -3
- {commkit-3.3.0 → commkit-3.5.0}/commkit/mapping/llr.py +43 -6
- commkit-3.5.0/commkit/metrics/__init__.py +125 -0
- commkit-3.5.0/commkit/metrics/_common.py +180 -0
- commkit-3.5.0/commkit/metrics/error_rates.py +240 -0
- commkit-3.5.0/commkit/metrics/information.py +354 -0
- commkit-3.5.0/commkit/metrics/quality.py +248 -0
- {commkit-3.3.0 → commkit-3.5.0}/commkit/plotting/__init__.py +21 -11
- {commkit-3.3.0 → commkit-3.5.0}/commkit/plotting/analysis.py +116 -26
- {commkit-3.3.0 → commkit-3.5.0}/commkit/plotting/coding.py +227 -6
- {commkit-3.3.0 → commkit-3.5.0}/commkit/plotting/constellation.py +2 -0
- commkit-3.5.0/commkit/plotting/frequency.py +374 -0
- commkit-3.5.0/commkit/plotting/recovery.py +551 -0
- {commkit-3.3.0 → commkit-3.5.0}/commkit/plotting/theme.py +15 -0
- commkit-3.5.0/commkit/plotting/timing.py +129 -0
- {commkit-3.3.0 → commkit-3.5.0}/commkit/recovery/_common.py +5 -2
- {commkit-3.3.0 → commkit-3.5.0}/commkit/recovery/pll.py +18 -13
- {commkit-3.3.0 → commkit-3.5.0}/examples/00-getting-started.ipynb +87 -19
- {commkit-3.3.0 → commkit-3.5.0}/examples/01-constellations-and-bit-mapping.ipynb +77 -6
- {commkit-3.3.0 → commkit-3.5.0}/examples/02-spectral-analysis.ipynb +42 -11
- {commkit-3.3.0 → commkit-3.5.0}/examples/03-digital-filters.ipynb +57 -21
- {commkit-3.3.0 → commkit-3.5.0}/examples/04-pulse-shaping-and-matched-filtering.ipynb +79 -21
- {commkit-3.3.0 → commkit-3.5.0}/examples/05-multirate-and-resampling.ipynb +59 -9
- {commkit-3.3.0 → commkit-3.5.0}/examples/06-awgn-and-performance-metrics.ipynb +199 -25
- {commkit-3.3.0 → commkit-3.5.0}/examples/07-channel-coding.ipynb +223 -79
- {commkit-3.3.0 → commkit-3.5.0}/examples/08-probabilistic-shaping-and-coded-modulation.ipynb +65 -39
- {commkit-3.3.0 → commkit-3.5.0}/examples/09-laser-phase-noise.ipynb +37 -21
- {commkit-3.3.0 → commkit-3.5.0}/examples/10-front-end-impairments.ipynb +45 -7
- {commkit-3.3.0 → commkit-3.5.0}/examples/11-frame-synchronization-and-timing.ipynb +49 -3
- {commkit-3.3.0 → commkit-3.5.0}/examples/12-frequency-offset-estimation.ipynb +58 -8
- {commkit-3.3.0 → commkit-3.5.0}/examples/13-blind-carrier-phase-recovery.ipynb +86 -19
- {commkit-3.3.0 → commkit-3.5.0}/examples/14-pilot-aided-carrier-recovery.ipynb +15 -5
- {commkit-3.3.0 → commkit-3.5.0}/examples/15-linear-channel-equalization.ipynb +51 -14
- {commkit-3.3.0 → commkit-3.5.0}/examples/16-adaptive-equalization.ipynb +21 -25
- {commkit-3.3.0 → commkit-3.5.0}/examples/17-blind-equalization.ipynb +50 -7
- {commkit-3.3.0 → commkit-3.5.0}/examples/18-polarization-multiplexing.ipynb +45 -20
- {commkit-3.3.0 → commkit-3.5.0}/examples/19-chromatic-dispersion.ipynb +11 -5
- {commkit-3.3.0 → commkit-3.5.0}/examples/20-nonlinear-fiber-and-backpropagation.ipynb +58 -0
- {commkit-3.3.0 → commkit-3.5.0}/examples/21-fec-in-optical-standards.ipynb +63 -26
- {commkit-3.3.0 → commkit-3.5.0}/examples/22-coherent-transceiver-end-to-end.ipynb +16 -13
- {commkit-3.3.0 → commkit-3.5.0}/examples/23-carrier-phase-characterization.ipynb +3 -3
- {commkit-3.3.0 → commkit-3.5.0}/examples/24-laser-linewidth-self-heterodyne.ipynb +3 -3
- {commkit-3.3.0 → commkit-3.5.0}/examples/25-laser-linewidth-short-delay-and-flicker.ipynb +5 -5
- {commkit-3.3.0 → commkit-3.5.0}/examples/26-laser-linewidth-self-homodyne-iq.ipynb +21 -20
- {commkit-3.3.0 → commkit-3.5.0}/examples/27-measurement-template-self-heterodyne.ipynb +4 -4
- {commkit-3.3.0 → commkit-3.5.0}/examples/28-measurement-template-self-homodyne-iq.ipynb +15 -15
- {commkit-3.3.0 → commkit-3.5.0}/examples/README.md +3 -3
- {commkit-3.3.0 → commkit-3.5.0}/pyproject.toml +2 -5
- commkit-3.5.0/scripts/sanitize.sh +76 -0
- {commkit-3.3.0 → commkit-3.5.0}/tests/analysis/test_drift.py +2 -2
- {commkit-3.3.0 → commkit-3.5.0}/tests/analysis/test_linewidth.py +11 -11
- {commkit-3.3.0 → commkit-3.5.0}/tests/coding/test_coding_base.py +97 -1
- {commkit-3.3.0 → commkit-3.5.0}/tests/coding/test_exit.py +9 -2
- {commkit-3.3.0 → commkit-3.5.0}/tests/coding/test_ldpc.py +3 -3
- {commkit-3.3.0 → commkit-3.5.0}/tests/coding/test_linear_codes.py +53 -0
- commkit-3.5.0/tests/coding/test_performance.py +214 -0
- {commkit-3.3.0 → commkit-3.5.0}/tests/coding/test_polar.py +2 -4
- {commkit-3.3.0 → commkit-3.5.0}/tests/coding/test_product.py +43 -1
- {commkit-3.3.0 → commkit-3.5.0}/tests/coding/test_shaping.py +11 -7
- {commkit-3.3.0 → commkit-3.5.0}/tests/coding/test_turbo.py +51 -5
- {commkit-3.3.0 → commkit-3.5.0}/tests/common/coding.py +1 -1
- {commkit-3.3.0 → commkit-3.5.0}/tests/common/signals.py +33 -5
- {commkit-3.3.0 → commkit-3.5.0}/tests/common/theory.py +17 -0
- {commkit-3.3.0 → commkit-3.5.0}/tests/core/test_frame.py +1 -16
- {commkit-3.3.0 → commkit-3.5.0}/tests/core/test_generation.py +43 -27
- commkit-3.5.0/tests/core/test_preamble.py +26 -0
- {commkit-3.3.0 → commkit-3.5.0}/tests/core/test_psqam.py +21 -8
- {commkit-3.3.0 → commkit-3.5.0}/tests/core/test_signal.py +84 -23
- {commkit-3.3.0 → commkit-3.5.0}/tests/core/test_signal_mimo.py +1 -1
- {commkit-3.3.0 → commkit-3.5.0}/tests/equalization/test_blind.py +7 -1
- {commkit-3.3.0 → commkit-3.5.0}/tests/equalization/test_cpr.py +17 -7
- {commkit-3.3.0 → commkit-3.5.0}/tests/equalization/test_cs_kernel.py +1 -1
- {commkit-3.3.0 → commkit-3.5.0}/tests/equalization/test_equalizer_oracles.py +6 -1
- {commkit-3.3.0 → commkit-3.5.0}/tests/equalization/test_sequential.py +13 -6
- {commkit-3.3.0 → commkit-3.5.0}/tests/equalization/test_state.py +3 -1
- {commkit-3.3.0 → commkit-3.5.0}/tests/impairments/test_noise.py +48 -2
- {commkit-3.3.0 → commkit-3.5.0}/tests/mapping/test_llr.py +78 -7
- commkit-3.5.0/tests/metrics/test_common.py +169 -0
- commkit-3.5.0/tests/metrics/test_error_rates.py +239 -0
- commkit-3.5.0/tests/metrics/test_information.py +285 -0
- commkit-3.5.0/tests/metrics/test_quality.py +185 -0
- {commkit-3.3.0 → commkit-3.5.0}/tests/plotting/test_plot_analysis.py +25 -3
- {commkit-3.3.0 → commkit-3.5.0}/tests/plotting/test_plot_coding.py +117 -1
- {commkit-3.3.0 → commkit-3.5.0}/tests/plotting/test_plot_constellation.py +9 -7
- {commkit-3.3.0 → commkit-3.5.0}/tests/plotting/test_plot_equalizer.py +31 -7
- commkit-3.5.0/tests/plotting/test_plot_frequency.py +120 -0
- commkit-3.5.0/tests/plotting/test_plot_recovery.py +157 -0
- {commkit-3.3.0 → commkit-3.5.0}/tests/plotting/test_plot_spectral.py +1 -1
- commkit-3.5.0/tests/plotting/test_plot_timing.py +75 -0
- {commkit-3.3.0 → commkit-3.5.0}/tests/plugins/precision_trace.py +25 -3
- {commkit-3.3.0 → commkit-3.5.0}/tests/plugins/transfer_trace.py +38 -8
- {commkit-3.3.0 → commkit-3.5.0}/tests/recovery/test_corrections.py +27 -7
- {commkit-3.3.0 → commkit-3.5.0}/tests/recovery/test_pilots.py +4 -4
- {commkit-3.3.0 → commkit-3.5.0}/tests/recovery/test_pll.py +30 -18
- {commkit-3.3.0 → commkit-3.5.0}/tests/recovery/test_recovery_oracles.py +3 -1
- {commkit-3.3.0 → commkit-3.5.0}/tests/test_api_contracts.py +35 -32
- {commkit-3.3.0 → commkit-3.5.0}/tests/test_fiber.py +1 -1
- {commkit-3.3.0 → commkit-3.5.0}/tests/test_frequency.py +5 -1
- {commkit-3.3.0 → commkit-3.5.0}/tests/test_io.py +15 -8
- {commkit-3.3.0 → commkit-3.5.0}/tests/test_pulse_shaping.py +22 -6
- {commkit-3.3.0 → commkit-3.5.0}/tests/test_receiver_chain.py +1 -1
- {commkit-3.3.0 → commkit-3.5.0}/tests/test_signal_pipeline.py +5 -1
- {commkit-3.3.0 → commkit-3.5.0}/tests/test_timing.py +8 -3
- {commkit-3.3.0 → commkit-3.5.0}/tests/test_working_memory.py +2 -1
- {commkit-3.3.0 → commkit-3.5.0}/uv.lock +1 -1
- commkit-3.3.0/AGENTS.md +0 -398
- commkit-3.3.0/commkit/equalization/_block/_blind.py +0 -202
- commkit-3.3.0/commkit/equalization/_kernels_numba.py +0 -48
- commkit-3.3.0/commkit/equalization/sequential/__init__.py +0 -19
- commkit-3.3.0/commkit/impairments/channel/__init__.py +0 -24
- commkit-3.3.0/commkit/impairments/channel/nonlinear.py +0 -182
- commkit-3.3.0/commkit/metrics.py +0 -991
- commkit-3.3.0/commkit/plotting/sync.py +0 -1120
- commkit-3.3.0/tests/plotting/test_plot_sync.py +0 -341
- commkit-3.3.0/tests/test_metrics.py +0 -839
- {commkit-3.3.0 → commkit-3.5.0}/.gitattributes +0 -0
- {commkit-3.3.0 → commkit-3.5.0}/.github/workflows/publish.yml +0 -0
- {commkit-3.3.0 → commkit-3.5.0}/.gitignore +0 -0
- {commkit-3.3.0 → commkit-3.5.0}/.python-version +0 -0
- {commkit-3.3.0 → commkit-3.5.0}/LICENSE +0 -0
- {commkit-3.3.0 → commkit-3.5.0}/benchmarks/baselines/Linux-CPython-3.14-64bit/0002_pre_v2.json +0 -0
- {commkit-3.3.0 → commkit-3.5.0}/benchmarks/baselines/Linux-CPython-3.14-64bit/0003_v2_0.json +0 -0
- {commkit-3.3.0 → commkit-3.5.0}/benchmarks/baselines/Linux-CPython-3.14-64bit/0004_fiber_dbp.json +0 -0
- {commkit-3.3.0 → commkit-3.5.0}/benchmarks/baselines/Linux-CPython-3.14-64bit/0005_gpu_decoder_path.json +0 -0
- {commkit-3.3.0 → commkit-3.5.0}/benchmarks/baselines/Linux-CPython-3.14-64bit/0006_perf_cleanup.json +0 -0
- {commkit-3.3.0 → commkit-3.5.0}/benchmarks/baselines/Linux-CPython-3.14-64bit/0007_v3_0.json +0 -0
- {commkit-3.3.0 → commkit-3.5.0}/benchmarks/baselines/Linux-CPython-3.14-64bit/0008_v3_1.json +0 -0
- {commkit-3.3.0 → commkit-3.5.0}/benchmarks/bench_block_blind.py +0 -0
- {commkit-3.3.0 → commkit-3.5.0}/benchmarks/bench_block_lms.py +0 -0
- {commkit-3.3.0 → commkit-3.5.0}/benchmarks/bench_bps.py +0 -0
- {commkit-3.3.0 → commkit-3.5.0}/benchmarks/bench_coding.py +0 -0
- {commkit-3.3.0 → commkit-3.5.0}/benchmarks/bench_equalizers.py +0 -0
- {commkit-3.3.0 → commkit-3.5.0}/benchmarks/bench_fiber.py +0 -0
- {commkit-3.3.0 → commkit-3.5.0}/benchmarks/bench_filters.py +0 -0
- {commkit-3.3.0 → commkit-3.5.0}/benchmarks/bench_import.py +0 -0
- {commkit-3.3.0 → commkit-3.5.0}/benchmarks/bench_llr.py +0 -0
- {commkit-3.3.0 → commkit-3.5.0}/benchmarks/bench_mapping.py +0 -0
- {commkit-3.3.0 → commkit-3.5.0}/benchmarks/bench_sync_misc.py +0 -0
- {commkit-3.3.0 → commkit-3.5.0}/benchmarks/benchutils.py +0 -0
- {commkit-3.3.0 → commkit-3.5.0}/benchmarks/conftest.py +0 -0
- {commkit-3.3.0 → commkit-3.5.0}/benchmarks/record_baseline.py +0 -0
- {commkit-3.3.0 → commkit-3.5.0}/commkit/_array.py +0 -0
- {commkit-3.3.0 → commkit-3.5.0}/commkit/_circular.py +0 -0
- {commkit-3.3.0 → commkit-3.5.0}/commkit/_constants.py +0 -0
- {commkit-3.3.0 → commkit-3.5.0}/commkit/_cuda/__init__.py +0 -0
- {commkit-3.3.0 → commkit-3.5.0}/commkit/_cuda/compiler.py +0 -0
- {commkit-3.3.0 → commkit-3.5.0}/commkit/_cuda/graphs.py +0 -0
- {commkit-3.3.0 → commkit-3.5.0}/commkit/_cuda/src/algebraic.cu +0 -0
- {commkit-3.3.0 → commkit-3.5.0}/commkit/_cuda/src/bps_anchor.cu +0 -0
- {commkit-3.3.0 → commkit-3.5.0}/commkit/_cuda/src/bps_min_d2.cu +0 -0
- {commkit-3.3.0 → commkit-3.5.0}/commkit/_cuda/src/cs_block.cu +0 -0
- {commkit-3.3.0 → commkit-3.5.0}/commkit/_cuda/src/iir.cu +0 -0
- {commkit-3.3.0 → commkit-3.5.0}/commkit/_cuda/src/ldpc.cu +0 -0
- {commkit-3.3.0 → commkit-3.5.0}/commkit/_cuda/src/llr.cu +0 -0
- {commkit-3.3.0 → commkit-3.5.0}/commkit/_cuda/src/selftest.cu +0 -0
- {commkit-3.3.0 → commkit-3.5.0}/commkit/_dispersion.py +0 -0
- {commkit-3.3.0 → commkit-3.5.0}/commkit/_fft.py +0 -0
- {commkit-3.3.0 → commkit-3.5.0}/commkit/_fiber.py +0 -0
- {commkit-3.3.0 → commkit-3.5.0}/commkit/_overlap_save.py +0 -0
- {commkit-3.3.0 → commkit-3.5.0}/commkit/_random.py +0 -0
- {commkit-3.3.0 → commkit-3.5.0}/commkit/analysis/__init__.py +0 -0
- {commkit-3.3.0 → commkit-3.5.0}/commkit/analysis/_common.py +0 -0
- {commkit-3.3.0 → commkit-3.5.0}/commkit/analysis/allan.py +0 -0
- {commkit-3.3.0 → commkit-3.5.0}/commkit/analysis/interferometry.py +0 -0
- {commkit-3.3.0 → commkit-3.5.0}/commkit/analysis/linewidth/__init__.py +0 -0
- {commkit-3.3.0 → commkit-3.5.0}/commkit/backend.py +0 -0
- {commkit-3.3.0 → commkit-3.5.0}/commkit/coding/_galois.py +0 -0
- {commkit-3.3.0 → commkit-3.5.0}/commkit/coding/_gf2.py +0 -0
- {commkit-3.3.0 → commkit-3.5.0}/commkit/coding/_ldpc_tables.py +0 -0
- {commkit-3.3.0 → commkit-3.5.0}/commkit/coding/interleaving.py +0 -0
- {commkit-3.3.0 → commkit-3.5.0}/commkit/core/_signal_adapter.py +0 -0
- {commkit-3.3.0 → commkit-3.5.0}/commkit/core/signal.py +0 -0
- {commkit-3.3.0 → commkit-3.5.0}/commkit/equalization/_block/_engine.py +0 -0
- {commkit-3.3.0 → commkit-3.5.0}/commkit/equalization/_common.py +0 -0
- {commkit-3.3.0/commkit/equalization/sequential → commkit-3.5.0/commkit/equalization/_sequential}/_setup.py +0 -0
- {commkit-3.3.0 → commkit-3.5.0}/commkit/equalization/pilots.py +0 -0
- {commkit-3.3.0 → commkit-3.5.0}/commkit/equalization/polarization.py +0 -0
- {commkit-3.3.0 → commkit-3.5.0}/commkit/equalization/result.py +0 -0
- {commkit-3.3.0 → commkit-3.5.0}/commkit/filtering/__init__.py +0 -0
- {commkit-3.3.0 → commkit-3.5.0}/commkit/filtering/fiber.py +0 -0
- {commkit-3.3.0 → commkit-3.5.0}/commkit/filtering/linear.py +0 -0
- {commkit-3.3.0 → commkit-3.5.0}/commkit/filtering/pulses.py +0 -0
- {commkit-3.3.0 → commkit-3.5.0}/commkit/filtering/response.py +0 -0
- {commkit-3.3.0 → commkit-3.5.0}/commkit/frequency.py +0 -0
- {commkit-3.3.0 → commkit-3.5.0}/commkit/impairments/__init__.py +0 -0
- {commkit-3.3.0 → commkit-3.5.0}/commkit/impairments/frontend.py +0 -0
- {commkit-3.3.0 → commkit-3.5.0}/commkit/impairments/source.py +0 -0
- {commkit-3.3.0 → commkit-3.5.0}/commkit/io.py +0 -0
- {commkit-3.3.0 → commkit-3.5.0}/commkit/logger.py +0 -0
- {commkit-3.3.0 → commkit-3.5.0}/commkit/mapping/__init__.py +0 -0
- {commkit-3.3.0 → commkit-3.5.0}/commkit/mapping/_numba_llr.py +0 -0
- {commkit-3.3.0 → commkit-3.5.0}/commkit/mapping/bits.py +0 -0
- {commkit-3.3.0 → commkit-3.5.0}/commkit/mapping/gray.py +0 -0
- {commkit-3.3.0 → commkit-3.5.0}/commkit/mapping/shaping.py +0 -0
- {commkit-3.3.0 → commkit-3.5.0}/commkit/math.py +0 -0
- {commkit-3.3.0 → commkit-3.5.0}/commkit/multirate.py +0 -0
- {commkit-3.3.0 → commkit-3.5.0}/commkit/plotting/equalizer.py +0 -0
- {commkit-3.3.0 → commkit-3.5.0}/commkit/plotting/eye.py +0 -0
- {commkit-3.3.0 → commkit-3.5.0}/commkit/plotting/filter_response.py +0 -0
- {commkit-3.3.0 → commkit-3.5.0}/commkit/plotting/spectral.py +0 -0
- {commkit-3.3.0 → commkit-3.5.0}/commkit/plotting/waveform.py +0 -0
- {commkit-3.3.0 → commkit-3.5.0}/commkit/py.typed +0 -0
- {commkit-3.3.0 → commkit-3.5.0}/commkit/recovery/__init__.py +0 -0
- {commkit-3.3.0 → commkit-3.5.0}/commkit/recovery/bps.py +0 -0
- {commkit-3.3.0 → commkit-3.5.0}/commkit/recovery/carrier_phase.py +0 -0
- {commkit-3.3.0 → commkit-3.5.0}/commkit/recovery/corrections.py +0 -0
- {commkit-3.3.0 → commkit-3.5.0}/commkit/recovery/pilots.py +0 -0
- {commkit-3.3.0 → commkit-3.5.0}/commkit/recovery/tikhonov.py +0 -0
- {commkit-3.3.0 → commkit-3.5.0}/commkit/recovery/viterbi_viterbi.py +0 -0
- {commkit-3.3.0 → commkit-3.5.0}/commkit/smoothing.py +0 -0
- {commkit-3.3.0 → commkit-3.5.0}/commkit/spectral.py +0 -0
- {commkit-3.3.0 → commkit-3.5.0}/commkit/timing.py +0 -0
- {commkit-3.3.0 → commkit-3.5.0}/tests/analysis/test_allan.py +0 -0
- {commkit-3.3.0 → commkit-3.5.0}/tests/analysis/test_fm_noise.py +0 -0
- {commkit-3.3.0 → commkit-3.5.0}/tests/analysis/test_interferometry.py +0 -0
- {commkit-3.3.0 → commkit-3.5.0}/tests/analysis/test_trajectory.py +0 -0
- {commkit-3.3.0 → commkit-3.5.0}/tests/coding/test_algebraic.py +0 -0
- {commkit-3.3.0 → commkit-3.5.0}/tests/coding/test_convolutional.py +0 -0
- {commkit-3.3.0 → commkit-3.5.0}/tests/coding/test_crc.py +0 -0
- {commkit-3.3.0 → commkit-3.5.0}/tests/coding/test_gf2.py +0 -0
- {commkit-3.3.0 → commkit-3.5.0}/tests/coding/test_interleaving.py +0 -0
- {commkit-3.3.0 → commkit-3.5.0}/tests/coding/test_ldpc_exit.py +0 -0
- {commkit-3.3.0 → commkit-3.5.0}/tests/coding/test_ldpc_kernel.py +0 -0
- {commkit-3.3.0 → commkit-3.5.0}/tests/coding/test_ldpc_tables.py +0 -0
- {commkit-3.3.0 → commkit-3.5.0}/tests/coding/test_ratematch.py +0 -0
- {commkit-3.3.0 → commkit-3.5.0}/tests/common/__init__.py +0 -0
- {commkit-3.3.0 → commkit-3.5.0}/tests/common/conversions.py +0 -0
- {commkit-3.3.0 → commkit-3.5.0}/tests/common/kernel_utils.py +0 -0
- {commkit-3.3.0 → commkit-3.5.0}/tests/common/memory.py +0 -0
- {commkit-3.3.0 → commkit-3.5.0}/tests/common/metrics.py +0 -0
- {commkit-3.3.0 → commkit-3.5.0}/tests/common/periodic.py +0 -0
- {commkit-3.3.0 → commkit-3.5.0}/tests/common/reference_impl.py +0 -0
- {commkit-3.3.0 → commkit-3.5.0}/tests/common/scoring.py +0 -0
- {commkit-3.3.0 → commkit-3.5.0}/tests/conftest.py +0 -0
- {commkit-3.3.0 → commkit-3.5.0}/tests/core/test_signal_adapter.py +0 -0
- {commkit-3.3.0 → commkit-3.5.0}/tests/equalization/test_block.py +0 -0
- {commkit-3.3.0 → commkit-3.5.0}/tests/equalization/test_bps_kernel.py +0 -0
- {commkit-3.3.0 → commkit-3.5.0}/tests/equalization/test_linear.py +0 -0
- {commkit-3.3.0 → commkit-3.5.0}/tests/equalization/test_mimo.py +0 -0
- {commkit-3.3.0 → commkit-3.5.0}/tests/equalization/test_polarization.py +0 -0
- {commkit-3.3.0 → commkit-3.5.0}/tests/equalization/test_winit.py +0 -0
- {commkit-3.3.0/tests/impairments/channel → commkit-3.5.0/tests/impairments}/test_channel_linear.py +0 -0
- {commkit-3.3.0/tests/impairments/channel → commkit-3.5.0/tests/impairments}/test_channel_nonlinear.py +0 -0
- {commkit-3.3.0 → commkit-3.5.0}/tests/impairments/test_frontend.py +0 -0
- {commkit-3.3.0 → commkit-3.5.0}/tests/impairments/test_source.py +0 -0
- {commkit-3.3.0 → commkit-3.5.0}/tests/mapping/test_bits.py +0 -0
- {commkit-3.3.0 → commkit-3.5.0}/tests/mapping/test_constellation.py +0 -0
- {commkit-3.3.0 → commkit-3.5.0}/tests/mapping/test_gray.py +0 -0
- {commkit-3.3.0 → commkit-3.5.0}/tests/plotting/test_plot_eye.py +0 -0
- {commkit-3.3.0 → commkit-3.5.0}/tests/plotting/test_plot_filter_response.py +0 -0
- {commkit-3.3.0 → commkit-3.5.0}/tests/plotting/test_plot_theme.py +0 -0
- {commkit-3.3.0 → commkit-3.5.0}/tests/plotting/test_plot_waveform.py +0 -0
- {commkit-3.3.0 → commkit-3.5.0}/tests/plugins/__init__.py +0 -0
- {commkit-3.3.0 → commkit-3.5.0}/tests/recovery/test_bps.py +0 -0
- {commkit-3.3.0 → commkit-3.5.0}/tests/recovery/test_joint.py +0 -0
- {commkit-3.3.0 → commkit-3.5.0}/tests/recovery/test_tikhonov.py +0 -0
- {commkit-3.3.0 → commkit-3.5.0}/tests/recovery/test_viterbi_viterbi.py +0 -0
- {commkit-3.3.0 → commkit-3.5.0}/tests/test_array.py +0 -0
- {commkit-3.3.0 → commkit-3.5.0}/tests/test_backend.py +0 -0
- {commkit-3.3.0 → commkit-3.5.0}/tests/test_cuda_infra.py +0 -0
- {commkit-3.3.0 → commkit-3.5.0}/tests/test_dispersion.py +0 -0
- {commkit-3.3.0 → commkit-3.5.0}/tests/test_examples.py +0 -0
- {commkit-3.3.0 → commkit-3.5.0}/tests/test_filter_response.py +0 -0
- {commkit-3.3.0 → commkit-3.5.0}/tests/test_filtering.py +0 -0
- {commkit-3.3.0 → commkit-3.5.0}/tests/test_import_hygiene.py +0 -0
- {commkit-3.3.0 → commkit-3.5.0}/tests/test_logger.py +0 -0
- {commkit-3.3.0 → commkit-3.5.0}/tests/test_math.py +0 -0
- {commkit-3.3.0 → commkit-3.5.0}/tests/test_multirate.py +0 -0
- {commkit-3.3.0 → commkit-3.5.0}/tests/test_smoothing.py +0 -0
- {commkit-3.3.0 → commkit-3.5.0}/tests/test_spectral.py +0 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" width="256" height="256" viewBox="17 25 256 256" role="img" aria-labelledby="title desc"><title id="title">CommKit symbol</title><desc id="desc">A hexagon containing equal-width rectangular and sine wave cycles. Transparent vector artwork.</desc><defs><linearGradient id="icon-ink" x1="35" y1="40" x2="250" y2="250" gradientUnits="userSpaceOnUse"><stop offset="0" stop-color="#fca50a" /><stop offset=".5" stop-color="#ed6925" /><stop offset="1" stop-color="#b53657" /></linearGradient><mask id="signal-cutout" maskUnits="userSpaceOnUse" x="20" y="20" width="260" height="260"><rect x="20" y="20" width="260" height="260" fill="white" /><path d="M20 193 H64 V113 H118 V193 H145 C154 193 163 173.944 172 153 C181 132.056 190 113 199 113 C208 113 217 132.056 226 153 C235 173.944 244 193 253 193 H275" fill="none" stroke="black" stroke-width="12" stroke-linejoin="round" stroke-linecap="round" /></mask></defs><g id="CommKit-symbol"><path d="M145 28 L253 90 V216 L145 278 L37 216 V90 Z" fill="url(#icon-ink)" mask="url(#signal-cutout)" /></g></svg>
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" width="1365" height="306" viewBox="0 0 1365 306" role="img" aria-labelledby="title desc"><title id="title">CommKit — digital to waveform</title><desc id="desc">CommKit signal logo. Exactly half of the hexagon width contains one rectangular pulse cycle; the other half contains one smooth sinusoidal cycle of equal amplitude and width. Plain Roboto Black vector wordmark in Inferno amber-to-purple. Transparent background.</desc><defs><linearGradient id="ink" x1="0" y1="0" x2="1257" y2="0" gradientUnits="userSpaceOnUse"><stop offset="0.000" stop-color="#fb9b06" /><stop offset="0.050" stop-color="#fa9207" /><stop offset="0.100" stop-color="#f8890c" /><stop offset="0.150" stop-color="#f68013" /><stop offset="0.200" stop-color="#f37819" /><stop offset="0.250" stop-color="#f06f20" /><stop offset="0.300" stop-color="#eb6628" /><stop offset="0.350" stop-color="#e75e2e" /><stop offset="0.400" stop-color="#e25734" /><stop offset="0.450" stop-color="#dd513a" /><stop offset="0.500" stop-color="#d74b3f" /><stop offset="0.550" stop-color="#d04545" /><stop offset="0.600" stop-color="#ca404a" /><stop offset="0.650" stop-color="#c33b4f" /><stop offset="0.700" stop-color="#bc3754" /><stop offset="0.750" stop-color="#b3325a" /><stop offset="0.800" stop-color="#ab2f5e" /><stop offset="0.850" stop-color="#a32c61" /><stop offset="0.900" stop-color="#9b2964" /><stop offset="0.950" stop-color="#932667" /><stop offset="1.000" stop-color="#8c2369" /></linearGradient><linearGradient id="icon-ink" x1="35" y1="40" x2="250" y2="250" gradientUnits="userSpaceOnUse"><stop offset="0" stop-color="#fca50a" /><stop offset=".5" stop-color="#ed6925" /><stop offset="1" stop-color="#b53657" /></linearGradient><mask id="signal-cutout" maskUnits="userSpaceOnUse" x="20" y="20" width="260" height="260"><rect x="20" y="20" width="260" height="260" fill="white" /><path d="M20 193 H64 V113 H118 V193 H145 C154 193 163 173.944 172 153 C181 132.056 190 113 199 113 C208 113 217 132.056 226 153 C235 173.944 244 193 253 193 H275" fill="none" stroke="black" stroke-width="12" stroke-linejoin="round" stroke-linecap="round" /></mask></defs><g id="CommKit-symbol"><path d="M145 28 L253 90 V216 L145 278 L37 216 V90 Z" fill="url(#icon-ink)" mask="url(#signal-cutout)" /></g><g id="CommKit-wordmark" transform="translate(255 18) scale(.88)" fill="url(#ink)"><path d="M 120.79102 36.884766 C 107.14268 36.884766 94.7305 39.308148 83.554688 44.154297 C 72.378875 49.000446 62.81114 55.998142 54.849609 65.146484 C 46.888079 74.294827 40.755319 85.371472 36.453125 98.376953 C 32.150931 111.38243 30 125.99508 30 142.21484 L 30 153.93359 C 30 170.15336 32.051859 184.74123 36.15625 197.69727 C 40.260641 210.6533 46.244792 221.6804 54.107422 230.7793 C 61.970051 239.87819 71.562554 246.85112 82.886719 251.69727 C 94.210883 256.54341 107.09366 258.9668 121.5332 258.9668 C 139.33538 258.9668 154.7882 255.77755 167.89258 249.39844 C 180.99696 243.01932 191.25821 234.09361 198.67578 222.62109 C 206.09336 211.14858 210.14754 197.89623 210.83984 182.86328 L 158.91797 182.86328 C 158.72017 190.97316 157.13695 197.6241 154.16992 202.81641 C 151.20289 208.00871 147.0001 211.86474 141.56055 214.38672 C 136.12099 216.90869 129.44528 218.16992 121.5332 218.16992 C 114.41233 218.16992 108.40341 216.95823 103.50781 214.53516 C 98.612213 212.11208 94.632337 208.33035 91.566406 203.1875 C 88.500475 198.04465 86.275239 191.41848 84.890625 183.30859 C 83.506011 175.19871 82.8125 165.40611 82.8125 153.93359 L 82.8125 141.91797 C 82.8125 130.44545 83.580316 120.63004 85.113281 112.4707 C 86.646247 104.31137 88.995324 97.658477 92.160156 92.515625 C 95.324988 87.372773 99.280096 83.615812 104.02734 81.242188 C 108.77459 78.868563 114.36245 77.681641 120.79102 77.681641 C 129.98881 77.681641 137.35803 79.06671 142.89648 81.835938 C 148.43494 84.605166 152.48912 88.733651 155.06055 94.222656 C 157.63197 99.711662 159.01704 106.51121 159.21484 114.62109 L 211.13672 114.62109 C 209.85101 99.687043 205.64822 86.360394 198.52734 74.640625 C 191.40647 62.920857 181.36814 53.697924 168.41211 46.972656 C 155.45608 40.247388 139.58221 36.884766 120.79102 36.884766 z " /><path d="M 298.42773 92.515625 C 285.86731 92.515625 274.79066 94.569437 265.19727 98.673828 C 255.60387 102.77822 247.54309 108.48992 241.01562 115.80859 C 234.48816 123.12727 229.54427 131.75576 226.18164 141.69531 C 222.81901 151.63486 221.13672 162.48859 221.13672 174.25781 L 221.13672 177.37305 C 221.13672 189.04336 222.81901 199.84951 226.18164 209.78906 C 229.54427 219.72861 234.48816 228.38188 241.01562 235.75 C 247.54309 243.11812 255.65341 248.82982 265.3457 252.88477 C 275.038 256.93971 286.16418 258.9668 298.72461 258.9668 C 311.28504 258.9668 322.36168 256.93971 331.95508 252.88477 C 341.54848 248.82982 349.60925 243.11812 356.13672 235.75 C 362.66418 228.38188 367.61002 219.72861 370.97266 209.78906 C 374.33529 199.84951 376.01562 189.04336 376.01562 177.37305 L 376.01562 174.25781 C 376.01563 162.48859 374.33529 151.63486 370.97266 141.69531 C 367.61002 131.75576 362.66418 123.12727 356.13672 115.80859 C 349.60925 108.48992 341.52566 102.77822 331.88281 98.673828 C 322.23997 94.569437 311.08706 92.515625 298.42773 92.515625 z M 298.42773 131.08789 C 303.76839 131.08789 308.21886 132.22528 311.7793 134.5 C 315.33973 136.77472 318.15941 139.91443 320.23633 143.91992 C 322.31325 147.92541 323.79739 152.52449 324.6875 157.7168 C 325.57761 162.9091 326.02148 168.42265 326.02148 174.25781 L 326.02148 177.37305 C 326.02148 183.40601 325.57761 189.0434 324.6875 194.28516 C 323.79739 199.52691 322.31325 204.10122 320.23633 208.00781 C 318.15941 211.9144 315.3645 214.95699 311.85352 217.13281 C 308.34253 219.30863 303.96636 220.39648 298.72461 220.39648 C 293.38396 220.39648 288.93348 219.30863 285.37305 217.13281 C 281.81261 214.95699 278.99489 211.9144 276.91797 208.00781 C 274.84105 204.10122 273.35691 199.52691 272.4668 194.28516 C 271.57669 189.0434 271.13281 183.40601 271.13281 177.37305 L 271.13281 174.25781 C 271.13281 168.42265 271.57669 162.9091 272.4668 157.7168 C 273.35691 152.52449 274.84105 147.92541 276.91797 143.91992 C 278.99489 139.91443 281.78784 136.77472 285.29883 134.5 C 288.80981 132.22528 293.18598 131.08789 298.42773 131.08789 z " /><path d="M 480.36719 92.515625 C 470.27929 92.515625 461.40311 94.445595 453.73828 98.302734 C 446.18903 102.10171 439.82175 107.44341 434.62305 114.3125 L 432.89648 95.484375 L 386.01562 95.484375 L 386.01562 256 L 436.01172 256 L 436.01172 144.32031 C 436.41891 143.62939 436.82378 142.93778 437.27148 142.28906 C 439.69456 138.77808 442.63807 136.03271 446.09961 134.05469 C 449.56114 132.07667 453.46672 131.08789 457.81836 131.08789 C 461.9722 131.08789 465.48343 131.68038 468.35156 132.86719 C 471.21969 134.054 473.42016 136.37831 474.95312 139.83984 C 476.48609 143.30138 477.25195 148.44536 477.25195 155.26953 L 477.25195 256 L 527.24805 256 L 527.24805 148.89062 C 527.24805 147.33464 527.17118 145.87431 527.11719 144.38672 C 527.51571 143.67165 527.91582 142.95911 528.35938 142.28906 C 530.68355 138.77808 533.57753 136.03271 537.03906 134.05469 C 540.5006 132.07667 544.60431 131.08789 549.35156 131.08789 C 553.3076 131.08789 556.71976 131.70514 559.58789 132.94141 C 562.45602 134.17767 564.65649 136.50198 566.18945 139.91406 C 567.72242 143.32615 568.48828 148.34629 568.48828 154.97266 L 568.48828 256 L 618.63281 256 L 618.63281 154.97266 C 618.63281 143.30234 617.39635 133.48692 614.92383 125.52539 C 612.4513 117.56386 608.98961 111.15865 604.53906 106.3125 C 600.08852 101.46635 594.79592 97.955119 588.66406 95.779297 C 582.5322 93.603475 575.80696 92.515625 568.48828 92.515625 C 558.40038 92.515625 549.54897 94.445595 541.93359 98.302734 C 534.31822 102.15987 527.9645 107.59912 522.87109 114.62109 C 522.37627 115.30328 521.90415 116.00736 521.43359 116.71289 C 519.502 112.42475 517.16107 108.70282 514.33984 105.64648 C 510.186 101.14649 505.24212 97.83145 499.50586 95.705078 C 493.7696 93.578707 487.38916 92.515625 480.36719 92.515625 z " /><path d="M 722.98438 92.515625 C 712.89647 92.515625 704.01834 94.445595 696.35352 98.302734 C 688.80426 102.10171 682.43698 107.44341 677.23828 114.3125 L 675.51172 95.484375 L 628.63281 95.484375 L 628.63281 256 L 678.62695 256 L 678.62695 144.32031 C 679.03414 143.62939 679.43901 142.93778 679.88672 142.28906 C 682.30979 138.77808 685.25331 136.03271 688.71484 134.05469 C 692.17638 132.07667 696.08195 131.08789 700.43359 131.08789 C 704.58744 131.08789 708.09867 131.68038 710.9668 132.86719 C 713.83493 134.054 716.03539 136.37831 717.56836 139.83984 C 719.10132 143.30138 719.86719 148.44536 719.86719 155.26953 L 719.86719 256 L 769.86328 256 L 769.86328 148.89062 C 769.86328 147.33464 769.78642 145.87431 769.73242 144.38672 C 770.13095 143.67165 770.53106 142.95911 770.97461 142.28906 C 773.29878 138.77808 776.19276 136.03271 779.6543 134.05469 C 783.11583 132.07667 787.21955 131.08789 791.9668 131.08789 C 795.92284 131.08789 799.335 131.70514 802.20312 132.94141 C 805.07125 134.17767 807.27172 136.50198 808.80469 139.91406 C 810.33765 143.32615 811.10352 148.34629 811.10352 154.97266 L 811.10352 256 L 861.24805 256 L 861.24805 154.97266 C 861.24805 143.30234 860.01159 133.48692 857.53906 125.52539 C 855.06654 117.56386 851.60484 111.15865 847.1543 106.3125 C 842.70375 101.46635 837.41116 97.955119 831.2793 95.779297 C 825.14743 93.603475 818.42219 92.515625 811.10352 92.515625 C 801.01561 92.515625 792.16421 94.445595 784.54883 98.302734 C 776.93345 102.15987 770.57973 107.59912 765.48633 114.62109 C 764.9915 115.30328 764.51939 116.00736 764.04883 116.71289 C 762.11723 112.42475 759.77631 108.70282 756.95508 105.64648 C 752.80124 101.14649 747.85735 97.83145 742.12109 95.705078 C 736.38484 93.578707 730.00635 92.515625 722.98438 92.515625 z " /><path d="M 871.24805 40 L 871.24805 256 L 923.31836 256 L 923.31836 198.98242 L 944.62891 175.66992 L 990.66992 256 L 1052.2363 256 L 979.33594 135.39062 L 1052.2363 40 L 988 40 L 941.41797 107.79688 L 923.31836 135.24609 L 923.31836 40 L 871.24805 40 z " /><path d="M 1089.9785 29.763672 C 1081.7697 29.763672 1075.094 32.038445 1069.9512 36.587891 C 1064.8083 41.137337 1062.2363 46.972878 1062.2363 54.09375 C 1062.2363 61.214622 1064.8083 67.050163 1069.9512 71.599609 C 1075.094 76.149055 1081.7697 78.423828 1089.9785 78.423828 C 1098.1873 78.423828 1104.863 76.149055 1110.0059 71.599609 C 1115.1487 67.050163 1117.7207 61.214622 1117.7207 54.09375 C 1117.7207 46.972878 1115.1487 41.137337 1110.0059 36.587891 C 1104.863 32.038445 1098.1873 29.763672 1089.9785 29.763672 z M 1065.2031 95.484375 L 1065.2031 256 L 1115.3457 256 L 1115.3457 95.484375 L 1065.2031 95.484375 z " /><path d="M 1148.0449 55.576172 L 1148.0449 95.484375 L 1127.7207 95.484375 L 1127.7207 130.19727 L 1148.0449 130.19727 L 1148.0449 209.86328 C 1148.0449 221.4347 1150.0225 230.82906 1153.9785 238.04883 C 1157.9346 245.2686 1163.6958 250.56119 1171.2617 253.92383 C 1178.8276 257.28646 1187.9515 258.9668 1198.6328 258.9668 C 1203.8746 258.9668 1208.8184 258.59527 1213.4668 257.85352 C 1218.1151 257.11176 1222.5656 256.04868 1226.8184 254.66406 L 1226.8184 218.91211 C 1225.137 219.20881 1223.3576 219.43173 1221.4785 219.58008 C 1219.5994 219.72843 1217.2256 219.80273 1214.3574 219.80273 C 1210.3025 219.80273 1207.0637 219.33214 1204.6406 218.39258 C 1202.2176 217.45302 1200.5105 215.82222 1199.5215 213.49805 C 1198.5325 211.17387 1198.0391 207.88556 1198.0391 203.63281 L 1198.0391 130.19727 L 1224.4453 130.19727 L 1224.4453 95.484375 L 1198.0391 95.484375 L 1198.0391 55.576172 L 1148.0449 55.576172 z " /></g></svg>
|
|
@@ -119,13 +119,14 @@ jobs:
|
|
|
119
119
|
tests/coding/test_polar.py::TestDecoding::test_crc_failure_keeps_the_best_path
|
|
120
120
|
tests/coding/test_convolutional.py tests/coding/test_turbo.py::TestEncoding
|
|
121
121
|
tests/coding/test_turbo.py::TestDecoding::test_noiseless
|
|
122
|
+
tests/coding/test_linear_codes.py::TestMaximumLikelihood
|
|
122
123
|
-k "not waterfall and not threshold"
|
|
123
124
|
--device=cpu --cov=commkit --cov-report=
|
|
124
125
|
|
|
125
126
|
- name: Kernel coverage report
|
|
126
127
|
run: >-
|
|
127
128
|
uv run coverage report --show-missing --fail-under=85
|
|
128
|
-
--include="commkit/equalization/
|
|
129
|
+
--include="commkit/equalization/_numba_loops.py,commkit/equalization/_sequential/*,commkit/frequency.py,commkit/recovery/*,commkit/coding/ldpc.py,commkit/coding/shaping.py,commkit/coding/polar.py,commkit/coding/convolutional.py,commkit/coding/turbo.py"
|
|
129
130
|
|
|
130
131
|
# GPU legs (--device=gpu / --device=all) require CuPy + CUDA and are run
|
|
131
132
|
# manually or on a self-hosted runner; they are intentionally not part of
|
commkit-3.5.0/AGENTS.md
ADDED
|
@@ -0,0 +1,288 @@
|
|
|
1
|
+
# AGENTS.md
|
|
2
|
+
|
|
3
|
+
Guide for anyone, human or coding agent, changing the **CommKit** repository.
|
|
4
|
+
|
|
5
|
+
The rules below describe the code as it is. New code follows them; a change that breaks one changes this file in the same commit.
|
|
6
|
+
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## 1. What CommKit is
|
|
10
|
+
|
|
11
|
+
A Python library for digital-communications research: simulation, receiver DSP, metrics, plotting and experimental captures, on NumPy (CPU) and CuPy (GPU) data. It is **not differentiable**. A separate JAX library, *commax*, will hold the blocks that must sit inside a gradient. commax depends on commkit, never the reverse, and takes its conventions (`Constellation`, bit labels, pulse taps, frame layouts) from commkit.
|
|
12
|
+
|
|
13
|
+
Users are researchers and engineers. Design for them:
|
|
14
|
+
|
|
15
|
+
- **Algorithms are plain functions.** Data is a `Signal` or an array. Things that *describe* a modulation, a pulse or an embedded sub-algorithm are small frozen value objects written inline in the call:
|
|
16
|
+
|
|
17
|
+
```python
|
|
18
|
+
res = lms(rx, num_taps=21, step_size=1e-3, cpr=BPS(test_phases=64))
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
- **Everything is expressed in Python.** No configuration files, registries, global switches or multi-step setup.
|
|
22
|
+
- **We deliberately do not build** pipeline or receiver objects, DSP methods on `Signal`, string shorthands for constellations, or public "config objects per algorithm".
|
|
23
|
+
- **Scope is the reference model and its inverse**, not a catalogue of research variants: the fiber channel and standard digital backpropagation (`correct_fiber_propagation`, with `steps_per_span` and `nonlinear_scale`), like chromatic dispersion and its compensation. Reduced-complexity nonlinearity compensation (filtered or enhanced split step, perturbation-based) is left to user code built on these; learned DBP, which needs gradients, belongs in commax.
|
|
24
|
+
|
|
25
|
+
---
|
|
26
|
+
|
|
27
|
+
## 2. Commands
|
|
28
|
+
|
|
29
|
+
Always use `uv`. Python 3.12+.
|
|
30
|
+
|
|
31
|
+
```bash
|
|
32
|
+
uv sync --all-extras # environment incl. GPU packages
|
|
33
|
+
uv run nbstripout --install # once per clone: strip notebook outputs on commit
|
|
34
|
+
uv run pytest # CPU + GPU tests (default --device=all)
|
|
35
|
+
uv run pytest --device=cpu # what CI runs
|
|
36
|
+
uv run pytest --run-slow # also the long statistical validation (marker slow)
|
|
37
|
+
uv run pytest tests/equalization/ -k lms # subset
|
|
38
|
+
uv run pytest --cov=commkit # coverage
|
|
39
|
+
uv run pytest --device=cpu -p tests.plugins.precision_trace \
|
|
40
|
+
--ignore=tests/test_examples.py # precision audit (see Numerics); the suite passes under it
|
|
41
|
+
uv run pytest --device=gpu -p tests.plugins.transfer_trace \
|
|
42
|
+
--ignore=tests/test_examples.py # transfer audit (see Performance); fails on an undocumented transfer
|
|
43
|
+
scripts/sanitize.sh # kernels under compute-sanitizer (see Performance); setup in the script
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
Numba kernel coverage runs the kernel tests with `NUMBA_DISABLE_JIT=1` and reports only the kernel modules; the test selection and the `--include` list are in the `kernel-coverage` job of `.github/workflows/ci.yml`.
|
|
47
|
+
|
|
48
|
+
**Commit gate.** CI fails on any of these, so run all of them before every commit, with `ruff format` last:
|
|
49
|
+
|
|
50
|
+
```bash
|
|
51
|
+
uv run ruff check . --fix
|
|
52
|
+
uv run mypy commkit/
|
|
53
|
+
uv run pytest
|
|
54
|
+
uv run ruff format .
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
**Benchmarks** are explicit-only; `uv run pytest` never collects them. A commit that changes a public signature smoke-runs them once, untimed, so their call sites cannot rot:
|
|
58
|
+
|
|
59
|
+
```bash
|
|
60
|
+
uv run pytest benchmarks/ --benchmark-disable --device=all
|
|
61
|
+
uv run pytest benchmarks/bench_bps.py --benchmark-only --device=all # timed
|
|
62
|
+
uv run pytest benchmarks/ --benchmark-only --device=all \
|
|
63
|
+
--benchmark-compare=0008 --benchmark-storage=file://benchmarks/baselines
|
|
64
|
+
uv run python benchmarks/record_baseline.py NAME # record a new baseline (see Benchmarks)
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
---
|
|
68
|
+
|
|
69
|
+
## 3. Architecture
|
|
70
|
+
|
|
71
|
+
Dependencies point downward only:
|
|
72
|
+
|
|
73
|
+
| Layer | Modules | May import |
|
|
74
|
+
| --- | --- | --- |
|
|
75
|
+
| Infrastructure | `backend`, `logger`, `_cuda` | NumPy, CuPy |
|
|
76
|
+
| Array and math helpers | `_array` (shape and validation), `math` (power, normalization, dB, linear trend) | infrastructure |
|
|
77
|
+
| Value objects | `mapping.Constellation`, pulse classes, `recovery` CPR configs, `coding` (code objects with their `encode` / `decode`; array-only, never imports `core`) | layers above |
|
|
78
|
+
| Core | `core` (`Signal`, frames, generation), `io` | layers above |
|
|
79
|
+
| DSP | `mapping`, `filtering`, `multirate`, `spectral`, `smoothing`, `impairments`, `timing`, `frequency`, `recovery`, `equalization`, `metrics`, `analysis` | layers above, and lower DSP modules |
|
|
80
|
+
| Presentation | `plotting` | everything; **nothing imports plotting** (no `debug_plot`: plot functions consume results or recompute through public functions) |
|
|
81
|
+
|
|
82
|
+
- **Responsibilities:** `filtering` holds signal-chain filters with a real frequency response; `smoothing` display and estimation smoothers only; `analysis` laser and phase characterization; `metrics` is the reporting layer.
|
|
83
|
+
- **Codes are storable:** `io` stores a code as its class and constructor fields, so those fields are numbers, strings, tuples, host arrays, constellations or other codes.
|
|
84
|
+
- **Splitting modules:** promote a flat module to a package only when it exceeds about 1000 lines *and* has two or more separable concerns. Never create a `utils/` package or one-function files.
|
|
85
|
+
- **One documented home per public name.** A package `__init__` re-exports its subpackages' public names, and users and documentation import them from the package (`commkit.impairments.Fiber`), not from the subpackage that defines them.
|
|
86
|
+
- **Shared physics lives in one private module**, used by the channel and by its compensation: `_dispersion` (β₂, β₃, the dispersion response) and `_fiber` (`Fiber` and the split-step solver behind `apply_fiber_propagation` and `correct_fiber_propagation`).
|
|
87
|
+
|
|
88
|
+
---
|
|
89
|
+
|
|
90
|
+
## 4. API rules
|
|
91
|
+
|
|
92
|
+
### Signal
|
|
93
|
+
|
|
94
|
+
- `Signal` holds:
|
|
95
|
+
- samples: NumPy or CuPy, `(N,)` or `(C, N)`, time on the last axis;
|
|
96
|
+
- the **facts** `sampling_rate`, `symbol_rate`, `center_frequency` and `domain` (`"DIG"`, `"RF"` or `"OPT"`; only RF and optical signals have a carrier, and an optical carrier gives the derived fact `center_wavelength_nm`);
|
|
97
|
+
- the **description** `constellation` and `pulse`;
|
|
98
|
+
- the ground truth `reference`: transmitted symbols, the bits on the air `bits`, the source bits `info_bits` (the same array when nothing is coded or matched) and the `code` between them, plus the `frame` layout.
|
|
99
|
+
- It never holds pipeline results.
|
|
100
|
+
- Construction does no hidden work: no mapping, no normalization, no device move, no shape guessing. Factories (`generate`, frames) do that work.
|
|
101
|
+
- `sig.to("gpu")` and `sig.replace(...)` return new Signals sharing the arrays; `clone()` copies.
|
|
102
|
+
- **Alignment invariant:** reference symbol *k* corresponds to symbol period *k* of the samples. A function that drops or shifts symbol periods slices the reference to match. Metrics raise on mismatch; they never guess.
|
|
103
|
+
|
|
104
|
+
### Function signatures
|
|
105
|
+
|
|
106
|
+
- `f(data, other_data, *, params)`: everything after the data arguments is keyword-only. A function without data arguments may keep a single primary parameter positional (`barker_sequence(13)`); with several, all are keyword-only. The contract registry records each function's data-argument count.
|
|
107
|
+
- **Signal awareness.** A function whose main argument is waveform data (or a field a Signal carries) accepts an array or a Signal: it unwraps once with `adapt_signal()` (`core/_signal_adapter.py`), works on arrays, and returns through `wrap_samples()`, never by re-entering the public function. Functions on *derived* quantities (phase trajectories, correlations, taps, PSD bins, results) and synthesis primitives stay array-only.
|
|
108
|
+
- **Types:** a transform is `def f(samples: S, ...) -> S` with the TypeVar `S` from `core/_signal_adapter.py`. Do not reassign `samples`; name the unwrapped array `x`.
|
|
109
|
+
- **Facts versus choices.** Facts (`sampling_rate`, `symbol_rate`, hence `sps`) come from the Signal; a conflicting argument raises `ValueError`, an equal one is fine, and a missing fact for array input raises. Choices (decision `constellation`, `noise_var`, `pulse`) default to the Signal's value; an explicit argument wins. Neither case warns.
|
|
110
|
+
- Integer-SPS algorithms validate with `require_integer_sps()` before any cast; never truncate 1.5 SPS to 1.
|
|
111
|
+
- **Shapes:** SISO is `(N,)`, MIMO `(C, N)`. Use `as_2d`, `restore_1d`, `broadcast_channels` and `require_channels`; never hand-roll the promote/squeeze idiom.
|
|
112
|
+
- **Value objects are frozen dataclasses** with read-only NumPy arrays:
|
|
113
|
+
- `Constellation`: `.qam(M)`, `.psk(M)`, `.pam(M, unipolar=)`, or arbitrary `points`; `.shaped(nu=|entropy=)` keeps unit power. Functions accept the object only, never strings.
|
|
114
|
+
- Pulses: `RRC`, `RC`, `Gaussian`, `Rect`, `SmoothRect`. A raw taps array is accepted wherever a pulse is.
|
|
115
|
+
- Algorithm objects, one per method (`BPS`, `PLL`, `ViterbiViterbi`, `MthPower`, `CycleSlip`, ...), used standalone (`correct_carrier_phase(y, BPS())`) and nested (`lms(..., cpr=BPS())`). Each lives next to its kernel and validates its parameters on construction. Class names are unique across the package.
|
|
116
|
+
- **Design and apply are separate functions**: `RRC(...).taps(sps)` and `fir_filter`, `butterworth_sos` and `iir_filter`.
|
|
117
|
+
- **Randomness:** `rng: int | np.random.Generator | None` (SciPy SPEC 7); never a global RNG.
|
|
118
|
+
- What is transmitted (bits, symbols, pilots) and the few random parameters of a realization (the PMD waveplates of `Fiber`) are drawn on the host, so a seed gives the same payload and fiber on every device.
|
|
119
|
+
- What the channel adds (AWGN, phase noise, ASE) is drawn on the data's device through `_random.standard_normal` / `complex_normal`; realizations differ between devices, statistics agree.
|
|
120
|
+
- A seed is reproducible within a library version only. Tests and baselines assert statistics, never exact realizations.
|
|
121
|
+
- **Device follows the data.** No `backend=` arguments, no global switches. Unsupported array types (JAX, PyTorch) raise `TypeError`.
|
|
122
|
+
- Only factories with no input data to follow take `device: str = "cpu"` (never "GPU if available"): `generate`, `generate_phase_noise`, `Preamble`/`SingleCarrierFrame.to_signal`, `load_npz`, `decoder_exit_curve`, `demapper_exit_curve`, `error_rate_curve`. They validate it with `require_device()` (in `coding`, by the same check in `coding.exit`), draw randomness as above and do the vectorized work on `device`.
|
|
123
|
+
|
|
124
|
+
### Returns
|
|
125
|
+
|
|
126
|
+
- **Transforms:** array in gives array out on the same device; Signal in gives a new Signal (with `sampling_rate` updated when the rate changes).
|
|
127
|
+
- **Estimators** reduce over the time axis: `(N,)` gives 0-d, `(C, N)` gives `(C,)`, on the input device.
|
|
128
|
+
- **Metrics** (`evm`, `snr`, `ber`, `fer`, `ser`, `gmi`, `ngmi`, `mi`) and **analysis summaries** (`estimate_linewidth`, `allan_deviation`, the `frequency_drift` statistics, which fit plot-sized reductions) return host values: a `float` for 1-D input, `np.ndarray (C,)` for 2-D. Sample-rate outputs (trajectories, PSDs, `dsh_phase`) stay on the input device.
|
|
129
|
+
- Metrics **raise** on empty selections, never return `None` or `0.0` for "nothing measured", and never silently align, rotate or permute against the reference.
|
|
130
|
+
- **More than one value** is a frozen dataclass with named fields. No dicts, no tuples longer than two.
|
|
131
|
+
- **Equalizers** return `EqualizerResult`: `y_hat` is always an array, `signal` the 1-SPS Signal for Signal input, `state` the continuation state (`state=result.state`), and diagnostics as data.
|
|
132
|
+
|
|
133
|
+
### Naming
|
|
134
|
+
|
|
135
|
+
| Prefix | Meaning |
|
|
136
|
+
| --- | --- |
|
|
137
|
+
| `apply_*` | add an impairment or apply a model |
|
|
138
|
+
| `estimate_<quantity>(x, method)` | measure, never change the data; returns a frozen `<Quantity>Estimate` (value plus diagnostics) |
|
|
139
|
+
| `correct_<quantity>(x, how)` | remove the impairment; returns only the corrected data. `how` is an estimate or an algorithm object (which estimates first) |
|
|
140
|
+
| `resolve_*` | pick among discrete candidates (π/2 rotation, channel permutation); nothing else |
|
|
141
|
+
| `generate` | synthesis from parameters, takes `rng` |
|
|
142
|
+
| `encode` / `decode` | information bits to a code's transmitted bits, and LLRs (or hard decisions) back to a `DecodeResult`; dispatched on the code object, the decoder chosen by a `method` object |
|
|
143
|
+
| `plot_*` | every public plotting function (only `apply_default_theme` is exempt) |
|
|
144
|
+
|
|
145
|
+
- The algorithm is chosen by an object, never by the function name or a string: `estimate_carrier_phase(y, BPS())`, not `recover_carrier_phase_bps(y)`. The verb function dispatches on the object's type through a private table in its module. There is no `recover_*` or `compensate_*`.
|
|
146
|
+
- Computations use plain nouns (`allan_deviation`).
|
|
147
|
+
- **Glossary**, one name each:
|
|
148
|
+
- `num_taps`, `step_size`, `sps`, `constellation`, `pulse`, `rng`, `num_symbols`, `num_channels` (never streams), `sampling_rate`, `symbol_rate`, `noise_var`, `nfft`, `regularization`, `max_iterations`.
|
|
149
|
+
- Noise has three names, each with one meaning: `noise_var` is the variance E|n|² of the noise in the samples a function receives (measured by `estimate_noise_variance`); `snr_db` a power ratio in those samples (measured by `snr`); `esn0_db` the channel's textbook Es/N0, independent of the dtype (real samples carry the in-phase half of the noise, `N0/2`). A function taking `noise_var` for a demapping model (`compute_llr`, `mi`, `gmi`, `ngmi`) uses the likelihood of complex or real noise after the samples' dtype and also accepts `esn0_db`, so users never convert by hand.
|
|
150
|
+
- A size names its unit: `num_symbols`, `num_blocks` (codewords of a FEC code), `info_bits` (a code's information length where it is a parameter), `blocks_per_frame`, `num_variables`, `num_segments`. In `coding`, `length` and `n` mean the codeword length in bits and `k` the information length, nothing else.
|
|
151
|
+
- A seeded part of a factory takes `<part>_rng` (`payload_rng`), with the semantics of `rng`.
|
|
152
|
+
- `method=` strings name only a numerical flavor of one algorithm (`method="maxlog" | "exact"`); algorithms are objects.
|
|
153
|
+
- Output scaling has three names: `normalize` (bool) measures the power and rescales to the library's convention; `correct_power` (bool) applies the exact, known gain of a rate change without measuring; `<thing>_normalization` (a string) chooses how coefficients are scaled (`taps_normalization="unit_energy" | "unity_gain"`).
|
|
154
|
+
- A search window is `search_range`, a `(start, stop)` pair in the axis's unit, or `search_band`, a half-width in Hz around a nominal frequency.
|
|
155
|
+
- Values are in SI units without a suffix (`linewidth` in Hz, `drift_rate` in rad/s, `taus` in s). A result holding one quantity in two units suffixes the SI one (`lags` in samples, `lag_s`). A conventional non-SI unit goes into the name: `_db`, `_dbm`, `_deg`, `_nm`, `_km`, `_ps_nm_km`. Normalized quantities say so (`normalized_bandwidth`). No second name for one quantity in another unit (no `center_frequency_ghz`); `center_wavelength_nm` is a different, derived quantity.
|
|
156
|
+
|
|
157
|
+
### Docstrings
|
|
158
|
+
|
|
159
|
+
Docstrings describe the code as it is; a change in behavior updates them in the same commit. NumPy format (numpydoc), sections in its order: Parameters, Returns, Raises, Warns, See Also, Notes, References, Examples.
|
|
160
|
+
|
|
161
|
+
- **Summary line** on the line of the opening quotes, one line, ending with a period. Functions: imperative mood (`Apply ...`), or a noun phrase for a function named after what it returns (`evm`, `welch_psd`). Classes and modules: a noun phrase.
|
|
162
|
+
- **Module docstrings** name what the module holds (`:func:`, `:class:`), then the conventions particular to it (a sign convention, a scaling, a file layout). No marketing adjectives, no history of earlier layouts, no restatement of this file. A private module's summary ends with "(private)" and the body names its users.
|
|
163
|
+
- **Sections by role:** functions and the objects users configure (algorithm objects, codes, pulses, `Constellation`, frames, `Signal`) list their arguments under Parameters. Result classes, the frozen dataclasses a function returns (`*Estimate`, `*Result`, `*Curve`, `FilterResponse`, ...), list their fields under Attributes with the stored types, even when users may build one themselves; the summary then says the constructor takes the same fields.
|
|
164
|
+
- **Types:** inputs are `array_like`, or `array_like or Signal` where Signal-aware; returned arrays and fields are `ndarray` (on the input's device), `ndarray or Signal` for transforms, `numpy.ndarray` where always on the host. Shapes go in the description in double backticks: ``(C, N)``.
|
|
165
|
+
- **Plots** return `(fig, ax) or None`, multi-panel plots `(fig, axes) or None` (`None` with `show=True`).
|
|
166
|
+
- **Defaults:** `name : type, default value`; `name : type, optional` when the default is `None`, with the description saying what `None` does.
|
|
167
|
+
- Units go in the description; inline code in double backticks; library names through `:func:` / `:class:` / `:mod:` / `:meth:`.
|
|
168
|
+
|
|
169
|
+
### Errors and logging
|
|
170
|
+
|
|
171
|
+
- Wrong input raises, with a message naming the argument.
|
|
172
|
+
- **No fallbacks for a broken environment.** Code never catches a failure of CuPy, a CUDA kernel (compile, load, launch), a graph capture or another dependency to run a different implementation; the failure raises to the caller. A path kept "just in case" is code no test or user runs. (Dispatch on the *input* is allowed; see the kernel policy in [Performance](#6-performance).)
|
|
173
|
+
- `logger` is for diagnostics only. Per-channel logging that needs a device-to-host transfer is wrapped in `if logger.isEnabledFor(logging.INFO):`.
|
|
174
|
+
- **Levels:** `DEBUG` traces what a call is about to do (method, parameters, sizes). `INFO` is one summary per public call of what a user would print next (the values found, the parameters applied, an equalizer's exit MSE, a decoder's failed checks, what `generate` or `io` produced). `WARNING` flags a returned but suspect result (no convergence, a fit outside its valid range, near-zero power, a code with 4-cycles). Value objects and pure helpers do not log.
|
|
175
|
+
- `warnings.warn` is used only for `PrecisionWarning` (see [Numerics](#5-numerics)). The library never configures handlers, Matplotlib or warning filters at import time.
|
|
176
|
+
|
|
177
|
+
---
|
|
178
|
+
|
|
179
|
+
## 5. Numerics
|
|
180
|
+
|
|
181
|
+
- **Storage dtypes:** `complex64` for IQ samples, `float32` for real signals; `generate`, frames and `load_npz` produce them, so single precision is the default path.
|
|
182
|
+
- **Precision contract.** Every function computes in a predictable precision, and none promotes single-precision data on its own:
|
|
183
|
+
- **Transforms keep the input's precision** throughout (filters, impairments, the fiber solver and DBP, resampling, delays, carrier-phase correction): complex64 in and out, complex128 in and out.
|
|
184
|
+
- **Fixed-precision blocks** compute in single precision whatever the input: the equalizers in complex64, `compute_llr` / `Constellation.llr` in float32. Double input emits `commkit.PrecisionWarning` (`_array.warn_if_double`, attributed to the caller's line). The test suite turns it into an error (`pytest_configure` in `tests/conftest.py`; not pyproject's `filterwarnings`, which imports commkit before coverage starts), so a library path feeding double data to these blocks fails CI. Training and reference symbols are not checked.
|
|
185
|
+
- **Deliberate float64 / complex128**, documented where it happens, only on phases, small arrays or accumulators, never on sample-rate data that is then cast back:
|
|
186
|
+
- phases before `xp.unwrap()` (float32 rounding causes spurious quadrant slips), phase trajectories and ramps (wrapped in float64, rotated in the data's precision), frequency grids and estimates;
|
|
187
|
+
- recursion state: PLL, Tikhonov/Kalman block transitions, the IIR filter's sections and state (poles near z = 1);
|
|
188
|
+
- RLS keeps `P`, the gain `k` and the regressor buffers in complex128 (in single precision `P` loses positive definiteness and the filter diverges);
|
|
189
|
+
- LMS/CMA/RDE accumulate dot products and gradient updates in complex128 around complex64 inputs and weights; the block equalizers stay complex64, as their tap sums run inside the FFTs;
|
|
190
|
+
- sums: metrics compute per element in the input's precision and accumulate in float64.
|
|
191
|
+
- complex128 pays only where single-precision rounding accumulates over many FFT passes, as in the fiber solver; there the GPU runs its plain CuPy stages, not the fused complex64 kernels.
|
|
192
|
+
- **The NEP 50 trap:** under NumPy 2 a NumPy scalar is not weak, so `np.float64(a) * x64` (any `np.sqrt`, `np.pi * ...` result, or a float64 array such as `xp.fft.fftfreq`) promotes complex64 to complex128. Combine single-precision data only with Python scalars or arrays cast to its precision. Frequency responses (dispersion, delays, Jones matrices) are computed at float64 phase accuracy and cast to the spectrum's precision before the multiply; rotate with `math._derotate`.
|
|
193
|
+
- **Audit with `tests/plugins/precision_trace.py`** after touching numerics. It lists every float64/complex128 array of sample-rate size that a NumPy path creates from single-precision, integer or bool data or from scratch, with its source line; output dtypes alone hide a compute-in-double-and-cast-back. Every reported site must be one of the deliberate uses above.
|
|
194
|
+
- **Normalization invariant:** at `sps` samples per symbol, `E[|x|²] = 1/sps`; at symbol rate, `E[|x|²] = 1`. Any rate-changing block applies the exact gain correction.
|
|
195
|
+
|
|
196
|
+
---
|
|
197
|
+
|
|
198
|
+
## 6. Performance
|
|
199
|
+
|
|
200
|
+
| Algorithm structure | Implementation | Device |
|
|
201
|
+
| --- | --- | --- |
|
|
202
|
+
| Vectorized: filters, resampling, spectra, impairments, metrics, frequency-domain equalizers | NumPy/CuPy via `x, xp, sp = dispatch(samples)` | follows the input |
|
|
203
|
+
| Per-symbol searches over a small table: LLRs (all points, or per axis for separable QAM; max-log without a prior in closed form), hard decisions without a square lattice | CPU: Numba `parallel=True` over blocks of symbols (LLRs), chunked NumPy (decisions). GPU: one thread per symbol (`llr`, `llr_separable`, `llr_separable_maxlog`, `bps_min_d2`'s nearest-point mode); decisions on double input or above 1024 points use the chunked search on both devices; LLRs above 16 bits per symbol raise on the GPU | follows the input |
|
|
204
|
+
| Sequential recursions: LMS/RLS/CMA/RDE, PLL, Tikhonov, cycle slips, the Mengali-Morelli bootstrap, per-codeword decoders on trellises and trees (CCDM, polar SC/SCL, Viterbi, BCJR, turbo, recursive systematic encoding) | Numba `@njit(cache=True, fastmath=True, nogil=True)`, independent rows in `prange`. The equalizers' kernel with inline carrier recovery compiles strict (unwrap, running sums) and calls fast-math tap and distance loops as separate functions (an `inline="always"` helper takes its caller's flags) | CPU; GPU input does one round trip |
|
|
205
|
+
| Linear recursions: IIR filtering | CPU: Numba without `fastmath`, so double input matches SciPy bit for bit. GPU: `iir` kernel: each block's state from a zero start in parallel, states carried across blocks in two levels (groups, then across groups), blocks filtered again, in double precision | follows the input |
|
|
206
|
+
| Binary linear algebra of codes: encoding, syndromes, ML-decoding correlations on the GPU, Chase's test patterns | one float32 GEMM per chunk (`coding._gf2.matmul_mod2`), exact below an inner dimension of 2^24, parity in place (CPU) or by one elementwise kernel writing int8 (GPU); small tables cached per code and device (`backend._device_tables`) | follows the input |
|
|
207
|
+
| Algebraic decoding (BCH, Reed-Solomon): Berlekamp-Massey, Chien search to the locator's degree, Forney | CPU: Numba `parallel=True` over codewords. GPU: `algebraic_correct`, one thread per codeword in integer table arithmetic, so both agree exactly | follows the input |
|
|
208
|
+
| Layered LDPC belief propagation (min-sum, sum-product) | CPU: Numba `parallel=True` over codewords, all iterations inside, no fast-math. GPU: `ldpc_layered`, one block per codeword, rows of a layer in parallel, early stop per codeword; same row order, so min-sum agrees bit for bit | follows the input |
|
|
209
|
+
| Maximum-likelihood decoding: the correlation with all `2^k` codewords, the decision and the per-bit maxima of the max-log soft output | CPU: one Numba pass, `parallel=True` over rows, branch-free selects under fast-math without the no-NaN and no-Inf flags (not a GEMM then a pass: its threads compete with BLAS's). GPU: the GEMM per chunk, the per-bit maxima by a CuPy `ReductionKernel` that broadcasts over the codewords instead of building `(rows, codewords, n)`. `soft=False` skips the maxima on both | follows the input |
|
|
210
|
+
| Monte Carlo analysis (EXIT curves, error-rate curves) | the existing encoders, decoders and demapper and vectorized NumPy/CuPy, through the private block functions (no INFO line per batch); closed-form parts are host NumPy on small grids. No kernel of its own | follows `device` |
|
|
211
|
+
| Elementwise chains between multi-core FFTs (fiber solver's Kerr and PMD stages) | NumPy: a Numba `parallel=True` pass competes with `scipy.fft`'s threads; adopt one only on a paired end-to-end A/B | CPU |
|
|
212
|
+
| Other hot spots limited by intermediates, launches or round trips | CUDA C++ via CuPy `RawModule` (`commkit/_cuda/src`) | GPU |
|
|
213
|
+
|
|
214
|
+
**Numba kernels live at module level** in a private module (`equalization/_numba_loops.py`, `coding/_numba_loops.py`, `mapping/_numba_llr.py`) that a getter `_get_numba_<name>()`, defined in the module that calls the kernel, imports on first use, so `import commkit` does not load Numba and `cache=True` works across processes. A kernel defined inside a function that calls another kernel defined there recompiles in every process (Numba's cache key misses the captured dispatcher); a getter may build a closure that captures only constants (`_get_numba_sos`).
|
|
215
|
+
|
|
216
|
+
**Host-sync hygiene:**
|
|
217
|
+
|
|
218
|
+
- Never extract scalars from a possibly-GPU array inside a loop (`float(x[c])`, `.item()`). Compute the vector on the device, transfer it once with `to_device(v, "cpu")`, then loop on the host.
|
|
219
|
+
- Reduce on the device before transferring, including for plotting; prefer on-device gathers (`xp.take_along_axis`); chunk `(N, M)` intermediates over N with an on-device accumulator.
|
|
220
|
+
- Build grids, windows and index tables on the data's device (`xp.fft.fftfreq`, `xp.hanning`, `xp.arange`).
|
|
221
|
+
- Every FFT goes through `commkit._fft` (`fft`, `ifft`, `rfft`, `irfft`), never `np.fft` / `xp.fft` (frequency grids excepted): on the CPU it runs multi-threaded `scipy.fft` and keeps complex64 in single precision (NumPy 2 computes it in double); on the GPU it is `cupy.fft`.
|
|
222
|
+
- Pass whole records to compiled code. No Python loops over symbols or small blocks, except inside a CUDA graph.
|
|
223
|
+
- Repeated GPU work with fixed buffers (the block equalizers' blocks, the fiber solver's spans) is replayed from CUDA graphs through `_cuda.graphs.GraphRunner`: warm once, capture on the second run, replay; a failed capture raises. Per-replay offsets come from a device index the graph advances, so the host launches one graph per block. Its stream is reused per device and thread, because CuPy's pool caches freed blocks per stream.
|
|
224
|
+
- **Audit with `tests/plugins/transfer_trace.py`** after touching a GPU path; the contract registry cannot see a silent round trip through the host. The plugin records every sample-rate host-device copy during a `--device=gpu` run and fails on a site missing from its documented list (the sequential recursions' round trip, equalizer continuation state, the reporting layer, explicit device moves, the transmitted payload, host values such as reference symbols or SciPy windows moved to the device). A new site is fixed or added to the list with its reason.
|
|
225
|
+
|
|
226
|
+
**Kernel policy:**
|
|
227
|
+
|
|
228
|
+
- A `.cu` kernel needs a benchmark showing that CuPy is limited by intermediates, launch count or round trips. It optimizes a CPU reference (Numba or NumPy) and is tested against it with `--device=all`.
|
|
229
|
+
- `_cuda.get_kernel` returns the launch wrapper or raises (compile, load, bad specialization); it is called only on CuPy input.
|
|
230
|
+
- Wrappers check dtype, contiguity, shape and size before launch and never launch a zero-size grid: empty input returns an empty result, or raises where the caller never has one (`cs_block`, `bps_anchor`). Grid dimensions y and z stop at 65535; a kernel mapping channels or rows there walks the rest in a grid-stride loop (`bps_min_d2`).
|
|
231
|
+
- **Dispatch on input limits:** a kernel's documented limits (dtype, number of points, phases, channels) may route to the general implementation where it exists anyway (the CPU's), saying so where it happens. A GPU-only path serving only inputs beyond a kernel's limit is not written; such input raises (`compute_llr` above 16 bits per symbol).
|
|
232
|
+
- `float32` with explicit literals. `--use_fast_math` only per kernel (through `options`), after an accuracy check.
|
|
233
|
+
- Short elementwise chains use `ElementwiseKernel` (named `commkit_*`) or `cupy.fuse`.
|
|
234
|
+
- Sequential recursions over a whole record are never ported to the GPU (IIR, above, is the linear exception). A short per-block scan may be, as one launch, when it replaces a host round trip per block (`cs_block`, which speculates its slip decisions in parallel; `bps_anchor`); keep its parallel part parallel, as float64 runs at 1/64 of float32 on consumer GPUs.
|
|
235
|
+
- A new or changed kernel passes `scripts/sanitize.sh` (memcheck, racecheck, synccheck, initcheck) before release; add its tests and kernel-name prefix to the script.
|
|
236
|
+
|
|
237
|
+
---
|
|
238
|
+
|
|
239
|
+
## 7. Tests
|
|
240
|
+
|
|
241
|
+
- **Fixtures:** use `backend_device`, `xp` and `xpt` from `tests/conftest.py`. Assert with `xpt.assert_allclose(...)` (`numpy.testing` raises on CuPy arrays); cast expectations with `xp.asarray` and reductions with `float(...)`.
|
|
242
|
+
- **No configuration skips.** The GPU legs skip only when CuPy is not installed, and fail instead under an explicit `--device=gpu`; a broken CuPy fails under any setting. Tests outside the selected run are deselected at collection (`gpu_only`, `cpu_only`, `slow` without `--run-slow`), so a run with CuPy reports no skips. A new test never calls `pytest.skip` for a configuration.
|
|
243
|
+
- **Layout mirrors the source:** `tests/<subpackage>/test_<module>.py`, `tests/test_<module>.py` for flat modules. Basenames are globally unique (prepend import mode). Split a large file by concern, each part named after its module (`impairments/test_channel_linear.py`, `test_channel_nonlinear.py`). Shared builders live in `tests/common/` and use the public API.
|
|
244
|
+
- **Contract registry** (`tests/test_api_contracts.py`): every public function has a row, checked generically for array and Signal in/out, device preserved, input not mutated, keyword-only parameters, `TypeError` on unsupported arrays, fact conflicts and return rules. Do not repeat these checks per module.
|
|
245
|
+
- **Oracles:** sequential kernels are tested against plain-Python references in `tests/common/reference_impl.py`, CUDA kernels against their CPU reference. Kernel tests skip only on the CPU parametrization (`skip_unless_gpu`), so a kernel that fails to compile fails its tests.
|
|
246
|
+
- **Mathematical meaning:** test documented definitions (a pulse width, a dispersion sign, noise-variance scaling) against independently derived expectations, not only shapes and energies.
|
|
247
|
+
- **A test of what an algorithm achieves** (an equalizer converges, carrier recovery tracks, a decoder corrects, an estimator is calibrated) must fail on a plausibly wrong implementation:
|
|
248
|
+
- **Score against the truth**: the transmitted reference or the true phase, delay or offset, never the receiver's own decisions or a sibling estimate; constellation symmetry hides a wrong quadrant, a slip or a duplicated stream. Helpers are in `tests/common/scoring.py` (`ser`, `blind_ser`, `phase_rms`, `block_rotations`, `stream_correlation`).
|
|
249
|
+
- **Resolve only the legitimate ambiguities**, each once over the whole scored interval, never per segment: one rotation per stream (by the constellation's symmetry, or any angle for a phase-blind equalizer), one bijective channel permutation for blind MIMO, a documented fixed delay.
|
|
250
|
+
- **A baseline must fail**: the same test without the block it credits (step size 0, `cpr=None`, the corrector off) asserts failure.
|
|
251
|
+
- **Tolerances are derived** (AWGN theory, a Cramér–Rao bound, a Wiener or loop-noise prediction, a binomial interval), and the docstring states the derivation and the measured value. `tests/common/theory.py` holds the closed forms, in NumPy and SciPy only.
|
|
252
|
+
- **Real channels**: convergence tests use `make_channel_signal` (RRC pulses, ISI, fractional delay, polarization rotation, DGD, AWGN). The repeat-upsampled builders serve plumbing tests only.
|
|
253
|
+
- **Break the code to check the test**: monkeypatch a plausible wrong result (zeros, a factor of two, the feature off) and confirm the test fails.
|
|
254
|
+
- A strengthened test that exposes a library fault is marked `pytest.mark.xfail(strict=True, reason=...)` with the measured numbers; the fix lands in its own commit, which removes the mark.
|
|
255
|
+
- **Monte Carlo tests** state what their sample bounds (0 failures in 12 frames bounds the FER below 0.22 at 95 %) and check the operating point (the pre-FEC BER against theory). The default run uses a smoke size; a `slow` parametrization repeats it with enough trials to mean something.
|
|
256
|
+
- **End-to-end chains** in `tests/test_receiver_chain.py` run several impairments and their receiver, scored against the source bits or AWGN theory, each with one correction left out as a failing baseline.
|
|
257
|
+
- **Warnings are failures**: a WARNING from the `commkit` logger fails the test unless it is marked `allow_warnings` (it provokes a suspect result on purpose) or asserts the log through `caplog`. A WARNING that reports what the caller asked for is a log-level bug, fixed in the library.
|
|
258
|
+
- **API changes never change expected values.** A signature change only changes how tests call the code; numerical changes land in their own commit with independent validation.
|
|
259
|
+
- **Examples** are Jupyter notebooks in `examples/`, committed without outputs (the `nbstripout` filter, see [Commands](#2-commands)). `tests/test_examples.py` executes each one, so an API change updates the notebooks in the same commit.
|
|
260
|
+
- They form a course: one topic per notebook, `NN-lowercase-words.ipynb`, in teaching order (signals and DSP, channel and metrics, coding, receiver algorithms, fiber systems, characterization). Each opens with what it covers and links the notebooks it builds on, and ends with a link to the next. A new notebook takes its place in the order and updates `examples/README.md` and its neighbors' links.
|
|
261
|
+
- Every claim in the text matches what the cells print: run the notebook and read its numbers before committing. Keep each under about 20 s on the CPU.
|
|
262
|
+
- Plots of hand-computed arrays go through `to_device(x, "cpu")`, so the notebooks run unchanged on the GPU.
|
|
263
|
+
- **Coverage** must not drop below the CI floors: 78 % for the package, 85 % for the Numba kernel modules (the `NUMBA_DISABLE_JIT=1` job, see [Commands](#2-commands)).
|
|
264
|
+
|
|
265
|
+
---
|
|
266
|
+
|
|
267
|
+
## 8. Benchmarks
|
|
268
|
+
|
|
269
|
+
`benchmarks/` tracks the GPU-relevant hot paths. Baselines are committed under `benchmarks/baselines/`. The reference is `0008` (`v3_1`) on the reference machine: RTX 4070 Ti, Ryzen 7 7800X3D, WSL2.
|
|
270
|
+
|
|
271
|
+
- **Workloads must converge**, or the benchmark times a failure mode (slip storms, divergence). Every equalizer benchmark asserts its symbol error rate with `benchutils.assert_converged`.
|
|
272
|
+
- **Workloads** come from `benchmarks/workloads.py` with fixed seeds; never generate data inline.
|
|
273
|
+
- **Baselines** are recorded with `benchmarks/record_baseline.py`: each file in its own process, best of `--passes` (default three; raise it when small benchmarks keep outliers). Never record from a single `pytest benchmarks/` run: small GPU benchmarks run after the large equalizer workloads are slowed by them.
|
|
274
|
+
- **IDs** are `[cpu]` or `[gpu]`, meaning the input device.
|
|
275
|
+
- **Timing:** timed bodies end with the `sync` fixture, or GPU timings measure launches. Each benchmark has one warmup round (compilation, pool growth), and a session fixture warms Numba's thread pool before the first.
|
|
276
|
+
- **Tools:** `benchmarks/benchutils.py` provides `CudaEventTimer` and `nvtx_range` for `nsys`. Logging is set to WARNING in `benchmarks/conftest.py`.
|
|
277
|
+
- **Trust deltas, not single runs.** Timings swing widely under load. Re-run a suspected regression in isolation, or do a paired A/B of at least 7 repetitions, alternating old and new. When even unchanged benchmarks move, time the changed part with `CudaEventTimer` and quote it against the step it sits in.
|
|
278
|
+
- A commit touching a hot path quotes its delta against `0008`. Tolerance is 5 % on CPU and 10 % on GPU.
|
|
279
|
+
|
|
280
|
+
---
|
|
281
|
+
|
|
282
|
+
## 9. Release
|
|
283
|
+
|
|
284
|
+
1. On `main`: the CPU and GPU suites pass with no failures or skips, slow validation included (`uv run pytest -rs --run-slow`; the only expected failures are strict xfails whose reason names a known fault); `scripts/sanitize.sh` is clean if a kernel changed; `uv build` succeeds and `uv run --with twine twine check dist/*` passes.
|
|
285
|
+
2. `CHANGELOG.md`: a `## [X.Y.Z] - YYYY-MM-DD` section written for users. Changes that may need code updates come first, with how to migrate, then Added, Performance (measured, as a ratio of new to old time), Fixed and Development. Add its compare link at the bottom and commit.
|
|
286
|
+
3. `uv run bump-my-version bump {patch|minor|major}` commits the new version and tags `vX.Y.Z`.
|
|
287
|
+
4. `git push origin main --tags`. The ruleset on `main` requires pull requests; repository admins bypass it for these two commits.
|
|
288
|
+
5. `gh release create vX.Y.Z --title vX.Y.Z --notes-file notes.md` with the changelog section as notes. Publishing the release runs `.github/workflows/publish.yml`, which uploads to PyPI through Trusted Publishing; a pushed tag alone publishes nothing.
|
|
@@ -4,6 +4,69 @@ Notable changes to CommKit, newest first. Performance figures are from the refer
|
|
|
4
4
|
|
|
5
5
|
## [Unreleased]
|
|
6
6
|
|
|
7
|
+
## [3.5.0] - 2026-11-05
|
|
8
|
+
|
|
9
|
+
One name per concept, finished. A review of the API and the module layout renamed the arguments and result fields that broke the library's naming rules, split or merged the modules that broke its size rules, and fixed the two audit tools that could pass without checking anything. Numerical results do not change.
|
|
10
|
+
|
|
11
|
+
### Changes that may need code updates
|
|
12
|
+
|
|
13
|
+
Every rename fails loudly with `TypeError` (or `AttributeError` for a field); none changes a result.
|
|
14
|
+
|
|
15
|
+
- `generate` takes `num_symbols` by keyword only, like every other parameter after the constellation: write `generate(c, num_symbols=N, ...)` instead of `generate(c, N, ...)`.
|
|
16
|
+
- `metrics.fer(info_bits=)` replaces `block_length=`, the code's information length as everywhere else in the coding API.
|
|
17
|
+
- Result fields of the laser analyses have descriptive names:
|
|
18
|
+
- `LinewidthEstimate`: `frequencies`, `fm_psd`, `fm_psd_model`, `increment_var`, `delta_phi_var` and `peak_frequency` replace `f`, `S_f`, `S_f_model`, `var`, `dphi_var` and `f_peak`;
|
|
19
|
+
- `DshFmNoisePsd`: `frequencies` and `fm_psd` replace `f` and `S_f`;
|
|
20
|
+
- `FrequencyDrift`: `peak_to_peak` replaces `pp`.
|
|
21
|
+
- `analysis.carrier_phase_trajectory(symbols, ...)` and `equalization.estimate_transfer_function(samples, ...)` rename their first argument (was `y_eq` and `received`); positional calls are unaffected.
|
|
22
|
+
- `recovery.PLL(proportional_gain=, integral_gain=)` replaces `mu=` and `beta=`, the raw loop gains. `normalized_bandwidth` is unchanged.
|
|
23
|
+
- `plotting.plot_error_rate(x_axis="ebn0")` replaces `ebn0=True`, as `plot_psd(x_axis=)` chooses its axis; another value raises `ValueError`.
|
|
24
|
+
- `plot_frequency_drift(reference_amplitude=)` and `plot_carrier_phase_characterization(reference_amplitude=)` replace `amp_ref=`.
|
|
25
|
+
- `plot_constellation` draws no grid.
|
|
26
|
+
- Modules moved; import from the packages as documented and nothing changes. `commkit.impairments.channel` is one module (it was the subpackage `channel.linear` / `channel.nonlinear`); `commkit.plotting.sync` is split into `plotting.timing`, `plotting.frequency` and `plotting.recovery`, and `plot_carrier_phase_decomposition` lives in `plotting.analysis`; `Preamble` is defined in `commkit.core.preamble`; `commkit.equalization.sequential` and `commkit.equalization.blind` are private (`_sequential`, `_block`).
|
|
27
|
+
|
|
28
|
+
### Fixed
|
|
29
|
+
|
|
30
|
+
- `ErrorRateCurve` and `ExitCurve` document their fields under Attributes, as every other result class does.
|
|
31
|
+
|
|
32
|
+
### Development
|
|
33
|
+
|
|
34
|
+
- The transfer audit (`tests/plugins/transfer_trace.py`) failed on 3.4.0 for three legitimate copies it did not list (`SingleCarrierFrame.to_signal`, `extract_payload`, `save_npz`); they are documented now. Both audit plugins found commkit by the path fragment `commkit/commkit/`, so in a checkout of another name (a git worktree) they recorded nothing and passed; they now match the installed package's directory.
|
|
35
|
+
- The Numba kernel getters of the equalizers sit in the modules that call them, as in `coding` and `mapping`, and `_kernels_numba.py` is gone.
|
|
36
|
+
- AGENTS.md holds only the rules for changing the code; it gains glossary entries for output scaling (`normalize`, `correct_power`, `*_normalization`) and search windows (`search_range`, `search_band`), and the rule that result classes document their fields under Attributes. The README carries the new logo.
|
|
37
|
+
|
|
38
|
+
## [3.4.0] - 2026-11-03
|
|
39
|
+
|
|
40
|
+
Error-rate curves for every code, decoders that say what they checked, and one noise convention for real and complex samples.
|
|
41
|
+
|
|
42
|
+
### Changes that may need code updates
|
|
43
|
+
|
|
44
|
+
- Real-valued samples (PAM, intensity modulation) now follow one noise convention, so their LLRs and information rates are right. Complex samples are unaffected.
|
|
45
|
+
- `apply_awgn(esn0_db=)` adds half the noise variance to real samples: the in-phase half of the channel's noise. A constellation's error rates at an Es/N0 no longer depend on whether its samples are real or complex, and BPSK has `BER = Q(sqrt(2 Es/N0))`. For a power ratio in the samples, as measurement code means it, use the new `apply_awgn(snr_db=)`; it adds what `esn0_db` used to add to real samples at one sample per symbol.
|
|
46
|
+
- `compute_llr`, `Constellation.llr`, `mi`, `gmi` and `ngmi` use the likelihood of real noise for real symbols. With the `noise_var` that `estimate_noise_variance` measures, real-symbol LLRs used to be twice too confident.
|
|
47
|
+
- `DecodeResult` has a new field, `checked`: whether `valid` comes from a check that can fail. Code that builds a `DecodeResult` itself must pass it. For decoders without a check, `valid` stays `True` everywhere, as before.
|
|
48
|
+
|
|
49
|
+
### Added
|
|
50
|
+
|
|
51
|
+
- `coding.error_rate_curve(code, constellation=, esn0_db=, ...)` measures a code's BER, FER and pre-FEC BER over AWGN by Monte Carlo. Each point stops after `min_block_errors` block errors or `max_blocks` blocks. It returns an `ErrorRateCurve` of counts, which you can also build from your own simulation loop.
|
|
52
|
+
- `plotting.plot_error_rate(curves)`: BER and FER against Es/N0 or Eb/N0, with 95 % intervals on the FER and the pre-FEC BER for reference, plus a panel splitting failures into detected and undetected ones.
|
|
53
|
+
- Soft output from more decoders: `TurboIterations` (a-posteriori LLRs of the last iteration), `MaximumLikelihood` (max-log) and `Concatenated` (the inner decoder's LLRs). `decoder_exit_curve` now covers turbo and maximum-likelihood decoding.
|
|
54
|
+
- `Chase(..., soft=False)` and `MaximumLikelihood(soft=False)` skip the soft output when only the decisions matter (`llrs` is `None`, the bits are the same).
|
|
55
|
+
- `compute_llr`, `mi`, `gmi` and `ngmi` take `esn0_db=` instead of `noise_var`, and convert it for real or complex symbols.
|
|
56
|
+
- `plot_llr_distribution(res, sig)` takes a `DecodeResult` and the transmitted Signal or Reference, as `ber` does.
|
|
57
|
+
- The example notebooks draw with the library's plot functions wherever one exists, and show spectra, constellations and eye diagrams before and after each block. Notebook 06 explains Es/N0, SNR and noise variance for real and complex samples; notebook 07 measures error-rate curves and the decoding outcome, compares the polar, convolutional and turbo codes against Eb/N0 and the turbo code at three block sizes; notebook 21 charts the optical standards' thresholds against their overhead.
|
|
58
|
+
|
|
59
|
+
### Performance
|
|
60
|
+
|
|
61
|
+
- `MaximumLikelihood` searches the codewords in one Numba pass on the CPU. For 16384 Hamming(16, 11) words (paired A/B against 3.3.0, 7 runs), decisions alone (`soft=False`) take 0.62x the time on the CPU and 1.0x on the GPU; with the new soft output, 1.55x and 4.3x.
|
|
62
|
+
- `Chase(soft=False)`: 0.44x the time of the default on the CPU and 0.74x on the GPU (400ZR Hamming(128, 119), 4 test positions, 4096 words).
|
|
63
|
+
- Turbo decoding is unchanged (1.00), now with its soft output.
|
|
64
|
+
|
|
65
|
+
### Development
|
|
66
|
+
|
|
67
|
+
- `metrics` is a package (`quality`, `error_rates`, `information`); every name is still imported from `commkit.metrics`.
|
|
68
|
+
- The transfer audit lists the payload moves of the EXIT and error-rate curves, which it flagged as undocumented; the kernel-coverage job runs the maximum-likelihood tests.
|
|
69
|
+
|
|
7
70
|
## [3.3.0] - 2026-11-03
|
|
8
71
|
|
|
9
72
|
Numerical filter responses and plots built for comparisons. `filter_response`, `impulse_response` and `step_response` evaluate any FIR, SOS or `(b, a)` filter on its device; the multi-panel diagnostics draw one selected panel, and every plot can overlay several results on one axis. The example notebooks explain what each result shows.
|
|
@@ -363,7 +426,9 @@ See the [GitHub release](https://github.com/lokgar/commkit/releases/tag/v1.1.0)
|
|
|
363
426
|
|
|
364
427
|
First release. See the [GitHub release](https://github.com/lokgar/commkit/releases/tag/v1.0.0).
|
|
365
428
|
|
|
366
|
-
[Unreleased]: https://github.com/lokgar/commkit/compare/v3.
|
|
429
|
+
[Unreleased]: https://github.com/lokgar/commkit/compare/v3.5.0...HEAD
|
|
430
|
+
[3.5.0]: https://github.com/lokgar/commkit/compare/v3.4.0...v3.5.0
|
|
431
|
+
[3.4.0]: https://github.com/lokgar/commkit/compare/v3.3.0...v3.4.0
|
|
367
432
|
[3.3.0]: https://github.com/lokgar/commkit/compare/v3.2.1...v3.3.0
|
|
368
433
|
[3.2.1]: https://github.com/lokgar/commkit/compare/v3.2.0...v3.2.1
|
|
369
434
|
[3.2.0]: https://github.com/lokgar/commkit/compare/v3.1.3...v3.2.0
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: commkit
|
|
3
|
-
Version: 3.
|
|
3
|
+
Version: 3.5.0
|
|
4
4
|
Summary: A Python library for high-performance digital communications research.
|
|
5
5
|
Project-URL: Homepage, https://github.com/lokgar/commkit
|
|
6
6
|
Project-URL: Repository, https://github.com/lokgar/commkit
|
|
@@ -34,18 +34,18 @@ Provides-Extra: notebook
|
|
|
34
34
|
Requires-Dist: jupyter>=1.1.1; extra == 'notebook'
|
|
35
35
|
Description-Content-Type: text/markdown
|
|
36
36
|
|
|
37
|
-
|
|
37
|
+
<p align="center">
|
|
38
|
+
<img src="https://raw.githubusercontent.com/lokgar/commkit/main/.github/logo.svg" alt="CommKit" width="520">
|
|
39
|
+
</p>
|
|
38
40
|
|
|
39
|
-
|
|
41
|
+
<p align="center">Digital communications research kit for Python with CUDA acceleration.</p>
|
|
40
42
|
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
---
|
|
43
|
+
<p align="center">
|
|
44
|
+
<img src="https://img.shields.io/badge/python-3.12%2B-blue" alt="Python 3.12+">
|
|
45
|
+
<img src="https://img.shields.io/badge/backend-NumPy%20%7C%20CuPy-orange" alt="Backends">
|
|
46
|
+
<img src="https://img.shields.io/badge/license-MIT-green" alt="License">
|
|
47
|
+
<img src="https://img.shields.io/badge/CUDA-13.x-76B900?logo=nvidia" alt="CUDA">
|
|
48
|
+
</p>
|
|
49
49
|
|
|
50
50
|
## The model
|
|
51
51
|
|
|
@@ -1,15 +1,15 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
1
|
+
<p align="center">
|
|
2
|
+
<img src="https://raw.githubusercontent.com/lokgar/commkit/main/.github/logo.svg" alt="CommKit" width="520">
|
|
3
|
+
</p>
|
|
4
|
+
|
|
5
|
+
<p align="center">Digital communications research kit for Python with CUDA acceleration.</p>
|
|
6
|
+
|
|
7
|
+
<p align="center">
|
|
8
|
+
<img src="https://img.shields.io/badge/python-3.12%2B-blue" alt="Python 3.12+">
|
|
9
|
+
<img src="https://img.shields.io/badge/backend-NumPy%20%7C%20CuPy-orange" alt="Backends">
|
|
10
|
+
<img src="https://img.shields.io/badge/license-MIT-green" alt="License">
|
|
11
|
+
<img src="https://img.shields.io/badge/CUDA-13.x-76B900?logo=nvidia" alt="CUDA">
|
|
12
|
+
</p>
|
|
13
13
|
|
|
14
14
|
## The model
|
|
15
15
|
|
|
@@ -126,7 +126,7 @@ def fiber_workload(n_samples: int, seed: int = 0) -> np.ndarray:
|
|
|
126
126
|
|
|
127
127
|
sig = generate(
|
|
128
128
|
Constellation.qam(16),
|
|
129
|
-
n_samples // 2,
|
|
129
|
+
num_symbols=n_samples // 2,
|
|
130
130
|
symbol_rate=32e9,
|
|
131
131
|
sps=2,
|
|
132
132
|
pulse=RRC(0.1),
|
|
@@ -162,7 +162,9 @@ def coded_workload(code, num_codewords: int, esn0_db: float, seed: int = 0):
|
|
|
162
162
|
from commkit.mapping import compute_llr
|
|
163
163
|
|
|
164
164
|
c = Constellation.qam(4)
|
|
165
|
-
sig = generate(
|
|
165
|
+
sig = generate(
|
|
166
|
+
c, num_symbols=num_codewords * code.n // 2, symbol_rate=FS, code=code, rng=seed
|
|
167
|
+
)
|
|
166
168
|
rx = apply_awgn(sig, esn0_db=esn0_db, rng=seed + 1)
|
|
167
169
|
llrs = compute_llr(rx, noise_var=10 ** (-esn0_db / 10))
|
|
168
170
|
return np.asarray(llrs, dtype=np.float32), sig.reference.info_bits
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
"""Synchronization sequences (private).
|
|
2
2
|
|
|
3
3
|
Barker and Zadoff-Chu generators and the per-stream ZC root assignment.
|
|
4
|
-
They live below ``core`` so that ``core.
|
|
4
|
+
They live below ``core`` so that ``core.preamble`` can build preambles without
|
|
5
5
|
importing a DSP module; ``commkit.timing`` exposes the generators publicly.
|
|
6
6
|
"""
|
|
7
7
|
|
|
@@ -25,14 +25,14 @@ class FrequencyDrift:
|
|
|
25
25
|
----------
|
|
26
26
|
df : ndarray
|
|
27
27
|
Per-sample residual frequency in Hz, on the input's device.
|
|
28
|
-
std,
|
|
28
|
+
std, peak_to_peak, max_abs : float or numpy.ndarray
|
|
29
29
|
Standard deviation, peak-to-peak and largest magnitude of ``df``:
|
|
30
30
|
floats (SISO) or ``(C,)`` arrays (MIMO).
|
|
31
31
|
"""
|
|
32
32
|
|
|
33
33
|
df: Any
|
|
34
34
|
std: float | np.ndarray
|
|
35
|
-
|
|
35
|
+
peak_to_peak: float | np.ndarray
|
|
36
36
|
max_abs: float | np.ndarray
|
|
37
37
|
|
|
38
38
|
|
|
@@ -187,6 +187,6 @@ def frequency_drift(
|
|
|
187
187
|
return FrequencyDrift(
|
|
188
188
|
df=df_out,
|
|
189
189
|
std=to_report_scalar(std, was_1d=was_1d),
|
|
190
|
-
|
|
190
|
+
peak_to_peak=to_report_scalar(pp, was_1d=was_1d),
|
|
191
191
|
max_abs=to_report_scalar(max_abs, was_1d=was_1d),
|
|
192
192
|
)
|