commkit 3.1.0__tar.gz → 3.1.1__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.1.0 → commkit-3.1.1}/AGENTS.md +9 -5
- {commkit-3.1.0 → commkit-3.1.1}/CHANGELOG.md +47 -0
- {commkit-3.1.0 → commkit-3.1.1}/PKG-INFO +6 -6
- {commkit-3.1.0 → commkit-3.1.1}/README.md +5 -5
- {commkit-3.1.0 → commkit-3.1.1}/benchmarks/bench_coding.py +6 -6
- {commkit-3.1.0 → commkit-3.1.1}/benchmarks/bench_fiber.py +1 -0
- {commkit-3.1.0 → commkit-3.1.1}/benchmarks/bench_signal_pipeline.py +1 -1
- {commkit-3.1.0 → commkit-3.1.1}/commkit/__init__.py +1 -1
- commkit-3.1.1/commkit/_constants.py +3 -0
- {commkit-3.1.0 → commkit-3.1.1}/commkit/_dispersion.py +1 -3
- {commkit-3.1.0 → commkit-3.1.1}/commkit/_sequences.py +34 -34
- {commkit-3.1.0 → commkit-3.1.1}/commkit/analysis/_common.py +2 -2
- {commkit-3.1.0 → commkit-3.1.1}/commkit/analysis/allan.py +6 -6
- {commkit-3.1.0 → commkit-3.1.1}/commkit/analysis/linewidth/dsh.py +4 -4
- {commkit-3.1.0 → commkit-3.1.1}/commkit/analysis/linewidth/phase.py +8 -6
- {commkit-3.1.0 → commkit-3.1.1}/commkit/analysis/linewidth/result.py +4 -4
- {commkit-3.1.0 → commkit-3.1.1}/commkit/coding/algebraic.py +9 -9
- {commkit-3.1.0 → commkit-3.1.1}/commkit/coding/convolutional.py +23 -16
- {commkit-3.1.0 → commkit-3.1.1}/commkit/coding/crc.py +16 -16
- {commkit-3.1.0 → commkit-3.1.1}/commkit/coding/ldpc.py +13 -13
- {commkit-3.1.0 → commkit-3.1.1}/commkit/coding/polar.py +6 -6
- {commkit-3.1.0 → commkit-3.1.1}/commkit/coding/product.py +33 -36
- {commkit-3.1.0 → commkit-3.1.1}/commkit/coding/shaping.py +26 -28
- {commkit-3.1.0 → commkit-3.1.1}/commkit/coding/turbo.py +31 -22
- {commkit-3.1.0 → commkit-3.1.1}/commkit/core/_signal_adapter.py +27 -0
- {commkit-3.1.0 → commkit-3.1.1}/commkit/core/frame.py +121 -73
- {commkit-3.1.0 → commkit-3.1.1}/commkit/core/generation.py +3 -3
- {commkit-3.1.0 → commkit-3.1.1}/commkit/core/signal.py +13 -5
- {commkit-3.1.0 → commkit-3.1.1}/commkit/equalization/__init__.py +11 -13
- {commkit-3.1.0 → commkit-3.1.1}/commkit/equalization/blind.py +19 -111
- {commkit-3.1.0 → commkit-3.1.1}/commkit/equalization/linear.py +46 -44
- commkit-3.1.1/commkit/equalization/pilots.py +115 -0
- {commkit-3.1.0 → commkit-3.1.1}/commkit/equalization/polarization.py +247 -381
- {commkit-3.1.0 → commkit-3.1.1}/commkit/equalization/sequential/_blind.py +38 -71
- {commkit-3.1.0 → commkit-3.1.1}/commkit/equalization/sequential/_dd.py +29 -25
- {commkit-3.1.0 → commkit-3.1.1}/commkit/filtering/__init__.py +2 -14
- {commkit-3.1.0 → commkit-3.1.1}/commkit/filtering/fiber.py +16 -9
- {commkit-3.1.0 → commkit-3.1.1}/commkit/filtering/linear.py +26 -24
- {commkit-3.1.0 → commkit-3.1.1}/commkit/filtering/pulses.py +40 -48
- {commkit-3.1.0 → commkit-3.1.1}/commkit/frequency.py +41 -1
- {commkit-3.1.0 → commkit-3.1.1}/commkit/impairments/__init__.py +13 -3
- {commkit-3.1.0 → commkit-3.1.1}/commkit/impairments/channel/__init__.py +3 -1
- {commkit-3.1.0 → commkit-3.1.1}/commkit/impairments/channel/linear.py +37 -23
- {commkit-3.1.0 → commkit-3.1.1}/commkit/impairments/channel/nonlinear.py +7 -3
- {commkit-3.1.0 → commkit-3.1.1}/commkit/impairments/frontend.py +150 -21
- commkit-3.1.1/commkit/impairments/noise.py +184 -0
- {commkit-3.1.0 → commkit-3.1.1}/commkit/mapping/__init__.py +3 -3
- {commkit-3.1.0 → commkit-3.1.1}/commkit/mapping/bits.py +5 -5
- {commkit-3.1.0 → commkit-3.1.1}/commkit/metrics.py +160 -37
- {commkit-3.1.0 → commkit-3.1.1}/commkit/plotting/analysis.py +1 -1
- {commkit-3.1.0 → commkit-3.1.1}/commkit/plotting/equalizer.py +3 -3
- {commkit-3.1.0 → commkit-3.1.1}/commkit/plotting/filter_response.py +6 -6
- {commkit-3.1.0 → commkit-3.1.1}/commkit/plotting/spectral.py +2 -2
- {commkit-3.1.0 → commkit-3.1.1}/commkit/plotting/sync.py +11 -11
- {commkit-3.1.0 → commkit-3.1.1}/commkit/recovery/carrier_phase.py +4 -3
- {commkit-3.1.0 → commkit-3.1.1}/commkit/recovery/pilots.py +32 -1
- {commkit-3.1.0 → commkit-3.1.1}/commkit/recovery/pll.py +15 -10
- {commkit-3.1.0 → commkit-3.1.1}/commkit/recovery/tikhonov.py +11 -10
- {commkit-3.1.0 → commkit-3.1.1}/commkit/spectral.py +1 -1
- {commkit-3.1.0 → commkit-3.1.1}/commkit/timing.py +8 -5
- {commkit-3.1.0 → commkit-3.1.1}/examples/01-constellations-and-bit-mapping.ipynb +2 -2
- {commkit-3.1.0 → commkit-3.1.1}/examples/03-digital-filters.ipynb +9 -7
- {commkit-3.1.0 → commkit-3.1.1}/examples/06-awgn-and-performance-metrics.ipynb +1 -1
- {commkit-3.1.0 → commkit-3.1.1}/examples/07-channel-coding.ipynb +2 -2
- {commkit-3.1.0 → commkit-3.1.1}/examples/08-probabilistic-shaping-and-coded-modulation.ipynb +2 -2
- {commkit-3.1.0 → commkit-3.1.1}/examples/10-front-end-impairments.ipynb +16 -10
- {commkit-3.1.0 → commkit-3.1.1}/examples/11-frame-synchronization-and-timing.ipynb +4 -1
- {commkit-3.1.0 → commkit-3.1.1}/examples/13-blind-carrier-phase-recovery.ipynb +3 -3
- {commkit-3.1.0 → commkit-3.1.1}/examples/14-pilot-aided-carrier-recovery.ipynb +2 -2
- {commkit-3.1.0 → commkit-3.1.1}/examples/15-linear-channel-equalization.ipynb +15 -15
- {commkit-3.1.0 → commkit-3.1.1}/examples/16-adaptive-equalization.ipynb +2 -2
- {commkit-3.1.0 → commkit-3.1.1}/examples/18-polarization-multiplexing.ipynb +5 -8
- {commkit-3.1.0 → commkit-3.1.1}/examples/20-nonlinear-fiber-and-backpropagation.ipynb +3 -1
- {commkit-3.1.0 → commkit-3.1.1}/examples/22-coherent-transceiver-end-to-end.ipynb +19 -10
- {commkit-3.1.0 → commkit-3.1.1}/examples/23-carrier-phase-characterization.ipynb +1 -1
- {commkit-3.1.0 → commkit-3.1.1}/examples/24-laser-linewidth-self-heterodyne.ipynb +4 -4
- {commkit-3.1.0 → commkit-3.1.1}/examples/25-laser-linewidth-short-delay-and-flicker.ipynb +2 -2
- {commkit-3.1.0 → commkit-3.1.1}/examples/26-laser-linewidth-self-homodyne-iq.ipynb +2 -2
- {commkit-3.1.0 → commkit-3.1.1}/examples/27-measurement-template-self-heterodyne.ipynb +1 -1
- {commkit-3.1.0 → commkit-3.1.1}/examples/28-measurement-template-self-homodyne-iq.ipynb +1 -1
- {commkit-3.1.0 → commkit-3.1.1}/pyproject.toml +2 -2
- {commkit-3.1.0 → commkit-3.1.1}/tests/analysis/test_allan.py +2 -2
- {commkit-3.1.0 → commkit-3.1.1}/tests/analysis/test_linewidth.py +4 -4
- {commkit-3.1.0 → commkit-3.1.1}/tests/coding/test_convolutional.py +7 -2
- {commkit-3.1.0 → commkit-3.1.1}/tests/coding/test_crc.py +1 -1
- {commkit-3.1.0 → commkit-3.1.1}/tests/coding/test_ldpc.py +1 -1
- {commkit-3.1.0 → commkit-3.1.1}/tests/coding/test_ldpc_kernel.py +3 -3
- {commkit-3.1.0 → commkit-3.1.1}/tests/coding/test_ldpc_tables.py +1 -1
- {commkit-3.1.0 → commkit-3.1.1}/tests/coding/test_polar.py +9 -9
- {commkit-3.1.0 → commkit-3.1.1}/tests/coding/test_product.py +3 -3
- {commkit-3.1.0 → commkit-3.1.1}/tests/coding/test_shaping.py +16 -16
- {commkit-3.1.0 → commkit-3.1.1}/tests/coding/test_turbo.py +5 -3
- {commkit-3.1.0 → commkit-3.1.1}/tests/common/reference_impl.py +13 -5
- {commkit-3.1.0 → commkit-3.1.1}/tests/core/test_frame.py +67 -33
- {commkit-3.1.0 → commkit-3.1.1}/tests/core/test_generation.py +2 -2
- {commkit-3.1.0 → commkit-3.1.1}/tests/core/test_signal.py +7 -7
- {commkit-3.1.0 → commkit-3.1.1}/tests/core/test_signal_mimo.py +13 -13
- {commkit-3.1.0 → commkit-3.1.1}/tests/equalization/test_blind.py +5 -9
- {commkit-3.1.0 → commkit-3.1.1}/tests/equalization/test_cpr.py +5 -5
- {commkit-3.1.0 → commkit-3.1.1}/tests/equalization/test_equalizer_oracles.py +4 -4
- {commkit-3.1.0 → commkit-3.1.1}/tests/equalization/test_linear.py +20 -18
- {commkit-3.1.0 → commkit-3.1.1}/tests/equalization/test_polarization.py +89 -110
- {commkit-3.1.0 → commkit-3.1.1}/tests/equalization/test_sequential.py +43 -63
- {commkit-3.1.0 → commkit-3.1.1}/tests/equalization/test_state.py +9 -7
- {commkit-3.1.0 → commkit-3.1.1}/tests/impairments/channel/test_channel_linear.py +56 -4
- {commkit-3.1.0 → commkit-3.1.1}/tests/impairments/channel/test_channel_nonlinear.py +20 -2
- {commkit-3.1.0 → commkit-3.1.1}/tests/impairments/test_frontend.py +76 -0
- {commkit-3.1.0 → commkit-3.1.1}/tests/impairments/test_noise.py +63 -0
- {commkit-3.1.0 → commkit-3.1.1}/tests/mapping/test_bits.py +10 -12
- {commkit-3.1.0 → commkit-3.1.1}/tests/mapping/test_llr.py +1 -1
- {commkit-3.1.0 → commkit-3.1.1}/tests/plotting/test_plot_analysis.py +1 -1
- {commkit-3.1.0 → commkit-3.1.1}/tests/plotting/test_plot_equalizer.py +2 -2
- {commkit-3.1.0 → commkit-3.1.1}/tests/plotting/test_plot_filter_response.py +3 -3
- {commkit-3.1.0 → commkit-3.1.1}/tests/plotting/test_plot_sync.py +4 -4
- {commkit-3.1.0 → commkit-3.1.1}/tests/recovery/test_corrections.py +3 -2
- {commkit-3.1.0 → commkit-3.1.1}/tests/recovery/test_joint.py +2 -3
- {commkit-3.1.0 → commkit-3.1.1}/tests/recovery/test_pilots.py +29 -4
- {commkit-3.1.0 → commkit-3.1.1}/tests/recovery/test_pll.py +7 -3
- {commkit-3.1.0 → commkit-3.1.1}/tests/recovery/test_tikhonov.py +36 -15
- {commkit-3.1.0 → commkit-3.1.1}/tests/recovery/test_viterbi_viterbi.py +6 -2
- {commkit-3.1.0 → commkit-3.1.1}/tests/test_api_contracts.py +18 -16
- {commkit-3.1.0 → commkit-3.1.1}/tests/test_dispersion.py +1 -1
- {commkit-3.1.0 → commkit-3.1.1}/tests/test_fiber.py +9 -0
- {commkit-3.1.0 → commkit-3.1.1}/tests/test_filtering.py +45 -33
- {commkit-3.1.0 → commkit-3.1.1}/tests/test_frequency.py +20 -0
- {commkit-3.1.0 → commkit-3.1.1}/tests/test_io.py +3 -3
- {commkit-3.1.0 → commkit-3.1.1}/tests/test_metrics.py +26 -0
- {commkit-3.1.0 → commkit-3.1.1}/tests/test_pulse_shaping.py +0 -30
- {commkit-3.1.0 → commkit-3.1.1}/tests/test_signal_pipeline.py +5 -5
- {commkit-3.1.0 → commkit-3.1.1}/tests/test_timing.py +7 -4
- {commkit-3.1.0 → commkit-3.1.1}/tests/test_working_memory.py +1 -1
- {commkit-3.1.0 → commkit-3.1.1}/uv.lock +1 -1
- commkit-3.1.0/commkit/impairments/noise.py +0 -108
- {commkit-3.1.0 → commkit-3.1.1}/.gitattributes +0 -0
- {commkit-3.1.0 → commkit-3.1.1}/.github/workflows/ci.yml +0 -0
- {commkit-3.1.0 → commkit-3.1.1}/.github/workflows/publish.yml +0 -0
- {commkit-3.1.0 → commkit-3.1.1}/.gitignore +0 -0
- {commkit-3.1.0 → commkit-3.1.1}/.python-version +0 -0
- {commkit-3.1.0 → commkit-3.1.1}/LICENSE +0 -0
- {commkit-3.1.0 → commkit-3.1.1}/benchmarks/baselines/Linux-CPython-3.14-64bit/0002_pre_v2.json +0 -0
- {commkit-3.1.0 → commkit-3.1.1}/benchmarks/baselines/Linux-CPython-3.14-64bit/0003_v2_0.json +0 -0
- {commkit-3.1.0 → commkit-3.1.1}/benchmarks/baselines/Linux-CPython-3.14-64bit/0004_fiber_dbp.json +0 -0
- {commkit-3.1.0 → commkit-3.1.1}/benchmarks/baselines/Linux-CPython-3.14-64bit/0005_gpu_decoder_path.json +0 -0
- {commkit-3.1.0 → commkit-3.1.1}/benchmarks/baselines/Linux-CPython-3.14-64bit/0006_perf_cleanup.json +0 -0
- {commkit-3.1.0 → commkit-3.1.1}/benchmarks/baselines/Linux-CPython-3.14-64bit/0007_v3_0.json +0 -0
- {commkit-3.1.0 → commkit-3.1.1}/benchmarks/baselines/Linux-CPython-3.14-64bit/0008_v3_1.json +0 -0
- {commkit-3.1.0 → commkit-3.1.1}/benchmarks/bench_block_blind.py +0 -0
- {commkit-3.1.0 → commkit-3.1.1}/benchmarks/bench_block_lms.py +0 -0
- {commkit-3.1.0 → commkit-3.1.1}/benchmarks/bench_bps.py +0 -0
- {commkit-3.1.0 → commkit-3.1.1}/benchmarks/bench_equalizers.py +0 -0
- {commkit-3.1.0 → commkit-3.1.1}/benchmarks/bench_filters.py +0 -0
- {commkit-3.1.0 → commkit-3.1.1}/benchmarks/bench_import.py +0 -0
- {commkit-3.1.0 → commkit-3.1.1}/benchmarks/bench_llr.py +0 -0
- {commkit-3.1.0 → commkit-3.1.1}/benchmarks/bench_mapping.py +0 -0
- {commkit-3.1.0 → commkit-3.1.1}/benchmarks/bench_sync_misc.py +0 -0
- {commkit-3.1.0 → commkit-3.1.1}/benchmarks/benchutils.py +0 -0
- {commkit-3.1.0 → commkit-3.1.1}/benchmarks/conftest.py +0 -0
- {commkit-3.1.0 → commkit-3.1.1}/benchmarks/record_baseline.py +0 -0
- {commkit-3.1.0 → commkit-3.1.1}/benchmarks/workloads.py +0 -0
- {commkit-3.1.0 → commkit-3.1.1}/commkit/_array.py +0 -0
- {commkit-3.1.0 → commkit-3.1.1}/commkit/_circular.py +0 -0
- {commkit-3.1.0 → commkit-3.1.1}/commkit/_cuda/__init__.py +0 -0
- {commkit-3.1.0 → commkit-3.1.1}/commkit/_cuda/compiler.py +0 -0
- {commkit-3.1.0 → commkit-3.1.1}/commkit/_cuda/graphs.py +0 -0
- {commkit-3.1.0 → commkit-3.1.1}/commkit/_cuda/src/algebraic.cu +0 -0
- {commkit-3.1.0 → commkit-3.1.1}/commkit/_cuda/src/bps_anchor.cu +0 -0
- {commkit-3.1.0 → commkit-3.1.1}/commkit/_cuda/src/bps_min_d2.cu +0 -0
- {commkit-3.1.0 → commkit-3.1.1}/commkit/_cuda/src/cs_block.cu +0 -0
- {commkit-3.1.0 → commkit-3.1.1}/commkit/_cuda/src/iir.cu +0 -0
- {commkit-3.1.0 → commkit-3.1.1}/commkit/_cuda/src/ldpc.cu +0 -0
- {commkit-3.1.0 → commkit-3.1.1}/commkit/_cuda/src/llr.cu +0 -0
- {commkit-3.1.0 → commkit-3.1.1}/commkit/_cuda/src/selftest.cu +0 -0
- {commkit-3.1.0 → commkit-3.1.1}/commkit/_fft.py +0 -0
- {commkit-3.1.0 → commkit-3.1.1}/commkit/_fiber.py +0 -0
- {commkit-3.1.0 → commkit-3.1.1}/commkit/_overlap_save.py +0 -0
- {commkit-3.1.0 → commkit-3.1.1}/commkit/_random.py +0 -0
- {commkit-3.1.0 → commkit-3.1.1}/commkit/analysis/__init__.py +0 -0
- {commkit-3.1.0 → commkit-3.1.1}/commkit/analysis/drift.py +0 -0
- {commkit-3.1.0 → commkit-3.1.1}/commkit/analysis/fm_noise.py +0 -0
- {commkit-3.1.0 → commkit-3.1.1}/commkit/analysis/interferometry.py +0 -0
- {commkit-3.1.0 → commkit-3.1.1}/commkit/analysis/linewidth/__init__.py +0 -0
- {commkit-3.1.0 → commkit-3.1.1}/commkit/analysis/trajectory.py +0 -0
- {commkit-3.1.0 → commkit-3.1.1}/commkit/backend.py +0 -0
- {commkit-3.1.0 → commkit-3.1.1}/commkit/coding/__init__.py +0 -0
- {commkit-3.1.0 → commkit-3.1.1}/commkit/coding/_galois.py +0 -0
- {commkit-3.1.0 → commkit-3.1.1}/commkit/coding/_gf2.py +0 -0
- {commkit-3.1.0 → commkit-3.1.1}/commkit/coding/_ldpc_tables.py +0 -0
- {commkit-3.1.0 → commkit-3.1.1}/commkit/coding/_numba_loops.py +0 -0
- {commkit-3.1.0 → commkit-3.1.1}/commkit/coding/base.py +0 -0
- {commkit-3.1.0 → commkit-3.1.1}/commkit/coding/interleaving.py +0 -0
- {commkit-3.1.0 → commkit-3.1.1}/commkit/coding/linear.py +0 -0
- {commkit-3.1.0 → commkit-3.1.1}/commkit/coding/ratematch.py +0 -0
- {commkit-3.1.0 → commkit-3.1.1}/commkit/core/__init__.py +0 -0
- {commkit-3.1.0 → commkit-3.1.1}/commkit/equalization/_block/__init__.py +0 -0
- {commkit-3.1.0 → commkit-3.1.1}/commkit/equalization/_block/_blind.py +0 -0
- {commkit-3.1.0 → commkit-3.1.1}/commkit/equalization/_block/_dd.py +0 -0
- {commkit-3.1.0 → commkit-3.1.1}/commkit/equalization/_block/_engine.py +0 -0
- {commkit-3.1.0 → commkit-3.1.1}/commkit/equalization/_common.py +0 -0
- {commkit-3.1.0 → commkit-3.1.1}/commkit/equalization/_kernels_numba.py +0 -0
- {commkit-3.1.0 → commkit-3.1.1}/commkit/equalization/_numba_loops.py +0 -0
- {commkit-3.1.0 → commkit-3.1.1}/commkit/equalization/result.py +0 -0
- {commkit-3.1.0 → commkit-3.1.1}/commkit/equalization/sequential/__init__.py +0 -0
- {commkit-3.1.0 → commkit-3.1.1}/commkit/equalization/sequential/_setup.py +0 -0
- {commkit-3.1.0 → commkit-3.1.1}/commkit/impairments/source.py +0 -0
- {commkit-3.1.0 → commkit-3.1.1}/commkit/io.py +0 -0
- {commkit-3.1.0 → commkit-3.1.1}/commkit/logger.py +0 -0
- {commkit-3.1.0 → commkit-3.1.1}/commkit/mapping/_numba_llr.py +0 -0
- {commkit-3.1.0 → commkit-3.1.1}/commkit/mapping/constellation.py +0 -0
- {commkit-3.1.0 → commkit-3.1.1}/commkit/mapping/gray.py +0 -0
- {commkit-3.1.0 → commkit-3.1.1}/commkit/mapping/llr.py +0 -0
- {commkit-3.1.0 → commkit-3.1.1}/commkit/mapping/shaping.py +0 -0
- {commkit-3.1.0 → commkit-3.1.1}/commkit/math.py +0 -0
- {commkit-3.1.0 → commkit-3.1.1}/commkit/multirate.py +0 -0
- {commkit-3.1.0 → commkit-3.1.1}/commkit/plotting/__init__.py +0 -0
- {commkit-3.1.0 → commkit-3.1.1}/commkit/plotting/constellation.py +0 -0
- {commkit-3.1.0 → commkit-3.1.1}/commkit/plotting/eye.py +0 -0
- {commkit-3.1.0 → commkit-3.1.1}/commkit/plotting/theme.py +0 -0
- {commkit-3.1.0 → commkit-3.1.1}/commkit/plotting/waveform.py +0 -0
- {commkit-3.1.0 → commkit-3.1.1}/commkit/py.typed +0 -0
- {commkit-3.1.0 → commkit-3.1.1}/commkit/recovery/__init__.py +0 -0
- {commkit-3.1.0 → commkit-3.1.1}/commkit/recovery/_common.py +0 -0
- {commkit-3.1.0 → commkit-3.1.1}/commkit/recovery/bps.py +0 -0
- {commkit-3.1.0 → commkit-3.1.1}/commkit/recovery/corrections.py +0 -0
- {commkit-3.1.0 → commkit-3.1.1}/commkit/recovery/viterbi_viterbi.py +0 -0
- {commkit-3.1.0 → commkit-3.1.1}/commkit/smoothing.py +0 -0
- {commkit-3.1.0 → commkit-3.1.1}/examples/00-getting-started.ipynb +0 -0
- {commkit-3.1.0 → commkit-3.1.1}/examples/02-spectral-analysis.ipynb +0 -0
- {commkit-3.1.0 → commkit-3.1.1}/examples/04-pulse-shaping-and-matched-filtering.ipynb +0 -0
- {commkit-3.1.0 → commkit-3.1.1}/examples/05-multirate-and-resampling.ipynb +0 -0
- {commkit-3.1.0 → commkit-3.1.1}/examples/09-laser-phase-noise.ipynb +0 -0
- {commkit-3.1.0 → commkit-3.1.1}/examples/12-frequency-offset-estimation.ipynb +0 -0
- {commkit-3.1.0 → commkit-3.1.1}/examples/17-blind-equalization.ipynb +0 -0
- {commkit-3.1.0 → commkit-3.1.1}/examples/19-chromatic-dispersion.ipynb +0 -0
- {commkit-3.1.0 → commkit-3.1.1}/examples/21-fec-in-optical-standards.ipynb +0 -0
- {commkit-3.1.0 → commkit-3.1.1}/examples/README.md +0 -0
- {commkit-3.1.0 → commkit-3.1.1}/tests/analysis/test_drift.py +0 -0
- {commkit-3.1.0 → commkit-3.1.1}/tests/analysis/test_fm_noise.py +0 -0
- {commkit-3.1.0 → commkit-3.1.1}/tests/analysis/test_interferometry.py +0 -0
- {commkit-3.1.0 → commkit-3.1.1}/tests/analysis/test_trajectory.py +0 -0
- {commkit-3.1.0 → commkit-3.1.1}/tests/coding/test_algebraic.py +0 -0
- {commkit-3.1.0 → commkit-3.1.1}/tests/coding/test_coding_base.py +0 -0
- {commkit-3.1.0 → commkit-3.1.1}/tests/coding/test_gf2.py +0 -0
- {commkit-3.1.0 → commkit-3.1.1}/tests/coding/test_interleaving.py +0 -0
- {commkit-3.1.0 → commkit-3.1.1}/tests/coding/test_linear_codes.py +0 -0
- {commkit-3.1.0 → commkit-3.1.1}/tests/coding/test_ratematch.py +0 -0
- {commkit-3.1.0 → commkit-3.1.1}/tests/common/__init__.py +0 -0
- {commkit-3.1.0 → commkit-3.1.1}/tests/common/coding.py +0 -0
- {commkit-3.1.0 → commkit-3.1.1}/tests/common/conversions.py +0 -0
- {commkit-3.1.0 → commkit-3.1.1}/tests/common/kernel_utils.py +0 -0
- {commkit-3.1.0 → commkit-3.1.1}/tests/common/memory.py +0 -0
- {commkit-3.1.0 → commkit-3.1.1}/tests/common/metrics.py +0 -0
- {commkit-3.1.0 → commkit-3.1.1}/tests/common/periodic.py +0 -0
- {commkit-3.1.0 → commkit-3.1.1}/tests/common/signals.py +0 -0
- {commkit-3.1.0 → commkit-3.1.1}/tests/conftest.py +0 -0
- {commkit-3.1.0 → commkit-3.1.1}/tests/core/test_psqam.py +0 -0
- {commkit-3.1.0 → commkit-3.1.1}/tests/core/test_signal_adapter.py +0 -0
- {commkit-3.1.0 → commkit-3.1.1}/tests/equalization/test_block.py +0 -0
- {commkit-3.1.0 → commkit-3.1.1}/tests/equalization/test_bps_kernel.py +0 -0
- {commkit-3.1.0 → commkit-3.1.1}/tests/equalization/test_cs_kernel.py +0 -0
- {commkit-3.1.0 → commkit-3.1.1}/tests/equalization/test_mimo.py +0 -0
- {commkit-3.1.0 → commkit-3.1.1}/tests/equalization/test_winit.py +0 -0
- {commkit-3.1.0 → commkit-3.1.1}/tests/impairments/test_source.py +0 -0
- {commkit-3.1.0 → commkit-3.1.1}/tests/mapping/test_constellation.py +0 -0
- {commkit-3.1.0 → commkit-3.1.1}/tests/mapping/test_gray.py +0 -0
- {commkit-3.1.0 → commkit-3.1.1}/tests/plotting/test_plot_constellation.py +0 -0
- {commkit-3.1.0 → commkit-3.1.1}/tests/plotting/test_plot_eye.py +0 -0
- {commkit-3.1.0 → commkit-3.1.1}/tests/plotting/test_plot_spectral.py +0 -0
- {commkit-3.1.0 → commkit-3.1.1}/tests/plotting/test_plot_theme.py +0 -0
- {commkit-3.1.0 → commkit-3.1.1}/tests/plotting/test_plot_waveform.py +0 -0
- {commkit-3.1.0 → commkit-3.1.1}/tests/plugins/__init__.py +0 -0
- {commkit-3.1.0 → commkit-3.1.1}/tests/plugins/precision_trace.py +0 -0
- {commkit-3.1.0 → commkit-3.1.1}/tests/plugins/transfer_trace.py +0 -0
- {commkit-3.1.0 → commkit-3.1.1}/tests/recovery/test_bps.py +0 -0
- {commkit-3.1.0 → commkit-3.1.1}/tests/recovery/test_recovery_oracles.py +0 -0
- {commkit-3.1.0 → commkit-3.1.1}/tests/test_array.py +0 -0
- {commkit-3.1.0 → commkit-3.1.1}/tests/test_backend.py +0 -0
- {commkit-3.1.0 → commkit-3.1.1}/tests/test_cuda_infra.py +0 -0
- {commkit-3.1.0 → commkit-3.1.1}/tests/test_examples.py +0 -0
- {commkit-3.1.0 → commkit-3.1.1}/tests/test_import_hygiene.py +0 -0
- {commkit-3.1.0 → commkit-3.1.1}/tests/test_logger.py +0 -0
- {commkit-3.1.0 → commkit-3.1.1}/tests/test_math.py +0 -0
- {commkit-3.1.0 → commkit-3.1.1}/tests/test_multirate.py +0 -0
- {commkit-3.1.0 → commkit-3.1.1}/tests/test_smoothing.py +0 -0
- {commkit-3.1.0 → commkit-3.1.1}/tests/test_spectral.py +0 -0
|
@@ -138,6 +138,7 @@ Dependencies point downward only:
|
|
|
138
138
|
- `analysis` holds laser and phase characterization.
|
|
139
139
|
- `metrics` is the reporting layer.
|
|
140
140
|
- **Splitting modules:** promote a flat module to a package only when it exceeds about 1000 lines *and* has two or more separable concerns. The package `__init__` re-exports the public names. Never create a `utils/` package or one-function files.
|
|
141
|
+
- **One documented home per public name.** A package re-exports its subpackages' names, and users and documentation import them from the package (`commkit.impairments.Fiber`, `commkit.impairments.apply_pmd`), not from the subpackage that defines them.
|
|
141
142
|
- **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`).
|
|
142
143
|
|
|
143
144
|
---
|
|
@@ -146,7 +147,7 @@ Dependencies point downward only:
|
|
|
146
147
|
|
|
147
148
|
### Signal
|
|
148
149
|
|
|
149
|
-
- `Signal` holds: samples (NumPy or CuPy, `(N,)` or `(C, N)`, time on the last axis), the **facts** `sampling_rate`, `symbol_rate`, `center_frequency` and `domain` (`"DIG"`, `"RF"` or `"OPT"`; only RF and optical signals have a carrier), the **description** `constellation` and `pulse`, and the ground truth `reference` (transmitted symbols, the bits on the air `bits` and the source bits `info_bits`, the same array when nothing is coded or matched, and the `code` between them) plus the `frame` layout.
|
|
150
|
+
- `Signal` holds: samples (NumPy or CuPy, `(N,)` or `(C, N)`, time on the last axis), the **facts** `sampling_rate`, `symbol_rate`, `center_frequency` and `domain` (`"DIG"`, `"RF"` or `"OPT"`; only RF and optical signals have a carrier; an optical carrier also gives the derived `center_wavelength_nm`, which the dispersion, fiber and OSNR functions take as a fact), the **description** `constellation` and `pulse`, and the ground truth `reference` (transmitted symbols, the bits on the air `bits` and the source bits `info_bits`, the same array when nothing is coded or matched, and the `code` between them) plus the `frame` layout.
|
|
150
151
|
- It never holds pipeline results.
|
|
151
152
|
- Construction does no hidden work: no mapping, no normalization, no device move, no shape guessing. Factories (`generate`, frames) do that work.
|
|
152
153
|
- `sig.to("gpu")` and `sig.replace(...)` return new Signals. Arrays are shared, not copied; use `clone()` for an independent copy.
|
|
@@ -154,7 +155,7 @@ Dependencies point downward only:
|
|
|
154
155
|
|
|
155
156
|
### Function signatures
|
|
156
157
|
|
|
157
|
-
- `f(data, other_data, *, params)`: everything after the data arguments is keyword-only. A function without data arguments may keep its single primary parameter positional (`barker_sequence(13)`, `gray_code(5)`); with several parameters, all are keyword-only (`
|
|
158
|
+
- `f(data, other_data, *, params)`: everything after the data arguments is keyword-only. A function without data arguments may keep its single primary parameter positional (`barker_sequence(13)`, `gray_code(5)`); with several parameters, all are keyword-only (`fir_taps(sampling_rate=..., num_taps=..., cutoff=...)`). The data-argument count is recorded per function in the contract registry.
|
|
158
159
|
- **Signal awareness.** A function whose main argument is waveform data (or a field a Signal really carries) accepts an array or a Signal. It unwraps once with `adapt_signal()` (in `core/_signal_adapter.py`), works on arrays, and returns through `wrap_samples()`. Never re-enter the public function recursively.
|
|
159
160
|
- Functions on *derived* quantities (phase trajectories, correlations, taps, PSD bins, results) and synthesis primitives stay array-only.
|
|
160
161
|
- **Metadata rule: facts versus choices.**
|
|
@@ -176,7 +177,7 @@ Dependencies point downward only:
|
|
|
176
177
|
- **Device follows the data.** There are no `backend=` arguments and no global switches. Unsupported array types (JAX, PyTorch) raise `TypeError`.
|
|
177
178
|
- Only factories with no input data to follow (`generate`, `generate_phase_noise`, `Preamble`/`SingleCarrierFrame.to_signal`, `load_npz`) take `device: str = "cpu"`, validated with `require_device()`. They draw randomness as the rule above says and do the vectorized work (mapping, shaping) on `device`. The default is never "GPU if available".
|
|
178
179
|
- **No `debug_plot`.** Numerical code never imports plotting. Plot functions consume results or recompute through public compute functions.
|
|
179
|
-
- **Design and apply are separate functions**, for example `
|
|
180
|
+
- **Design and apply are separate functions**, for example `RRC(...).taps(sps)` and `fir_filter`, or `butterworth_sos` and `iir_filter`.
|
|
180
181
|
|
|
181
182
|
### Returns
|
|
182
183
|
|
|
@@ -202,8 +203,11 @@ Dependencies point downward only:
|
|
|
202
203
|
- 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_*`.
|
|
203
204
|
- Computations use plain nouns (`allan_deviation`).
|
|
204
205
|
- **Glossary**, one name each:
|
|
205
|
-
- `num_taps`, `step_size`, `sps`, `constellation`, `pulse`, `rng`, `num_symbols`, `
|
|
206
|
-
-
|
|
206
|
+
- `num_taps`, `step_size`, `sps`, `constellation`, `pulse`, `rng`, `num_symbols`, `num_channels` (never streams), `sampling_rate`, `symbol_rate`, `noise_var`, `nfft`, `regularization`, `max_iterations`.
|
|
207
|
+
- A size names its unit: `num_symbols`, `num_blocks` (code 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.
|
|
208
|
+
- A seeded part of a factory takes `<part>_rng` (`payload_rng`), with the same int / Generator / None semantics as `rng`.
|
|
209
|
+
- A variant of one algorithm is a `method=` string only where it is a numerical flavor, not an algorithm (`method="maxlog" | "exact"`); algorithms are objects.
|
|
210
|
+
- Values are in SI units without a suffix: Hz, s, rad, rad/s, W (`linewidth` and `reference_bandwidth` in Hz, `drift_rate` in rad/s, `taus` in s). A result that holds one quantity in two units suffixes the SI one (`lags` in samples, `lag_s` in seconds). A conventional non-SI unit goes into the name: `_db`, `_dbm`, `_deg`, `_nm`, `_km`, `_ps_nm_km`. Normalized quantities say so (`normalized_bandwidth`). Do not add second names for one quantity in another unit (no `center_frequency_ghz` next to `center_frequency`); `center_wavelength_nm` is a different quantity, derived from the carrier.
|
|
207
211
|
|
|
208
212
|
### Docstrings
|
|
209
213
|
|
|
@@ -4,6 +4,53 @@ Notable changes to CommKit, newest first. Performance figures are from the refer
|
|
|
4
4
|
|
|
5
5
|
## [Unreleased]
|
|
6
6
|
|
|
7
|
+
## [3.1.1] - 2026-10-29
|
|
8
|
+
|
|
9
|
+
API consistency. One name per concept and a unit in every size, defaults that model the physics, physical units where the rest of the API has them, and features that use what the Signal and the frame already carry: the optical carrier for dispersion, fiber and OSNR noise, the frame's pilots for the pilot-aided algorithms, the code of the reference for post-FEC metrics. Most changes are renames that fail loudly with `TypeError`; the four default changes and the speed of light alter numbers and are listed first.
|
|
10
|
+
|
|
11
|
+
### Changes that alter results
|
|
12
|
+
|
|
13
|
+
- **`iir_filter` is causal by default** (`zero_phase=False`): a DAC, driver or photodiode bandwidth has group delay and an unsquared magnitude response. The old default filtered forward and backward, squaring the response. Pass `zero_phase=True` for offline zero-phase filtering.
|
|
14
|
+
- **`correct_timing` zero-fills by default** (`mode="zero"`); `mode="circular"` wrapped the end of a capture onto its start, right only for a periodic record.
|
|
15
|
+
- **`SingleCarrierFrame` draws a fresh payload by default** (`payload_rng=None`), so a Monte Carlo loop over frames no longer repeats one payload. Pass `payload_rng=<int>` for a fixed payload; the frame keeps the seed it used, so copies, `replace()` and saved frames rebuild the same one. The pilots keep their fixed `pilot_seed`.
|
|
16
|
+
- **`SingleCarrierFrame.to_signal` and `Preamble.to_signal` require `symbol_rate`** and default to `sps=1`, as `generate` does; they used to default to 1 MBd at 4 samples per symbol.
|
|
17
|
+
- **One exact speed of light** (299 792 458 m/s): dispersion used 2.998e8, which moves chromatic-dispersion results by 7e-5 relative.
|
|
18
|
+
|
|
19
|
+
### Changes that may need code updates
|
|
20
|
+
|
|
21
|
+
- **Code sizes name their unit**: `CRC`, `ConvolutionalCode` and `TurboCode` (and `CRC.nr`, `CRC.ethernet`, `ConvolutionalCode.k7`, `TurboCode.lte`) take `info_bits` instead of `block_length`; `CCDM` takes `num_symbols`; `StaircaseCode`, `StaircaseCode.g709_2` and `Concatenated.cfec` take `blocks_per_frame` instead of `num_blocks` (which `generate` uses for codewords); `LDPCCode`'s mother length is `num_variables`; `PolarCode.nr(k=, n=)` is keyword-only. The Reed-Solomon `length` property (in code symbols) is gone: read `symbols` or `n`.
|
|
22
|
+
- **`num_streams` is `num_channels`** in `SingleCarrierFrame`, `Preamble` and `Signal`.
|
|
23
|
+
- **One name per concept**:
|
|
24
|
+
- `noise_var` in `zf_equalizer` and `plot_zf_equalizer_response` (was `noise_variance`); `LinewidthEstimate.awgn_var`, a phase term in rad², is `angle_noise_var`.
|
|
25
|
+
- `nfft` in `estimate_transfer_function` (was `n_fft`) and `ols_fir_filter` (was `fft_size`).
|
|
26
|
+
- `regularization` in `estimate_transfer_function` (was `reg`) and `rls` (was `delta`).
|
|
27
|
+
- `zf_equalizer(center_tap=)` (was `center`); `ols_fir_filter(causal=False)` replaces `center=True`.
|
|
28
|
+
- `num_train_symbols` and `num_impulse_samples` in the equalizer plots (were `n_train`, `n_impulse`).
|
|
29
|
+
- `HardIterations(max_iterations=)` (was `iterations`, though it stops early); `BCJR` and `TurboIterations` take `method="maxlog" | "exact"` like `compute_llr` (was `exact=False | True`).
|
|
30
|
+
- `barker_sequence` and `zadoff_chu_sequence` take `num_symbols` like `Preamble` (was `length`).
|
|
31
|
+
- `AllanDeviation.taus` matches `allan_deviation(taus=)` (was `tau_s`); `LinewidthEstimate.num_segments` (was `n_segments`).
|
|
32
|
+
- **`estimate_transfer_function(received, reference)`**: the data under test first, as everywhere else (was `reference, received`).
|
|
33
|
+
- **Physical units**: `apply_polarization_mixing(drift_rate=)` in rad/s with the sampling rate from the Signal (was `drift_rad_per_sample`); `Tikhonov(linewidth=)` in Hz like `apply_phase_noise`, the symbol period taken from its one-sample-per-symbol input (was `linewidth_symbol_periods`); `PLL(normalized_bandwidth=)` (was `bandwidth`), so a bandwidth in Hz cannot be passed by mistake.
|
|
34
|
+
- **One entry point per concept**: the pulse objects (`RRC`, `RC`, `Gaussian`, `Rect`, `SmoothRect`) are the only way to build pulse taps; `rrc_taps`, `rc_taps`, `gaussian_taps`, `rect_taps` and `smoothrect_taps` are private, as their defaults disagreed with the objects'. `demap_symbols_hard` is `demap_symbols`, the twin of `Constellation.demap`.
|
|
35
|
+
- **Polarization demultiplexing on pilot tones is an estimate / correct pair**: `estimate_polarization_tones(samples, tone_frequencies=, track_bandwidth=)` returns a `JonesTrack` (one unmixing matrix by default, or matrices tracked over time), and `correct_polarization(samples, track)` applies it. This replaces `demultiplex_polarization_tones_static` / `_dynamic`, whose return type changed with `return_matrix` and `apply`, and the public `apply_interpolated_matrix`.
|
|
36
|
+
- **The optical functions read the carrier**: `apply_chromatic_dispersion`, `correct_chromatic_dispersion`, `apply_fiber_propagation` and `correct_fiber_propagation` take `center_wavelength_nm` from an optical Signal's `center_frequency`; an explicit value must agree. Arrays and other Signals must pass it: the fiber functions no longer assume 1550 nm silently.
|
|
37
|
+
- **Pilots for the blind equalizers are one object**: `EqualizerPilots(values, mask, gain_db)` replaces the `pilot_ref`, `pilot_mask` and `pilot_gain_db` arguments of `cma`, `rde`, `block_cma` and `block_rde` (now `pilots=`) and the `build_pilot_ref` helper.
|
|
38
|
+
|
|
39
|
+
### Added
|
|
40
|
+
|
|
41
|
+
- **`Signal.center_wavelength_nm`**, the wavelength of an optical Signal's carrier.
|
|
42
|
+
- **`apply_awgn(osnr_db=, reference_bandwidth=12.5e9)`** adds ASE to an optical Signal at an OSNR, counted over both polarizations in the reference bandwidth, so `Es/N0 = 2 B_ref / (p R_s) · OSNR` whatever the oversampling.
|
|
43
|
+
- **`EqualizerPilots.from_frame`, `PilotAided.from_frame` and `PilotSymbols.from_frame`** take the pilot positions, symbols and gain from a `SingleCarrierFrame`.
|
|
44
|
+
- **Post-FEC metrics from a `DecodeResult`**: `ber(decoded, sig)` and `fer(decoded, sig)` compare with the reference's `info_bits`, and `fer` takes the block length from the code.
|
|
45
|
+
- **`estimate_noise_variance`**, the data-aided noise variance that `compute_llr`, `gmi` and `mi` take, without the round trip through `snr`.
|
|
46
|
+
- **`estimate_iq_imbalance`** measures the amplitude (dB) and phase (degrees) of an IQ imbalance blindly, per channel, as an `IQImbalanceEstimate`; `correct_iq_imbalance` also takes the estimate and inverts the model exactly.
|
|
47
|
+
- `SingleCarrierFrame.pilot_mask` and `payload_mask`, the symbol masks over the whole frame.
|
|
48
|
+
|
|
49
|
+
### Fixed
|
|
50
|
+
|
|
51
|
+
- `apply_awgn` rejects a non-positive `reference_bandwidth`.
|
|
52
|
+
- `zadoff_chu_sequence`'s length error names the argument.
|
|
53
|
+
|
|
7
54
|
## [3.1.0] - 2026-10-29
|
|
8
55
|
|
|
9
56
|
Review fixes and the completion of the coding release. Coded records are sized in codewords (`generate(num_blocks=)`, `SingleCarrierFrame(payload_blocks=)`) or padded to any length, the reference carries its code, `save_npz` stores everything a Signal holds (codes included), and `Signal.domain` is back. Reed-Solomon and BCH decoding run in per-codeword kernels (KP4 23 times faster on the GPU, 16 on the CPU), and the remaining decoders, LLRs and several DSP paths got faster. Correctness fixes cover bit validation in `encode`, staircase and CCDM validity, complex FIR taps, ZF precision and timing correction. Frame lengths in symbols are renamed `*_num_symbols`; see the first section.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: commkit
|
|
3
|
-
Version: 3.1.
|
|
3
|
+
Version: 3.1.1
|
|
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
|
|
@@ -52,7 +52,7 @@ CommKit covers the receiver chain of coherent and IM/DD links (waveform generati
|
|
|
52
52
|
Data is a NumPy or CuPy array, or a `Signal` that wraps the samples together with what is known about them. Algorithms are plain functions that take the data and return a new `Signal` or array, or a typed result such as `EqualizerResult` or an `*Estimate`. Plotting functions draw those results; they never compute them.
|
|
53
53
|
|
|
54
54
|
- **Things that describe** a modulation, a pulse or a sub-algorithm are small frozen objects written in the call: `lms(rx, cpr=BPS(test_phases=64))`. The same object works standalone: `correct_carrier_phase(y, BPS())`.
|
|
55
|
-
- **A `Signal` carries facts and ground truth.** Facts are `sampling_rate`, `symbol_rate` and `
|
|
55
|
+
- **A `Signal` carries facts and ground truth.** Facts are `sampling_rate`, `symbol_rate`, `center_frequency` and `domain`; the carrier of an optical Signal gives its `center_wavelength_nm`, which dispersion, fiber propagation and OSNR noise read. The description is `constellation` and `pulse`. The ground truth is `reference`: the transmitted symbols, the bits on the air and the source bits before coding or distribution matching (the same array when there is neither). Functions take facts from the Signal and raise on a conflicting argument. Choices such as the decision constellation default to the Signal's and can be overridden.
|
|
56
56
|
- **Results with several values are frozen dataclasses** with named fields. Metrics return host floats: one per channel for `(C, N)` input.
|
|
57
57
|
- **The device follows the data.** Processing functions have no `backend=` or `device=` argument. Only factories, which have no input data to follow, take `device=`: `generate(..., device="gpu")`. Move existing data with `sig.to("gpu")`.
|
|
58
58
|
- **Plots draw results.** `plot_*` functions take a Signal, an array or a result object; numerical code never imports plotting.
|
|
@@ -114,17 +114,17 @@ CommKit never picks a device for you: data stays where you put it, and each func
|
|
|
114
114
|
|
|
115
115
|
| Module | Contents |
|
|
116
116
|
| --- | --- |
|
|
117
|
-
| [`commkit.core`](https://github.com/lokgar/commkit/tree/main/commkit/core) | `Signal`, `Reference`, `generate` (uniform, shaped through a distribution matcher, or coded with `code=`), `SingleCarrierFrame` and `Preamble` (pilots, guard intervals, MIMO
|
|
117
|
+
| [`commkit.core`](https://github.com/lokgar/commkit/tree/main/commkit/core) | `Signal`, `Reference`, `generate` (uniform, shaped through a distribution matcher, or coded with `code=`), `SingleCarrierFrame` and `Preamble` (pilots, guard intervals, MIMO channels), `extract_payload`. |
|
|
118
118
|
| [`commkit.mapping`](https://github.com/lokgar/commkit/tree/main/commkit/mapping) | `Constellation` (QAM, PSK, PAM, arbitrary points; Gray labels; probabilistic shaping with `.shaped()`), bit mapping, hard demapping, max-log and exact LLRs. |
|
|
119
119
|
| [`commkit.filtering`](https://github.com/lokgar/commkit/tree/main/commkit/filtering) | Pulses (`RRC`, `RC`, `Gaussian`, `Rect`, `SmoothRect`), FIR and IIR design (Butterworth, Chebyshev I/II, elliptic, Bessel), `fir_filter`, `iir_filter`, `matched_filter`, overlap-save, chromatic-dispersion compensation and digital backpropagation (`correct_fiber_propagation`). |
|
|
120
120
|
| [`commkit.multirate`](https://github.com/lokgar/commkit/blob/main/commkit/multirate.py) | `resample` (fractional), `decimate`, `upsample`, `decimate_to_symbol_rate`. |
|
|
121
121
|
| [`commkit.spectral`](https://github.com/lokgar/commkit/blob/main/commkit/spectral.py) | Welch PSD, spectrograms, frequency shifting, pilot tones. |
|
|
122
|
-
| [`commkit.impairments`](https://github.com/lokgar/commkit/tree/main/commkit/impairments) | AWGN, phase noise, IQ imbalance (
|
|
122
|
+
| [`commkit.impairments`](https://github.com/lokgar/commkit/tree/main/commkit/impairments) | AWGN at an Es/N0 or an OSNR, phase noise, IQ imbalance (blind estimate; Löwdin and Gram-Schmidt correction), chromatic dispersion, PMD and polarization mixing, and nonlinear fiber propagation over amplified spans (split-step Kerr effect with dispersion, loss, PMD and ASE) of a `Fiber`. |
|
|
123
123
|
| [`commkit.timing`](https://github.com/lokgar/commkit/blob/main/commkit/timing.py) | Barker and Zadoff-Chu sequences, `estimate_timing` / `correct_timing`, fractional delay estimation and correction. |
|
|
124
124
|
| [`commkit.frequency`](https://github.com/lokgar/commkit/blob/main/commkit/frequency.py) | `estimate_frequency_offset` / `correct_frequency_offset` with `MthPower`, `MengaliMorelli`, `PilotSymbols` and `BiasTone`; static and blockwise. |
|
|
125
125
|
| [`commkit.recovery`](https://github.com/lokgar/commkit/tree/main/commkit/recovery) | `estimate_carrier_phase` / `correct_carrier_phase` with `ViterbiViterbi`, `BPS`, `PLL`, `Tikhonov`, `DataAided`, `PilotAided`, `PilotTone(s)`; cycle-slip correction; `resolve_phase_ambiguity` and `resolve_channel_permutation`. |
|
|
126
|
-
| [`commkit.equalization`](https://github.com/lokgar/commkit/tree/main/commkit/equalization) | Sequential `lms`, `rls`, `cma`, `rde` (Numba) and frequency-domain `block_lms`, `block_cma`, `block_rde` (CuPy, CUDA graphs), butterfly MIMO, inline carrier recovery (`cpr=PLL()` / `BPS()`), continuation with `state=`, `zf_equalizer
|
|
127
|
-
| [`commkit.metrics`](https://github.com/lokgar/commkit/blob/main/commkit/metrics.py) | `evm`, `snr`, `ber`, `fer`, `ser`, `gmi`, `ngmi`, `mi`, including shaped constellations; pre-FEC and post-FEC; host values per channel. |
|
|
126
|
+
| [`commkit.equalization`](https://github.com/lokgar/commkit/tree/main/commkit/equalization) | Sequential `lms`, `rls`, `cma`, `rde` (Numba) and frequency-domain `block_lms`, `block_cma`, `block_rde` (CuPy, CUDA graphs), butterfly MIMO, inline carrier recovery (`cpr=PLL()` / `BPS()`), continuation with `state=`, `zf_equalizer`; pilot-aided CMA and RDE from a frame's pilots (`EqualizerPilots`); polarization demultiplexing on pilot tones (`estimate_polarization_tones` / `correct_polarization`). |
|
|
127
|
+
| [`commkit.metrics`](https://github.com/lokgar/commkit/blob/main/commkit/metrics.py) | `evm`, `snr`, `ber`, `fer`, `ser`, `gmi`, `ngmi`, `mi`, including shaped constellations; pre-FEC and post-FEC (`ber(decoded, sig)`); `estimate_noise_variance` for LLRs; host values per channel. |
|
|
128
128
|
| [`commkit.analysis`](https://github.com/lokgar/commkit/tree/main/commkit/analysis) | Laser and carrier-phase characterization: `estimate_linewidth` (increment slope, β-separation, delayed self-heterodyne FM-PSD / increment / Lorentzian), FM-noise PSDs, drift separation, Allan deviation. |
|
|
129
129
|
| [`commkit.math`](https://github.com/lokgar/commkit/blob/main/commkit/math.py) | `rms`, `normalize`, dB conversions. |
|
|
130
130
|
| [`commkit.smoothing`](https://github.com/lokgar/commkit/blob/main/commkit/smoothing.py) | Display and estimation smoothers (moving average, Savitzky-Golay, 2-D density). Signal-chain filters are in `filtering`. |
|
|
@@ -16,7 +16,7 @@ CommKit covers the receiver chain of coherent and IM/DD links (waveform generati
|
|
|
16
16
|
Data is a NumPy or CuPy array, or a `Signal` that wraps the samples together with what is known about them. Algorithms are plain functions that take the data and return a new `Signal` or array, or a typed result such as `EqualizerResult` or an `*Estimate`. Plotting functions draw those results; they never compute them.
|
|
17
17
|
|
|
18
18
|
- **Things that describe** a modulation, a pulse or a sub-algorithm are small frozen objects written in the call: `lms(rx, cpr=BPS(test_phases=64))`. The same object works standalone: `correct_carrier_phase(y, BPS())`.
|
|
19
|
-
- **A `Signal` carries facts and ground truth.** Facts are `sampling_rate`, `symbol_rate` and `
|
|
19
|
+
- **A `Signal` carries facts and ground truth.** Facts are `sampling_rate`, `symbol_rate`, `center_frequency` and `domain`; the carrier of an optical Signal gives its `center_wavelength_nm`, which dispersion, fiber propagation and OSNR noise read. The description is `constellation` and `pulse`. The ground truth is `reference`: the transmitted symbols, the bits on the air and the source bits before coding or distribution matching (the same array when there is neither). Functions take facts from the Signal and raise on a conflicting argument. Choices such as the decision constellation default to the Signal's and can be overridden.
|
|
20
20
|
- **Results with several values are frozen dataclasses** with named fields. Metrics return host floats: one per channel for `(C, N)` input.
|
|
21
21
|
- **The device follows the data.** Processing functions have no `backend=` or `device=` argument. Only factories, which have no input data to follow, take `device=`: `generate(..., device="gpu")`. Move existing data with `sig.to("gpu")`.
|
|
22
22
|
- **Plots draw results.** `plot_*` functions take a Signal, an array or a result object; numerical code never imports plotting.
|
|
@@ -78,17 +78,17 @@ CommKit never picks a device for you: data stays where you put it, and each func
|
|
|
78
78
|
|
|
79
79
|
| Module | Contents |
|
|
80
80
|
| --- | --- |
|
|
81
|
-
| [`commkit.core`](https://github.com/lokgar/commkit/tree/main/commkit/core) | `Signal`, `Reference`, `generate` (uniform, shaped through a distribution matcher, or coded with `code=`), `SingleCarrierFrame` and `Preamble` (pilots, guard intervals, MIMO
|
|
81
|
+
| [`commkit.core`](https://github.com/lokgar/commkit/tree/main/commkit/core) | `Signal`, `Reference`, `generate` (uniform, shaped through a distribution matcher, or coded with `code=`), `SingleCarrierFrame` and `Preamble` (pilots, guard intervals, MIMO channels), `extract_payload`. |
|
|
82
82
|
| [`commkit.mapping`](https://github.com/lokgar/commkit/tree/main/commkit/mapping) | `Constellation` (QAM, PSK, PAM, arbitrary points; Gray labels; probabilistic shaping with `.shaped()`), bit mapping, hard demapping, max-log and exact LLRs. |
|
|
83
83
|
| [`commkit.filtering`](https://github.com/lokgar/commkit/tree/main/commkit/filtering) | Pulses (`RRC`, `RC`, `Gaussian`, `Rect`, `SmoothRect`), FIR and IIR design (Butterworth, Chebyshev I/II, elliptic, Bessel), `fir_filter`, `iir_filter`, `matched_filter`, overlap-save, chromatic-dispersion compensation and digital backpropagation (`correct_fiber_propagation`). |
|
|
84
84
|
| [`commkit.multirate`](https://github.com/lokgar/commkit/blob/main/commkit/multirate.py) | `resample` (fractional), `decimate`, `upsample`, `decimate_to_symbol_rate`. |
|
|
85
85
|
| [`commkit.spectral`](https://github.com/lokgar/commkit/blob/main/commkit/spectral.py) | Welch PSD, spectrograms, frequency shifting, pilot tones. |
|
|
86
|
-
| [`commkit.impairments`](https://github.com/lokgar/commkit/tree/main/commkit/impairments) | AWGN, phase noise, IQ imbalance (
|
|
86
|
+
| [`commkit.impairments`](https://github.com/lokgar/commkit/tree/main/commkit/impairments) | AWGN at an Es/N0 or an OSNR, phase noise, IQ imbalance (blind estimate; Löwdin and Gram-Schmidt correction), chromatic dispersion, PMD and polarization mixing, and nonlinear fiber propagation over amplified spans (split-step Kerr effect with dispersion, loss, PMD and ASE) of a `Fiber`. |
|
|
87
87
|
| [`commkit.timing`](https://github.com/lokgar/commkit/blob/main/commkit/timing.py) | Barker and Zadoff-Chu sequences, `estimate_timing` / `correct_timing`, fractional delay estimation and correction. |
|
|
88
88
|
| [`commkit.frequency`](https://github.com/lokgar/commkit/blob/main/commkit/frequency.py) | `estimate_frequency_offset` / `correct_frequency_offset` with `MthPower`, `MengaliMorelli`, `PilotSymbols` and `BiasTone`; static and blockwise. |
|
|
89
89
|
| [`commkit.recovery`](https://github.com/lokgar/commkit/tree/main/commkit/recovery) | `estimate_carrier_phase` / `correct_carrier_phase` with `ViterbiViterbi`, `BPS`, `PLL`, `Tikhonov`, `DataAided`, `PilotAided`, `PilotTone(s)`; cycle-slip correction; `resolve_phase_ambiguity` and `resolve_channel_permutation`. |
|
|
90
|
-
| [`commkit.equalization`](https://github.com/lokgar/commkit/tree/main/commkit/equalization) | Sequential `lms`, `rls`, `cma`, `rde` (Numba) and frequency-domain `block_lms`, `block_cma`, `block_rde` (CuPy, CUDA graphs), butterfly MIMO, inline carrier recovery (`cpr=PLL()` / `BPS()`), continuation with `state=`, `zf_equalizer
|
|
91
|
-
| [`commkit.metrics`](https://github.com/lokgar/commkit/blob/main/commkit/metrics.py) | `evm`, `snr`, `ber`, `fer`, `ser`, `gmi`, `ngmi`, `mi`, including shaped constellations; pre-FEC and post-FEC; host values per channel. |
|
|
90
|
+
| [`commkit.equalization`](https://github.com/lokgar/commkit/tree/main/commkit/equalization) | Sequential `lms`, `rls`, `cma`, `rde` (Numba) and frequency-domain `block_lms`, `block_cma`, `block_rde` (CuPy, CUDA graphs), butterfly MIMO, inline carrier recovery (`cpr=PLL()` / `BPS()`), continuation with `state=`, `zf_equalizer`; pilot-aided CMA and RDE from a frame's pilots (`EqualizerPilots`); polarization demultiplexing on pilot tones (`estimate_polarization_tones` / `correct_polarization`). |
|
|
91
|
+
| [`commkit.metrics`](https://github.com/lokgar/commkit/blob/main/commkit/metrics.py) | `evm`, `snr`, `ber`, `fer`, `ser`, `gmi`, `ngmi`, `mi`, including shaped constellations; pre-FEC and post-FEC (`ber(decoded, sig)`); `estimate_noise_variance` for LLRs; host values per channel. |
|
|
92
92
|
| [`commkit.analysis`](https://github.com/lokgar/commkit/tree/main/commkit/analysis) | Laser and carrier-phase characterization: `estimate_linewidth` (increment slope, β-separation, delayed self-heterodyne FM-PSD / increment / Lorentzian), FM-noise PSDs, drift separation, Allan deviation. |
|
|
93
93
|
| [`commkit.math`](https://github.com/lokgar/commkit/blob/main/commkit/math.py) | `rms`, `normalize`, dB conversions. |
|
|
94
94
|
| [`commkit.smoothing`](https://github.com/lokgar/commkit/blob/main/commkit/smoothing.py) | Display and estimation smoothers (moving average, Savitzky-Golay, 2-D density). Signal-chain filters are in `filtering`. |
|
|
@@ -102,8 +102,8 @@ def _bench_decode(benchmark, xp, sync, code, method, num_codewords, esn0_db):
|
|
|
102
102
|
("method", "esn0_db"), [(SC(), 3.0), (SCL(8), 2.0)], ids=["sc", "scl8"]
|
|
103
103
|
)
|
|
104
104
|
def bench_polar_decode(benchmark, backend_device, xp, sync, method, esn0_db):
|
|
105
|
-
"""PolarCode.nr(512, 1024) with the NR CRC-16, 64 codewords."""
|
|
106
|
-
code = PolarCode.nr(512, 1024, crc="16")
|
|
105
|
+
"""PolarCode.nr(k=512, n=1024) with the NR CRC-16, 64 codewords."""
|
|
106
|
+
code = PolarCode.nr(k=512, n=1024, crc="16")
|
|
107
107
|
_bench_decode(benchmark, xp, sync, code, method, 64, esn0_db)
|
|
108
108
|
|
|
109
109
|
|
|
@@ -114,11 +114,11 @@ def bench_viterbi_decode(benchmark, backend_device, xp, sync, termination):
|
|
|
114
114
|
_bench_decode(benchmark, xp, sync, code, Viterbi(), 16, 5.0)
|
|
115
115
|
|
|
116
116
|
|
|
117
|
-
@pytest.mark.parametrize("
|
|
118
|
-
def bench_turbo_decode(benchmark, backend_device, xp, sync,
|
|
117
|
+
@pytest.mark.parametrize("method", ["maxlog", "exact"])
|
|
118
|
+
def bench_turbo_decode(benchmark, backend_device, xp, sync, method):
|
|
119
119
|
"""TurboCode.lte(1024), 32 blocks, 6 iterations, at Es/N0 = 0 dB."""
|
|
120
120
|
code = TurboCode.lte(1024)
|
|
121
|
-
_bench_decode(benchmark, xp, sync, code, TurboIterations(6,
|
|
121
|
+
_bench_decode(benchmark, xp, sync, code, TurboIterations(6, method=method), 32, 0.0)
|
|
122
122
|
|
|
123
123
|
|
|
124
124
|
def bench_chase_decode(benchmark, backend_device, xp, sync):
|
|
@@ -131,7 +131,7 @@ def bench_chase_decode(benchmark, backend_device, xp, sync):
|
|
|
131
131
|
|
|
132
132
|
def bench_ccdm_decode(benchmark, backend_device, xp, sync):
|
|
133
133
|
"""Dematching of 8 blocks of 20000 PS-64-QAM symbols (labels, no noise)."""
|
|
134
|
-
code = CCDM(Constellation.qam(64).shaped(entropy=5.0),
|
|
134
|
+
code = CCDM(Constellation.qam(64).shaped(entropy=5.0), num_symbols=20_000)
|
|
135
135
|
rng = np.random.default_rng(0)
|
|
136
136
|
bits = rng.integers(0, 2, 8 * code.k, dtype=np.int8)
|
|
137
137
|
labels = xp.asarray(np.asarray(encode(bits, code=code)))
|
|
@@ -33,7 +33,7 @@ def _frame_signal():
|
|
|
33
33
|
# snapped 32_768 up to, so the workload matches the 0002 baseline.
|
|
34
34
|
payload_num_symbols=32_798,
|
|
35
35
|
payload_constellation=Constellation.qam(16),
|
|
36
|
-
|
|
36
|
+
payload_rng=42,
|
|
37
37
|
preamble=Preamble(sequence_type="barker", num_symbols=13),
|
|
38
38
|
pilot_pattern="comb",
|
|
39
39
|
pilot_period=32,
|
|
@@ -26,12 +26,10 @@ import numpy as np
|
|
|
26
26
|
|
|
27
27
|
from . import _fft
|
|
28
28
|
from ._array import as_2d, restore_1d
|
|
29
|
+
from ._constants import SPEED_OF_LIGHT as _SPEED_OF_LIGHT
|
|
29
30
|
from .backend import ArrayType, dispatch
|
|
30
31
|
from .math import _phasor
|
|
31
32
|
|
|
32
|
-
# Speed of light used by the 1.x conversion, kept so results do not change.
|
|
33
|
-
_SPEED_OF_LIGHT = 2.998e8 # m/s
|
|
34
|
-
|
|
35
33
|
|
|
36
34
|
def beta2_length(
|
|
37
35
|
*,
|
|
@@ -21,7 +21,7 @@ _BARKER_SEQUENCES = {
|
|
|
21
21
|
}
|
|
22
22
|
|
|
23
23
|
|
|
24
|
-
def barker_sequence(
|
|
24
|
+
def barker_sequence(num_symbols: int) -> np.ndarray:
|
|
25
25
|
"""Generate a Barker sequence.
|
|
26
26
|
|
|
27
27
|
Barker sequences are binary (+1, -1) sequences whose aperiodic
|
|
@@ -30,36 +30,36 @@ def barker_sequence(length: int) -> np.ndarray:
|
|
|
30
30
|
|
|
31
31
|
Parameters
|
|
32
32
|
----------
|
|
33
|
-
|
|
34
|
-
Sequence length.
|
|
33
|
+
num_symbols : {2, 3, 4, 5, 7, 11, 13}
|
|
34
|
+
Sequence length in symbols.
|
|
35
35
|
|
|
36
36
|
Returns
|
|
37
37
|
-------
|
|
38
38
|
ndarray
|
|
39
|
-
Host float32 BPSK symbols (+1, -1), ``(
|
|
39
|
+
Host float32 BPSK symbols (+1, -1), ``(num_symbols,)``; move them with
|
|
40
40
|
``to_device`` if needed.
|
|
41
41
|
|
|
42
42
|
Raises
|
|
43
43
|
------
|
|
44
44
|
ValueError
|
|
45
|
-
If
|
|
45
|
+
If ``num_symbols`` is not a Barker length.
|
|
46
46
|
|
|
47
47
|
Examples
|
|
48
48
|
--------
|
|
49
49
|
>>> barker_sequence(7)
|
|
50
50
|
array([ 1., 1., 1., -1., -1., 1., -1.], dtype=float32)
|
|
51
51
|
"""
|
|
52
|
-
if
|
|
52
|
+
if num_symbols not in _BARKER_SEQUENCES:
|
|
53
53
|
valid = sorted(_BARKER_SEQUENCES.keys())
|
|
54
|
-
raise ValueError(f"No Barker sequence of length {
|
|
54
|
+
raise ValueError(f"No Barker sequence of length {num_symbols}. Valid: {valid}")
|
|
55
55
|
|
|
56
|
-
seq = np.array(_BARKER_SEQUENCES[
|
|
56
|
+
seq = np.array(_BARKER_SEQUENCES[num_symbols], dtype="float32")
|
|
57
57
|
|
|
58
|
-
logger.debug("Generated Barker-%s sequence.",
|
|
58
|
+
logger.debug("Generated Barker-%s sequence.", num_symbols)
|
|
59
59
|
return seq
|
|
60
60
|
|
|
61
61
|
|
|
62
|
-
def zadoff_chu_sequence(
|
|
62
|
+
def zadoff_chu_sequence(num_symbols: int, *, root: int = 1) -> np.ndarray:
|
|
63
63
|
r"""Generate a Zadoff-Chu (ZC) synchronization sequence.
|
|
64
64
|
|
|
65
65
|
ZC sequences are constant-amplitude zero-autocorrelation (CAZAC)
|
|
@@ -69,72 +69,72 @@ def zadoff_chu_sequence(length: int, *, root: int = 1) -> np.ndarray:
|
|
|
69
69
|
|
|
70
70
|
Parameters
|
|
71
71
|
----------
|
|
72
|
-
|
|
72
|
+
num_symbols : int
|
|
73
73
|
Sequence length ``N_ZC``; a prime gives the lowest cross-correlation
|
|
74
74
|
between roots.
|
|
75
75
|
root : int, default 1
|
|
76
|
-
Root index ``u`` in ``[1,
|
|
76
|
+
Root index ``u`` in ``[1, num_symbols)``, coprime to ``num_symbols`` (not
|
|
77
77
|
checked).
|
|
78
78
|
|
|
79
79
|
Returns
|
|
80
80
|
-------
|
|
81
81
|
ndarray
|
|
82
|
-
Host complex64 symbols of unit magnitude, ``(
|
|
82
|
+
Host complex64 symbols of unit magnitude, ``(num_symbols,)``.
|
|
83
83
|
|
|
84
84
|
Raises
|
|
85
85
|
------
|
|
86
86
|
ValueError
|
|
87
|
-
If ``
|
|
87
|
+
If ``num_symbols < 1`` or ``root`` is outside ``[1, num_symbols)``.
|
|
88
88
|
|
|
89
89
|
Notes
|
|
90
90
|
-----
|
|
91
91
|
- For odd lengths: x[n] = exp(-j * pi * u * n * (n + 1) / N_ZC)
|
|
92
92
|
- For even lengths: x[n] = exp(-j * pi * u * n^2 / N_ZC)
|
|
93
93
|
"""
|
|
94
|
-
if
|
|
95
|
-
raise ValueError("
|
|
96
|
-
if root < 1 or root >=
|
|
97
|
-
raise ValueError(f"Root must be in [1, {
|
|
94
|
+
if num_symbols < 1:
|
|
95
|
+
raise ValueError(f"num_symbols must be positive, got {num_symbols}.")
|
|
96
|
+
if root < 1 or root >= num_symbols:
|
|
97
|
+
raise ValueError(f"Root must be in [1, {num_symbols - 1}].")
|
|
98
98
|
|
|
99
99
|
xp = np
|
|
100
100
|
|
|
101
101
|
# ZC formula: x[n] = exp(-j * pi * u * n * (n+1) / N)
|
|
102
|
-
n = xp.arange(
|
|
103
|
-
if
|
|
102
|
+
n = xp.arange(num_symbols)
|
|
103
|
+
if num_symbols % 2 == 0:
|
|
104
104
|
# Even length: x[n] = exp(-j * pi * u * n^2 / N)
|
|
105
|
-
seq = xp.exp(-1j * xp.pi * root * n * n /
|
|
105
|
+
seq = xp.exp(-1j * xp.pi * root * n * n / num_symbols)
|
|
106
106
|
else:
|
|
107
107
|
# Odd length: x[n] = exp(-j * pi * u * n * (n+1) / N)
|
|
108
|
-
seq = xp.exp(-1j * xp.pi * root * n * (n + 1) /
|
|
108
|
+
seq = xp.exp(-1j * xp.pi * root * n * (n + 1) / num_symbols)
|
|
109
109
|
|
|
110
110
|
out: np.ndarray = seq.astype(xp.complex64)
|
|
111
111
|
|
|
112
|
-
logger.debug("Generated ZC sequence:
|
|
112
|
+
logger.debug("Generated ZC sequence: num_symbols=%s, root=%s.", num_symbols, root)
|
|
113
113
|
return out
|
|
114
114
|
|
|
115
115
|
|
|
116
|
-
def zc_mimo_root(
|
|
117
|
-
"""Return the Zadoff-Chu root for
|
|
116
|
+
def zc_mimo_root(channel_index: int, base_root: int, num_symbols: int) -> int:
|
|
117
|
+
"""Return the Zadoff-Chu root for channel ``channel_index`` in a MIMO preamble.
|
|
118
118
|
|
|
119
|
-
Assigns a deterministic unique root to each
|
|
119
|
+
Assigns a deterministic unique root to each channel by cycling through
|
|
120
120
|
distinct roots starting from ``base_root``, wrapping in the range
|
|
121
|
-
``[1,
|
|
121
|
+
``[1, num_symbols-1]``. For a prime ``num_symbols`` all roots are valid CAZAC
|
|
122
122
|
sequences; any two distinct roots are near-orthogonal with cross-correlation
|
|
123
|
-
magnitude ``1/sqrt(
|
|
123
|
+
magnitude ``1/sqrt(num_symbols)`` at every lag.
|
|
124
124
|
|
|
125
125
|
Parameters
|
|
126
126
|
----------
|
|
127
|
-
|
|
128
|
-
|
|
127
|
+
channel_index : int
|
|
128
|
+
Channel index (0-based).
|
|
129
129
|
base_root : int
|
|
130
|
-
ZC root assigned to
|
|
131
|
-
|
|
130
|
+
ZC root assigned to channel 0. Must be in ``[1, num_symbols-1]``.
|
|
131
|
+
num_symbols : int
|
|
132
132
|
Sequence length (should be prime for the CAZAC property).
|
|
133
133
|
|
|
134
134
|
Returns
|
|
135
135
|
-------
|
|
136
136
|
int
|
|
137
|
-
ZC root for
|
|
137
|
+
ZC root for channel ``channel_index``, guaranteed in ``[1, num_symbols-1]``.
|
|
138
138
|
|
|
139
139
|
Examples
|
|
140
140
|
--------
|
|
@@ -143,4 +143,4 @@ def zc_mimo_root(stream_idx: int, base_root: int, length: int) -> int:
|
|
|
143
143
|
>>> [zc_mimo_root(k, 10, 13) for k in range(4)]
|
|
144
144
|
[10, 11, 12, 1]
|
|
145
145
|
"""
|
|
146
|
-
return ((base_root - 1 +
|
|
146
|
+
return ((base_root - 1 + channel_index) % (num_symbols - 1)) + 1
|
|
@@ -76,7 +76,7 @@ def _welch_median_bias(n_samples: int, nperseg: int) -> tuple[float, int]:
|
|
|
76
76
|
-------
|
|
77
77
|
factor : float
|
|
78
78
|
``median/mean`` ratio in ``(0, 1]``.
|
|
79
|
-
|
|
79
|
+
num_segments : int
|
|
80
80
|
Welch segment count ``K``.
|
|
81
81
|
"""
|
|
82
82
|
step = max(nperseg // 2, 1)
|
|
@@ -269,7 +269,7 @@ def _welch_floor_bias(n_samples: int, nperseg: int, *, label: str) -> tuple[floa
|
|
|
269
269
|
-------
|
|
270
270
|
factor : float
|
|
271
271
|
``median/mean`` ratio to divide the median-based floor by.
|
|
272
|
-
|
|
272
|
+
num_segments : int
|
|
273
273
|
Welch segment count ``K``.
|
|
274
274
|
"""
|
|
275
275
|
m_med, k_seg = _welch_median_bias(n_samples, nperseg)
|
|
@@ -19,14 +19,14 @@ class AllanDeviation:
|
|
|
19
19
|
|
|
20
20
|
Attributes
|
|
21
21
|
----------
|
|
22
|
-
|
|
22
|
+
taus : numpy.ndarray
|
|
23
23
|
Averaging times in seconds, ``(n_tau,)``.
|
|
24
24
|
adev : numpy.ndarray
|
|
25
25
|
Allan deviation in Hz, ``(n_tau,)`` or ``(C, n_tau)``; NaN where a
|
|
26
26
|
``tau`` does not fit the record.
|
|
27
27
|
"""
|
|
28
28
|
|
|
29
|
-
|
|
29
|
+
taus: np.ndarray
|
|
30
30
|
adev: np.ndarray
|
|
31
31
|
|
|
32
32
|
|
|
@@ -59,7 +59,7 @@ def allan_deviation(
|
|
|
59
59
|
Returns
|
|
60
60
|
-------
|
|
61
61
|
AllanDeviation
|
|
62
|
-
``
|
|
62
|
+
``taus`` and ``adev``, ``(n_tau,)`` (SISO) or ``(C, n_tau)`` (MIMO),
|
|
63
63
|
host NumPy arrays.
|
|
64
64
|
|
|
65
65
|
Notes
|
|
@@ -101,7 +101,7 @@ def allan_deviation(
|
|
|
101
101
|
ms = np.unique(np.maximum(1, np.round(taus_cpu / tau0).astype(int)))
|
|
102
102
|
ms = ms[ms <= max(1, (x.shape[-1] - 1) // 2)]
|
|
103
103
|
|
|
104
|
-
|
|
104
|
+
taus = ms * tau0
|
|
105
105
|
adev = xp.full((c, ms.size), xp.nan, dtype=xp.float64)
|
|
106
106
|
# One vectorized second-difference per τ across all channels (the C-loop
|
|
107
107
|
# would launch C·n_tau tiny kernels on GPU for no benefit).
|
|
@@ -113,8 +113,8 @@ def allan_deviation(
|
|
|
113
113
|
avar = xp.mean(d2**2, axis=-1) / (2.0 * (m * tau0) ** 2)
|
|
114
114
|
adev[:, j] = xp.sqrt(avar)
|
|
115
115
|
|
|
116
|
-
|
|
116
|
+
taus_cpu = np.asarray(taus, dtype=np.float64)
|
|
117
117
|
adev_cpu = to_device(adev, "cpu")
|
|
118
118
|
adev_out = restore_1d(was_1d, adev_cpu)
|
|
119
119
|
|
|
120
|
-
return AllanDeviation(
|
|
120
|
+
return AllanDeviation(taus=taus_cpu, adev=adev_out)
|
|
@@ -118,7 +118,7 @@ class DshFmPsd:
|
|
|
118
118
|
* the last term: additive detection noise. Noise on the beat adds a
|
|
119
119
|
white angle error of ``σ²`` rad² per sample, whose first difference
|
|
120
120
|
has this ``≈ f²`` spectrum; it bypasses the interferometer, so ``g``
|
|
121
|
-
does not shape it (reported as ``
|
|
121
|
+
does not shape it (reported as ``angle_noise_var``).
|
|
122
122
|
|
|
123
123
|
The PSD is computed without the ``sinc²`` correction of
|
|
124
124
|
:func:`fm_noise_psd`: sampled white FM has white increments, so its PSD is
|
|
@@ -474,9 +474,9 @@ def _dsh_fm_psd(samples: ArrayType, method: DshFmPsd, fs: float) -> LinewidthEst
|
|
|
474
474
|
valid=valid,
|
|
475
475
|
used=per_channel(used2),
|
|
476
476
|
band=(float(f_used[0]), float(f_used[-1])),
|
|
477
|
-
|
|
477
|
+
num_segments=k_seg,
|
|
478
478
|
flicker=to_report_scalar(theta[:, 1], was_1d=was_1d),
|
|
479
|
-
|
|
479
|
+
angle_noise_var=to_report_scalar(theta[:, 2], was_1d=was_1d),
|
|
480
480
|
)
|
|
481
481
|
|
|
482
482
|
|
|
@@ -534,7 +534,7 @@ def _dsh_increment(
|
|
|
534
534
|
value=to_report_scalar(np.maximum(slope, 0.0) / (4.0 * np.pi), was_1d=was_1d),
|
|
535
535
|
method=method,
|
|
536
536
|
f_shift=f_hat,
|
|
537
|
-
|
|
537
|
+
angle_noise_var=to_report_scalar(intercept, was_1d=was_1d),
|
|
538
538
|
dphi_var=to_report_scalar(to_device(dphi_var, "cpu"), was_1d=was_1d),
|
|
539
539
|
lags=ls,
|
|
540
540
|
lag_s=a_sec,
|
|
@@ -247,7 +247,7 @@ def _increment_slope(
|
|
|
247
247
|
value=to_report_scalar(np.maximum(slope, 0.0) / (2.0 * np.pi), was_1d=was_1d),
|
|
248
248
|
method=method,
|
|
249
249
|
dphi_var=to_report_scalar(to_device(var1, "cpu"), was_1d=was_1d),
|
|
250
|
-
|
|
250
|
+
angle_noise_var=to_report_scalar(intercept, was_1d=was_1d),
|
|
251
251
|
lag_s=lag_sec,
|
|
252
252
|
var=var_k_cpu.T,
|
|
253
253
|
slope=slope,
|
|
@@ -285,10 +285,10 @@ def _increment_subtract(
|
|
|
285
285
|
d2 = normalize(d2, mode="average_power", axis=-1)
|
|
286
286
|
inv = 1.0 / xp.maximum(xp.abs(d2) ** 2, 1e-12)
|
|
287
287
|
pair_mean = 0.5 * (inv[:, 1:] + inv[:, :-1])
|
|
288
|
-
|
|
288
|
+
angle_noise_var = sigma_n2 * xp.mean(pair_mean, axis=-1)
|
|
289
289
|
else:
|
|
290
|
-
|
|
291
|
-
linewidth = xp.maximum(var1 -
|
|
290
|
+
angle_noise_var = sigma_n2.copy()
|
|
291
|
+
linewidth = xp.maximum(var1 - angle_noise_var, 0.0) / (2.0 * np.pi * t_sym)
|
|
292
292
|
|
|
293
293
|
var1_cpu = to_device(var1, "cpu")
|
|
294
294
|
was_1d = p.ndim == 1
|
|
@@ -296,7 +296,9 @@ def _increment_subtract(
|
|
|
296
296
|
value=to_report_scalar(to_device(linewidth, "cpu"), was_1d=was_1d),
|
|
297
297
|
method=method,
|
|
298
298
|
dphi_var=to_report_scalar(var1_cpu, was_1d=was_1d),
|
|
299
|
-
|
|
299
|
+
angle_noise_var=to_report_scalar(
|
|
300
|
+
to_device(angle_noise_var, "cpu"), was_1d=was_1d
|
|
301
|
+
),
|
|
300
302
|
lag_s=np.array([t_sym]),
|
|
301
303
|
var=np.atleast_1d(var1_cpu)[:, None],
|
|
302
304
|
)
|
|
@@ -370,7 +372,7 @@ def _beta_separation(
|
|
|
370
372
|
value=to_report_scalar(lw_cpu, was_1d=was_1d),
|
|
371
373
|
method=method,
|
|
372
374
|
linewidth_floor=to_report_scalar(lw_floor_cpu, was_1d=was_1d),
|
|
373
|
-
|
|
375
|
+
num_segments=k_seg,
|
|
374
376
|
area_hz2=to_report_scalar(area_cpu, was_1d=was_1d),
|
|
375
377
|
f=f_cpu,
|
|
376
378
|
S_f=S_cpu,
|
|
@@ -51,7 +51,7 @@ class LinewidthEstimate:
|
|
|
51
51
|
band : tuple of float or None
|
|
52
52
|
``BetaSeparation``: the ``(f_min, f_max)`` fence applied.
|
|
53
53
|
``DshFmPsd``: the frequency extent of ``used``.
|
|
54
|
-
|
|
54
|
+
num_segments : int or None
|
|
55
55
|
Welch segment count ``K`` behind every bin (``BetaSeparation``,
|
|
56
56
|
``DshFmPsd``).
|
|
57
57
|
flicker : float or numpy.ndarray or None
|
|
@@ -82,7 +82,7 @@ class LinewidthEstimate:
|
|
|
82
82
|
slope, intercept : numpy.ndarray or None
|
|
83
83
|
The fit ``var = slope * lag_s + intercept`` per channel, rad²/s and
|
|
84
84
|
rad² (``IncrementSlope``, ``DshIncrement``).
|
|
85
|
-
|
|
85
|
+
angle_noise_var : float or numpy.ndarray or None
|
|
86
86
|
Additive-noise term in rad²: the fitted intercept (``IncrementSlope``,
|
|
87
87
|
``DshIncrement``), the subtracted term (``IncrementSubtract``) or the
|
|
88
88
|
fitted angle-noise variance per sample (``DshFmPsd``).
|
|
@@ -109,7 +109,7 @@ class LinewidthEstimate:
|
|
|
109
109
|
S_f_model: np.ndarray | None = None
|
|
110
110
|
used: np.ndarray | None = None
|
|
111
111
|
band: tuple[float, float] | None = None
|
|
112
|
-
|
|
112
|
+
num_segments: int | None = None
|
|
113
113
|
flicker: float | np.ndarray | None = None
|
|
114
114
|
linewidth_floor: float | np.ndarray | None = None
|
|
115
115
|
area_hz2: float | np.ndarray | None = None
|
|
@@ -122,7 +122,7 @@ class LinewidthEstimate:
|
|
|
122
122
|
var: np.ndarray | None = None
|
|
123
123
|
slope: np.ndarray | None = None
|
|
124
124
|
intercept: np.ndarray | None = None
|
|
125
|
-
|
|
125
|
+
angle_noise_var: float | np.ndarray | None = None
|
|
126
126
|
dphi_var: float | np.ndarray | None = None
|
|
127
127
|
linewidth_3db: float | np.ndarray | None = None
|
|
128
128
|
lineshape_ratio: float | np.ndarray | None = None
|