commkit 3.7.0__tar.gz → 3.7.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.7.0 → commkit-3.7.1}/CHANGELOG.md +23 -1
- {commkit-3.7.0 → commkit-3.7.1}/PKG-INFO +1 -1
- {commkit-3.7.0 → commkit-3.7.1}/commkit/__init__.py +1 -1
- {commkit-3.7.0 → commkit-3.7.1}/commkit/timing/_numba_loops.py +21 -5
- {commkit-3.7.0 → commkit-3.7.1}/commkit/timing/clock_recovery.py +78 -2
- {commkit-3.7.0 → commkit-3.7.1}/examples/10-front-end-impairments.ipynb +259 -75
- commkit-3.7.1/examples/12-sampling-clock-recovery.ipynb +743 -0
- commkit-3.7.1/examples/22-direct-and-self-coherent-detection.ipynb +736 -0
- {commkit-3.7.0 → commkit-3.7.1}/examples/README.md +3 -3
- {commkit-3.7.0 → commkit-3.7.1}/pyproject.toml +2 -2
- {commkit-3.7.0 → commkit-3.7.1}/tests/common/reference_impl.py +4 -2
- {commkit-3.7.0 → commkit-3.7.1}/tests/timing/test_clock_recovery.py +73 -0
- {commkit-3.7.0 → commkit-3.7.1}/uv.lock +1 -1
- commkit-3.7.0/examples/12-sampling-clock-recovery.ipynb +0 -316
- commkit-3.7.0/examples/22-direct-and-self-coherent-detection.ipynb +0 -278
- {commkit-3.7.0 → commkit-3.7.1}/.gitattributes +0 -0
- {commkit-3.7.0 → commkit-3.7.1}/.github/icon.svg +0 -0
- {commkit-3.7.0 → commkit-3.7.1}/.github/logo.svg +0 -0
- {commkit-3.7.0 → commkit-3.7.1}/.github/workflows/ci.yml +0 -0
- {commkit-3.7.0 → commkit-3.7.1}/.github/workflows/publish.yml +0 -0
- {commkit-3.7.0 → commkit-3.7.1}/.gitignore +0 -0
- {commkit-3.7.0 → commkit-3.7.1}/.python-version +0 -0
- {commkit-3.7.0 → commkit-3.7.1}/AGENTS.md +0 -0
- {commkit-3.7.0 → commkit-3.7.1}/LICENSE +0 -0
- {commkit-3.7.0 → commkit-3.7.1}/README.md +0 -0
- {commkit-3.7.0 → commkit-3.7.1}/benchmarks/baselines/Linux-CPython-3.14-64bit/0002_pre_v2.json +0 -0
- {commkit-3.7.0 → commkit-3.7.1}/benchmarks/baselines/Linux-CPython-3.14-64bit/0003_v2_0.json +0 -0
- {commkit-3.7.0 → commkit-3.7.1}/benchmarks/baselines/Linux-CPython-3.14-64bit/0004_fiber_dbp.json +0 -0
- {commkit-3.7.0 → commkit-3.7.1}/benchmarks/baselines/Linux-CPython-3.14-64bit/0005_gpu_decoder_path.json +0 -0
- {commkit-3.7.0 → commkit-3.7.1}/benchmarks/baselines/Linux-CPython-3.14-64bit/0006_perf_cleanup.json +0 -0
- {commkit-3.7.0 → commkit-3.7.1}/benchmarks/baselines/Linux-CPython-3.14-64bit/0007_v3_0.json +0 -0
- {commkit-3.7.0 → commkit-3.7.1}/benchmarks/baselines/Linux-CPython-3.14-64bit/0008_v3_1.json +0 -0
- {commkit-3.7.0 → commkit-3.7.1}/benchmarks/bench_block_blind.py +0 -0
- {commkit-3.7.0 → commkit-3.7.1}/benchmarks/bench_block_lms.py +0 -0
- {commkit-3.7.0 → commkit-3.7.1}/benchmarks/bench_bps.py +0 -0
- {commkit-3.7.0 → commkit-3.7.1}/benchmarks/bench_coding.py +0 -0
- {commkit-3.7.0 → commkit-3.7.1}/benchmarks/bench_equalizers.py +0 -0
- {commkit-3.7.0 → commkit-3.7.1}/benchmarks/bench_fiber.py +0 -0
- {commkit-3.7.0 → commkit-3.7.1}/benchmarks/bench_filters.py +0 -0
- {commkit-3.7.0 → commkit-3.7.1}/benchmarks/bench_gardner.py +0 -0
- {commkit-3.7.0 → commkit-3.7.1}/benchmarks/bench_import.py +0 -0
- {commkit-3.7.0 → commkit-3.7.1}/benchmarks/bench_llr.py +0 -0
- {commkit-3.7.0 → commkit-3.7.1}/benchmarks/bench_mapping.py +0 -0
- {commkit-3.7.0 → commkit-3.7.1}/benchmarks/bench_signal_pipeline.py +0 -0
- {commkit-3.7.0 → commkit-3.7.1}/benchmarks/bench_sync_misc.py +0 -0
- {commkit-3.7.0 → commkit-3.7.1}/benchmarks/benchutils.py +0 -0
- {commkit-3.7.0 → commkit-3.7.1}/benchmarks/conftest.py +0 -0
- {commkit-3.7.0 → commkit-3.7.1}/benchmarks/record_baseline.py +0 -0
- {commkit-3.7.0 → commkit-3.7.1}/benchmarks/workloads.py +0 -0
- {commkit-3.7.0 → commkit-3.7.1}/commkit/_array.py +0 -0
- {commkit-3.7.0 → commkit-3.7.1}/commkit/_circular.py +0 -0
- {commkit-3.7.0 → commkit-3.7.1}/commkit/_constants.py +0 -0
- {commkit-3.7.0 → commkit-3.7.1}/commkit/_cuda/__init__.py +0 -0
- {commkit-3.7.0 → commkit-3.7.1}/commkit/_cuda/compiler.py +0 -0
- {commkit-3.7.0 → commkit-3.7.1}/commkit/_cuda/graphs.py +0 -0
- {commkit-3.7.0 → commkit-3.7.1}/commkit/_cuda/src/algebraic.cu +0 -0
- {commkit-3.7.0 → commkit-3.7.1}/commkit/_cuda/src/bps_anchor.cu +0 -0
- {commkit-3.7.0 → commkit-3.7.1}/commkit/_cuda/src/bps_min_d2.cu +0 -0
- {commkit-3.7.0 → commkit-3.7.1}/commkit/_cuda/src/cs_block.cu +0 -0
- {commkit-3.7.0 → commkit-3.7.1}/commkit/_cuda/src/iir.cu +0 -0
- {commkit-3.7.0 → commkit-3.7.1}/commkit/_cuda/src/ldpc.cu +0 -0
- {commkit-3.7.0 → commkit-3.7.1}/commkit/_cuda/src/llr.cu +0 -0
- {commkit-3.7.0 → commkit-3.7.1}/commkit/_cuda/src/selftest.cu +0 -0
- {commkit-3.7.0 → commkit-3.7.1}/commkit/_dispersion.py +0 -0
- {commkit-3.7.0 → commkit-3.7.1}/commkit/_fft.py +0 -0
- {commkit-3.7.0 → commkit-3.7.1}/commkit/_fiber.py +0 -0
- {commkit-3.7.0 → commkit-3.7.1}/commkit/_overlap_save.py +0 -0
- {commkit-3.7.0 → commkit-3.7.1}/commkit/_random.py +0 -0
- {commkit-3.7.0 → commkit-3.7.1}/commkit/_sequences.py +0 -0
- {commkit-3.7.0 → commkit-3.7.1}/commkit/analysis/__init__.py +0 -0
- {commkit-3.7.0 → commkit-3.7.1}/commkit/analysis/_common.py +0 -0
- {commkit-3.7.0 → commkit-3.7.1}/commkit/analysis/allan.py +0 -0
- {commkit-3.7.0 → commkit-3.7.1}/commkit/analysis/drift.py +0 -0
- {commkit-3.7.0 → commkit-3.7.1}/commkit/analysis/fm_noise.py +0 -0
- {commkit-3.7.0 → commkit-3.7.1}/commkit/analysis/interferometry.py +0 -0
- {commkit-3.7.0 → commkit-3.7.1}/commkit/analysis/linewidth/__init__.py +0 -0
- {commkit-3.7.0 → commkit-3.7.1}/commkit/analysis/linewidth/dsh.py +0 -0
- {commkit-3.7.0 → commkit-3.7.1}/commkit/analysis/linewidth/phase.py +0 -0
- {commkit-3.7.0 → commkit-3.7.1}/commkit/analysis/linewidth/result.py +0 -0
- {commkit-3.7.0 → commkit-3.7.1}/commkit/analysis/trajectory.py +0 -0
- {commkit-3.7.0 → commkit-3.7.1}/commkit/backend.py +0 -0
- {commkit-3.7.0 → commkit-3.7.1}/commkit/coding/__init__.py +0 -0
- {commkit-3.7.0 → commkit-3.7.1}/commkit/coding/_galois.py +0 -0
- {commkit-3.7.0 → commkit-3.7.1}/commkit/coding/_gf2.py +0 -0
- {commkit-3.7.0 → commkit-3.7.1}/commkit/coding/_ldpc_tables.py +0 -0
- {commkit-3.7.0 → commkit-3.7.1}/commkit/coding/_numba_loops.py +0 -0
- {commkit-3.7.0 → commkit-3.7.1}/commkit/coding/algebraic.py +0 -0
- {commkit-3.7.0 → commkit-3.7.1}/commkit/coding/base.py +0 -0
- {commkit-3.7.0 → commkit-3.7.1}/commkit/coding/convolutional.py +0 -0
- {commkit-3.7.0 → commkit-3.7.1}/commkit/coding/crc.py +0 -0
- {commkit-3.7.0 → commkit-3.7.1}/commkit/coding/exit.py +0 -0
- {commkit-3.7.0 → commkit-3.7.1}/commkit/coding/interleaving.py +0 -0
- {commkit-3.7.0 → commkit-3.7.1}/commkit/coding/ldpc.py +0 -0
- {commkit-3.7.0 → commkit-3.7.1}/commkit/coding/linear.py +0 -0
- {commkit-3.7.0 → commkit-3.7.1}/commkit/coding/performance.py +0 -0
- {commkit-3.7.0 → commkit-3.7.1}/commkit/coding/polar.py +0 -0
- {commkit-3.7.0 → commkit-3.7.1}/commkit/coding/product.py +0 -0
- {commkit-3.7.0 → commkit-3.7.1}/commkit/coding/ratematch.py +0 -0
- {commkit-3.7.0 → commkit-3.7.1}/commkit/coding/shaping.py +0 -0
- {commkit-3.7.0 → commkit-3.7.1}/commkit/coding/turbo.py +0 -0
- {commkit-3.7.0 → commkit-3.7.1}/commkit/core/__init__.py +0 -0
- {commkit-3.7.0 → commkit-3.7.1}/commkit/core/_signal_adapter.py +0 -0
- {commkit-3.7.0 → commkit-3.7.1}/commkit/core/frame.py +0 -0
- {commkit-3.7.0 → commkit-3.7.1}/commkit/core/generation.py +0 -0
- {commkit-3.7.0 → commkit-3.7.1}/commkit/core/preamble.py +0 -0
- {commkit-3.7.0 → commkit-3.7.1}/commkit/core/signal.py +0 -0
- {commkit-3.7.0 → commkit-3.7.1}/commkit/equalization/__init__.py +0 -0
- {commkit-3.7.0 → commkit-3.7.1}/commkit/equalization/_block/__init__.py +0 -0
- {commkit-3.7.0 → commkit-3.7.1}/commkit/equalization/_block/_blind.py +0 -0
- {commkit-3.7.0 → commkit-3.7.1}/commkit/equalization/_block/_dd.py +0 -0
- {commkit-3.7.0 → commkit-3.7.1}/commkit/equalization/_block/_engine.py +0 -0
- {commkit-3.7.0 → commkit-3.7.1}/commkit/equalization/_common.py +0 -0
- {commkit-3.7.0 → commkit-3.7.1}/commkit/equalization/_numba_loops.py +0 -0
- {commkit-3.7.0 → commkit-3.7.1}/commkit/equalization/_sequential/__init__.py +0 -0
- {commkit-3.7.0 → commkit-3.7.1}/commkit/equalization/_sequential/_blind.py +0 -0
- {commkit-3.7.0 → commkit-3.7.1}/commkit/equalization/_sequential/_dd.py +0 -0
- {commkit-3.7.0 → commkit-3.7.1}/commkit/equalization/_sequential/_setup.py +0 -0
- {commkit-3.7.0 → commkit-3.7.1}/commkit/equalization/linear.py +0 -0
- {commkit-3.7.0 → commkit-3.7.1}/commkit/equalization/pilots.py +0 -0
- {commkit-3.7.0 → commkit-3.7.1}/commkit/equalization/polarization.py +0 -0
- {commkit-3.7.0 → commkit-3.7.1}/commkit/equalization/result.py +0 -0
- {commkit-3.7.0 → commkit-3.7.1}/commkit/filtering/__init__.py +0 -0
- {commkit-3.7.0 → commkit-3.7.1}/commkit/filtering/fiber.py +0 -0
- {commkit-3.7.0 → commkit-3.7.1}/commkit/filtering/linear.py +0 -0
- {commkit-3.7.0 → commkit-3.7.1}/commkit/filtering/pulses.py +0 -0
- {commkit-3.7.0 → commkit-3.7.1}/commkit/filtering/response.py +0 -0
- {commkit-3.7.0 → commkit-3.7.1}/commkit/frequency.py +0 -0
- {commkit-3.7.0 → commkit-3.7.1}/commkit/impairments/__init__.py +0 -0
- {commkit-3.7.0 → commkit-3.7.1}/commkit/impairments/fiber_channel.py +0 -0
- {commkit-3.7.0 → commkit-3.7.1}/commkit/impairments/frontend.py +0 -0
- {commkit-3.7.0 → commkit-3.7.1}/commkit/impairments/noise.py +0 -0
- {commkit-3.7.0 → commkit-3.7.1}/commkit/impairments/source.py +0 -0
- {commkit-3.7.0 → commkit-3.7.1}/commkit/io.py +0 -0
- {commkit-3.7.0 → commkit-3.7.1}/commkit/logger.py +0 -0
- {commkit-3.7.0 → commkit-3.7.1}/commkit/mapping/__init__.py +0 -0
- {commkit-3.7.0 → commkit-3.7.1}/commkit/mapping/_numba_llr.py +0 -0
- {commkit-3.7.0 → commkit-3.7.1}/commkit/mapping/bits.py +0 -0
- {commkit-3.7.0 → commkit-3.7.1}/commkit/mapping/constellation.py +0 -0
- {commkit-3.7.0 → commkit-3.7.1}/commkit/mapping/gray.py +0 -0
- {commkit-3.7.0 → commkit-3.7.1}/commkit/mapping/llr.py +0 -0
- {commkit-3.7.0 → commkit-3.7.1}/commkit/mapping/shaping.py +0 -0
- {commkit-3.7.0 → commkit-3.7.1}/commkit/math.py +0 -0
- {commkit-3.7.0 → commkit-3.7.1}/commkit/metrics/__init__.py +0 -0
- {commkit-3.7.0 → commkit-3.7.1}/commkit/metrics/_common.py +0 -0
- {commkit-3.7.0 → commkit-3.7.1}/commkit/metrics/awgn.py +0 -0
- {commkit-3.7.0 → commkit-3.7.1}/commkit/metrics/error_rates.py +0 -0
- {commkit-3.7.0 → commkit-3.7.1}/commkit/metrics/information.py +0 -0
- {commkit-3.7.0 → commkit-3.7.1}/commkit/metrics/quality.py +0 -0
- {commkit-3.7.0 → commkit-3.7.1}/commkit/multirate.py +0 -0
- {commkit-3.7.0 → commkit-3.7.1}/commkit/plotting/__init__.py +0 -0
- {commkit-3.7.0 → commkit-3.7.1}/commkit/plotting/analysis.py +0 -0
- {commkit-3.7.0 → commkit-3.7.1}/commkit/plotting/coding.py +0 -0
- {commkit-3.7.0 → commkit-3.7.1}/commkit/plotting/constellation.py +0 -0
- {commkit-3.7.0 → commkit-3.7.1}/commkit/plotting/equalizer.py +0 -0
- {commkit-3.7.0 → commkit-3.7.1}/commkit/plotting/eye.py +0 -0
- {commkit-3.7.0 → commkit-3.7.1}/commkit/plotting/filter_response.py +0 -0
- {commkit-3.7.0 → commkit-3.7.1}/commkit/plotting/frequency.py +0 -0
- {commkit-3.7.0 → commkit-3.7.1}/commkit/plotting/recovery.py +0 -0
- {commkit-3.7.0 → commkit-3.7.1}/commkit/plotting/spectral.py +0 -0
- {commkit-3.7.0 → commkit-3.7.1}/commkit/plotting/theme.py +0 -0
- {commkit-3.7.0 → commkit-3.7.1}/commkit/plotting/timing.py +0 -0
- {commkit-3.7.0 → commkit-3.7.1}/commkit/plotting/waveform.py +0 -0
- {commkit-3.7.0 → commkit-3.7.1}/commkit/py.typed +0 -0
- {commkit-3.7.0 → commkit-3.7.1}/commkit/recovery/__init__.py +0 -0
- {commkit-3.7.0 → commkit-3.7.1}/commkit/recovery/_common.py +0 -0
- {commkit-3.7.0 → commkit-3.7.1}/commkit/recovery/bps.py +0 -0
- {commkit-3.7.0 → commkit-3.7.1}/commkit/recovery/carrier_phase.py +0 -0
- {commkit-3.7.0 → commkit-3.7.1}/commkit/recovery/corrections.py +0 -0
- {commkit-3.7.0 → commkit-3.7.1}/commkit/recovery/pilots.py +0 -0
- {commkit-3.7.0 → commkit-3.7.1}/commkit/recovery/pll.py +0 -0
- {commkit-3.7.0 → commkit-3.7.1}/commkit/recovery/tikhonov.py +0 -0
- {commkit-3.7.0 → commkit-3.7.1}/commkit/recovery/viterbi_viterbi.py +0 -0
- {commkit-3.7.0 → commkit-3.7.1}/commkit/smoothing.py +0 -0
- {commkit-3.7.0 → commkit-3.7.1}/commkit/spectral.py +0 -0
- {commkit-3.7.0 → commkit-3.7.1}/commkit/timing/__init__.py +0 -0
- {commkit-3.7.0 → commkit-3.7.1}/commkit/timing/synchronization.py +0 -0
- {commkit-3.7.0 → commkit-3.7.1}/examples/00-getting-started.ipynb +0 -0
- {commkit-3.7.0 → commkit-3.7.1}/examples/01-constellations-and-bit-mapping.ipynb +0 -0
- {commkit-3.7.0 → commkit-3.7.1}/examples/02-spectral-analysis.ipynb +0 -0
- {commkit-3.7.0 → commkit-3.7.1}/examples/03-digital-filters.ipynb +0 -0
- {commkit-3.7.0 → commkit-3.7.1}/examples/04-pulse-shaping-and-matched-filtering.ipynb +0 -0
- {commkit-3.7.0 → commkit-3.7.1}/examples/05-multirate-and-resampling.ipynb +0 -0
- {commkit-3.7.0 → commkit-3.7.1}/examples/06-awgn-and-performance-metrics.ipynb +0 -0
- {commkit-3.7.0 → commkit-3.7.1}/examples/07-channel-coding.ipynb +0 -0
- {commkit-3.7.0 → commkit-3.7.1}/examples/08-probabilistic-shaping-and-coded-modulation.ipynb +0 -0
- {commkit-3.7.0 → commkit-3.7.1}/examples/09-laser-phase-noise.ipynb +0 -0
- {commkit-3.7.0 → commkit-3.7.1}/examples/11-frame-synchronization-and-timing.ipynb +0 -0
- {commkit-3.7.0 → commkit-3.7.1}/examples/13-frequency-offset-estimation.ipynb +0 -0
- {commkit-3.7.0 → commkit-3.7.1}/examples/14-blind-carrier-phase-recovery.ipynb +0 -0
- {commkit-3.7.0 → commkit-3.7.1}/examples/15-pilot-aided-carrier-recovery.ipynb +0 -0
- {commkit-3.7.0 → commkit-3.7.1}/examples/16-linear-channel-equalization.ipynb +0 -0
- {commkit-3.7.0 → commkit-3.7.1}/examples/17-adaptive-equalization.ipynb +0 -0
- {commkit-3.7.0 → commkit-3.7.1}/examples/18-blind-equalization.ipynb +0 -0
- {commkit-3.7.0 → commkit-3.7.1}/examples/19-polarization-multiplexing.ipynb +0 -0
- {commkit-3.7.0 → commkit-3.7.1}/examples/20-chromatic-dispersion.ipynb +0 -0
- {commkit-3.7.0 → commkit-3.7.1}/examples/21-nonlinear-fiber-and-backpropagation.ipynb +0 -0
- {commkit-3.7.0 → commkit-3.7.1}/examples/23-fec-in-optical-standards.ipynb +0 -0
- {commkit-3.7.0 → commkit-3.7.1}/examples/24-coherent-transceiver-end-to-end.ipynb +0 -0
- {commkit-3.7.0 → commkit-3.7.1}/examples/25-carrier-phase-characterization.ipynb +0 -0
- {commkit-3.7.0 → commkit-3.7.1}/examples/26-laser-linewidth-self-heterodyne.ipynb +0 -0
- {commkit-3.7.0 → commkit-3.7.1}/examples/27-laser-linewidth-short-delay-and-flicker.ipynb +0 -0
- {commkit-3.7.0 → commkit-3.7.1}/examples/28-laser-linewidth-self-homodyne-iq.ipynb +0 -0
- {commkit-3.7.0 → commkit-3.7.1}/examples/29-measurement-template-self-heterodyne.ipynb +0 -0
- {commkit-3.7.0 → commkit-3.7.1}/examples/30-measurement-template-self-homodyne-iq.ipynb +0 -0
- {commkit-3.7.0 → commkit-3.7.1}/scripts/sanitize.sh +0 -0
- {commkit-3.7.0 → commkit-3.7.1}/tests/analysis/test_allan.py +0 -0
- {commkit-3.7.0 → commkit-3.7.1}/tests/analysis/test_drift.py +0 -0
- {commkit-3.7.0 → commkit-3.7.1}/tests/analysis/test_fm_noise.py +0 -0
- {commkit-3.7.0 → commkit-3.7.1}/tests/analysis/test_interferometry.py +0 -0
- {commkit-3.7.0 → commkit-3.7.1}/tests/analysis/test_linewidth.py +0 -0
- {commkit-3.7.0 → commkit-3.7.1}/tests/analysis/test_trajectory.py +0 -0
- {commkit-3.7.0 → commkit-3.7.1}/tests/coding/test_algebraic.py +0 -0
- {commkit-3.7.0 → commkit-3.7.1}/tests/coding/test_coding_base.py +0 -0
- {commkit-3.7.0 → commkit-3.7.1}/tests/coding/test_convolutional.py +0 -0
- {commkit-3.7.0 → commkit-3.7.1}/tests/coding/test_crc.py +0 -0
- {commkit-3.7.0 → commkit-3.7.1}/tests/coding/test_exit.py +0 -0
- {commkit-3.7.0 → commkit-3.7.1}/tests/coding/test_gf2.py +0 -0
- {commkit-3.7.0 → commkit-3.7.1}/tests/coding/test_interleaving.py +0 -0
- {commkit-3.7.0 → commkit-3.7.1}/tests/coding/test_ldpc.py +0 -0
- {commkit-3.7.0 → commkit-3.7.1}/tests/coding/test_ldpc_exit.py +0 -0
- {commkit-3.7.0 → commkit-3.7.1}/tests/coding/test_ldpc_kernel.py +0 -0
- {commkit-3.7.0 → commkit-3.7.1}/tests/coding/test_ldpc_tables.py +0 -0
- {commkit-3.7.0 → commkit-3.7.1}/tests/coding/test_linear_codes.py +0 -0
- {commkit-3.7.0 → commkit-3.7.1}/tests/coding/test_performance.py +0 -0
- {commkit-3.7.0 → commkit-3.7.1}/tests/coding/test_polar.py +0 -0
- {commkit-3.7.0 → commkit-3.7.1}/tests/coding/test_product.py +0 -0
- {commkit-3.7.0 → commkit-3.7.1}/tests/coding/test_ratematch.py +0 -0
- {commkit-3.7.0 → commkit-3.7.1}/tests/coding/test_shaping.py +0 -0
- {commkit-3.7.0 → commkit-3.7.1}/tests/coding/test_turbo.py +0 -0
- {commkit-3.7.0 → commkit-3.7.1}/tests/common/__init__.py +0 -0
- {commkit-3.7.0 → commkit-3.7.1}/tests/common/coding.py +0 -0
- {commkit-3.7.0 → commkit-3.7.1}/tests/common/conversions.py +0 -0
- {commkit-3.7.0 → commkit-3.7.1}/tests/common/kernel_utils.py +0 -0
- {commkit-3.7.0 → commkit-3.7.1}/tests/common/memory.py +0 -0
- {commkit-3.7.0 → commkit-3.7.1}/tests/common/metrics.py +0 -0
- {commkit-3.7.0 → commkit-3.7.1}/tests/common/periodic.py +0 -0
- {commkit-3.7.0 → commkit-3.7.1}/tests/common/scoring.py +0 -0
- {commkit-3.7.0 → commkit-3.7.1}/tests/common/signals.py +0 -0
- {commkit-3.7.0 → commkit-3.7.1}/tests/common/theory.py +0 -0
- {commkit-3.7.0 → commkit-3.7.1}/tests/conftest.py +0 -0
- {commkit-3.7.0 → commkit-3.7.1}/tests/core/test_frame.py +0 -0
- {commkit-3.7.0 → commkit-3.7.1}/tests/core/test_generation.py +0 -0
- {commkit-3.7.0 → commkit-3.7.1}/tests/core/test_preamble.py +0 -0
- {commkit-3.7.0 → commkit-3.7.1}/tests/core/test_psqam.py +0 -0
- {commkit-3.7.0 → commkit-3.7.1}/tests/core/test_signal.py +0 -0
- {commkit-3.7.0 → commkit-3.7.1}/tests/core/test_signal_adapter.py +0 -0
- {commkit-3.7.0 → commkit-3.7.1}/tests/core/test_signal_mimo.py +0 -0
- {commkit-3.7.0 → commkit-3.7.1}/tests/equalization/test_blind.py +0 -0
- {commkit-3.7.0 → commkit-3.7.1}/tests/equalization/test_block.py +0 -0
- {commkit-3.7.0 → commkit-3.7.1}/tests/equalization/test_bps_kernel.py +0 -0
- {commkit-3.7.0 → commkit-3.7.1}/tests/equalization/test_cold_start.py +0 -0
- {commkit-3.7.0 → commkit-3.7.1}/tests/equalization/test_cpr.py +0 -0
- {commkit-3.7.0 → commkit-3.7.1}/tests/equalization/test_cs_kernel.py +0 -0
- {commkit-3.7.0 → commkit-3.7.1}/tests/equalization/test_equalizer_oracles.py +0 -0
- {commkit-3.7.0 → commkit-3.7.1}/tests/equalization/test_linear.py +0 -0
- {commkit-3.7.0 → commkit-3.7.1}/tests/equalization/test_mimo.py +0 -0
- {commkit-3.7.0 → commkit-3.7.1}/tests/equalization/test_polarization.py +0 -0
- {commkit-3.7.0 → commkit-3.7.1}/tests/equalization/test_result.py +0 -0
- {commkit-3.7.0 → commkit-3.7.1}/tests/equalization/test_sequential.py +0 -0
- {commkit-3.7.0 → commkit-3.7.1}/tests/equalization/test_state.py +0 -0
- {commkit-3.7.0 → commkit-3.7.1}/tests/equalization/test_winit.py +0 -0
- {commkit-3.7.0 → commkit-3.7.1}/tests/impairments/test_fiber_channel_linear.py +0 -0
- {commkit-3.7.0 → commkit-3.7.1}/tests/impairments/test_fiber_channel_nonlinear.py +0 -0
- {commkit-3.7.0 → commkit-3.7.1}/tests/impairments/test_frontend.py +0 -0
- {commkit-3.7.0 → commkit-3.7.1}/tests/impairments/test_noise.py +0 -0
- {commkit-3.7.0 → commkit-3.7.1}/tests/impairments/test_source.py +0 -0
- {commkit-3.7.0 → commkit-3.7.1}/tests/mapping/test_bits.py +0 -0
- {commkit-3.7.0 → commkit-3.7.1}/tests/mapping/test_constellation.py +0 -0
- {commkit-3.7.0 → commkit-3.7.1}/tests/mapping/test_gray.py +0 -0
- {commkit-3.7.0 → commkit-3.7.1}/tests/mapping/test_llr.py +0 -0
- {commkit-3.7.0 → commkit-3.7.1}/tests/metrics/test_awgn.py +0 -0
- {commkit-3.7.0 → commkit-3.7.1}/tests/metrics/test_common.py +0 -0
- {commkit-3.7.0 → commkit-3.7.1}/tests/metrics/test_error_rates.py +0 -0
- {commkit-3.7.0 → commkit-3.7.1}/tests/metrics/test_information.py +0 -0
- {commkit-3.7.0 → commkit-3.7.1}/tests/metrics/test_quality.py +0 -0
- {commkit-3.7.0 → commkit-3.7.1}/tests/plotting/test_plot_analysis.py +0 -0
- {commkit-3.7.0 → commkit-3.7.1}/tests/plotting/test_plot_coding.py +0 -0
- {commkit-3.7.0 → commkit-3.7.1}/tests/plotting/test_plot_constellation.py +0 -0
- {commkit-3.7.0 → commkit-3.7.1}/tests/plotting/test_plot_equalizer.py +0 -0
- {commkit-3.7.0 → commkit-3.7.1}/tests/plotting/test_plot_eye.py +0 -0
- {commkit-3.7.0 → commkit-3.7.1}/tests/plotting/test_plot_filter_response.py +0 -0
- {commkit-3.7.0 → commkit-3.7.1}/tests/plotting/test_plot_frequency.py +0 -0
- {commkit-3.7.0 → commkit-3.7.1}/tests/plotting/test_plot_recovery.py +0 -0
- {commkit-3.7.0 → commkit-3.7.1}/tests/plotting/test_plot_spectral.py +0 -0
- {commkit-3.7.0 → commkit-3.7.1}/tests/plotting/test_plot_theme.py +0 -0
- {commkit-3.7.0 → commkit-3.7.1}/tests/plotting/test_plot_timing.py +0 -0
- {commkit-3.7.0 → commkit-3.7.1}/tests/plotting/test_plot_waveform.py +0 -0
- {commkit-3.7.0 → commkit-3.7.1}/tests/plugins/__init__.py +0 -0
- {commkit-3.7.0 → commkit-3.7.1}/tests/plugins/precision_trace.py +0 -0
- {commkit-3.7.0 → commkit-3.7.1}/tests/plugins/transfer_trace.py +0 -0
- {commkit-3.7.0 → commkit-3.7.1}/tests/recovery/test_bps.py +0 -0
- {commkit-3.7.0 → commkit-3.7.1}/tests/recovery/test_corrections.py +0 -0
- {commkit-3.7.0 → commkit-3.7.1}/tests/recovery/test_joint.py +0 -0
- {commkit-3.7.0 → commkit-3.7.1}/tests/recovery/test_pilots.py +0 -0
- {commkit-3.7.0 → commkit-3.7.1}/tests/recovery/test_pll.py +0 -0
- {commkit-3.7.0 → commkit-3.7.1}/tests/recovery/test_recovery_oracles.py +0 -0
- {commkit-3.7.0 → commkit-3.7.1}/tests/recovery/test_tikhonov.py +0 -0
- {commkit-3.7.0 → commkit-3.7.1}/tests/recovery/test_viterbi_viterbi.py +0 -0
- {commkit-3.7.0 → commkit-3.7.1}/tests/test_api_contracts.py +0 -0
- {commkit-3.7.0 → commkit-3.7.1}/tests/test_array.py +0 -0
- {commkit-3.7.0 → commkit-3.7.1}/tests/test_backend.py +0 -0
- {commkit-3.7.0 → commkit-3.7.1}/tests/test_cuda_infra.py +0 -0
- {commkit-3.7.0 → commkit-3.7.1}/tests/test_dispersion.py +0 -0
- {commkit-3.7.0 → commkit-3.7.1}/tests/test_examples.py +0 -0
- {commkit-3.7.0 → commkit-3.7.1}/tests/test_fiber.py +0 -0
- {commkit-3.7.0 → commkit-3.7.1}/tests/test_filter_response.py +0 -0
- {commkit-3.7.0 → commkit-3.7.1}/tests/test_filtering.py +0 -0
- {commkit-3.7.0 → commkit-3.7.1}/tests/test_frequency.py +0 -0
- {commkit-3.7.0 → commkit-3.7.1}/tests/test_import_hygiene.py +0 -0
- {commkit-3.7.0 → commkit-3.7.1}/tests/test_io.py +0 -0
- {commkit-3.7.0 → commkit-3.7.1}/tests/test_logger.py +0 -0
- {commkit-3.7.0 → commkit-3.7.1}/tests/test_math.py +0 -0
- {commkit-3.7.0 → commkit-3.7.1}/tests/test_multirate.py +0 -0
- {commkit-3.7.0 → commkit-3.7.1}/tests/test_pulse_shaping.py +0 -0
- {commkit-3.7.0 → commkit-3.7.1}/tests/test_receiver_chain.py +0 -0
- {commkit-3.7.0 → commkit-3.7.1}/tests/test_signal_pipeline.py +0 -0
- {commkit-3.7.0 → commkit-3.7.1}/tests/test_smoothing.py +0 -0
- {commkit-3.7.0 → commkit-3.7.1}/tests/test_spectral.py +0 -0
- {commkit-3.7.0 → commkit-3.7.1}/tests/test_working_memory.py +0 -0
- {commkit-3.7.0 → commkit-3.7.1}/tests/timing/test_synchronization.py +0 -0
|
@@ -4,6 +4,27 @@ Notable changes to CommKit, newest first. Performance figures are from the refer
|
|
|
4
4
|
|
|
5
5
|
## [Unreleased]
|
|
6
6
|
|
|
7
|
+
## [3.7.1] - 2026-11-10
|
|
8
|
+
|
|
9
|
+
Gardner clock recovery no longer runs away when its loop is too wide: the integrator is bounded to plausible clock offsets, and slips are reported. The notebooks on clock recovery, direct and coherent detection and the ADC now introduce their topics with figures.
|
|
10
|
+
|
|
11
|
+
### Added
|
|
12
|
+
|
|
13
|
+
- `timing.Gardner(max_offset_ppm=1000.0)`: the largest clock offset the loop tracks. The loop's integrator is held within it.
|
|
14
|
+
- A WARNING when Gardner clock recovery slips whole symbols (the timing moves by more than 0.75 symbol within `8 / (B·T)` symbols), after which output symbols no longer match the transmitted ones in order. A second WARNING when the estimated offset sits at `max_offset_ppm`.
|
|
15
|
+
|
|
16
|
+
### Performance
|
|
17
|
+
|
|
18
|
+
- Gardner clock recovery checks its trajectory for slips after the loop: 1.01 to 1.04 of the former time (paired runs, 2^17 symbols).
|
|
19
|
+
|
|
20
|
+
### Fixed
|
|
21
|
+
|
|
22
|
+
- A Gardner loop too wide for its detector's noise, such as the classical detector at `normalized_bandwidth=1e-2` on a 0.1-rolloff signal, no longer runs away in frequency without notice. Once it slipped, nothing pulled its integrator back, and it reached tens of thousands of ppm on a 50 ppm clock, with thousands of extra symbols in the corrected output. The bounded loop keeps returning to lock and reports the slips.
|
|
23
|
+
|
|
24
|
+
### Development
|
|
25
|
+
|
|
26
|
+
- Notebooks 12 (sampling-clock recovery), 22 (direct and self-coherent detection, now also coherent detection and its LO gain) and the skew and ADC sections of 10 explain their topics with figures: the drifting eye, how the Gardner detector tells early from late, loop bandwidth and cycle slips, PAM-4 eyes and sensitivity, thermal against shot noise, laser RIN, balanced photodiodes, quantization and jitter.
|
|
27
|
+
|
|
7
28
|
## [3.7.0] - 2026-11-10
|
|
8
29
|
|
|
9
30
|
The receiver's analog front end and the detector, at absolute power. The ADC clock, its jitter and quantizer, IQ skew and the photodiode join the impairments, with Gardner clock recovery as the inverse of the clock; coherent and square-law detection turn received and LO powers into the noise they give. Closed-form AWGN error rates and information rates sit next to the measured ones. Blind decision-directed equalizers no longer lock falsely on unipolar PAM and dense QAM, because their input scaling hands the slicer the right gain.
|
|
@@ -503,7 +524,8 @@ See the [GitHub release](https://github.com/lokgar/commkit/releases/tag/v1.1.0)
|
|
|
503
524
|
|
|
504
525
|
First release. See the [GitHub release](https://github.com/lokgar/commkit/releases/tag/v1.0.0).
|
|
505
526
|
|
|
506
|
-
[Unreleased]: https://github.com/lokgar/commkit/compare/v3.7.
|
|
527
|
+
[Unreleased]: https://github.com/lokgar/commkit/compare/v3.7.1...HEAD
|
|
528
|
+
[3.7.1]: https://github.com/lokgar/commkit/compare/v3.7.0...v3.7.1
|
|
507
529
|
[3.7.0]: https://github.com/lokgar/commkit/compare/v3.6.1...v3.7.0
|
|
508
530
|
[3.6.1]: https://github.com/lokgar/commkit/compare/v3.6.0...v3.6.1
|
|
509
531
|
[3.6.0]: https://github.com/lokgar/commkit/compare/v3.5.0...v3.6.0
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: commkit
|
|
3
|
-
Version: 3.7.
|
|
3
|
+
Version: 3.7.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
|
|
@@ -111,9 +111,10 @@ def _clamp_step(step, sps):
|
|
|
111
111
|
|
|
112
112
|
|
|
113
113
|
@_step
|
|
114
|
-
def _track(x, c0, c1, pad, sps, p0, mu, beta, power, n_valid, pos, err):
|
|
114
|
+
def _track(x, c0, c1, pad, sps, p0, mu, beta, nu_max, power, n_valid, pos, err):
|
|
115
115
|
"""One loop over channels ``c0 .. c1 - 1`` (their mean error drives it);
|
|
116
|
-
fills ``pos`` and ``err`` and returns the number of strobes.
|
|
116
|
+
fills ``pos`` and ``err`` and returns the number of strobes. The
|
|
117
|
+
integrator stays within ``±nu_max``."""
|
|
117
118
|
nu = 0.0
|
|
118
119
|
e_last = 0.0
|
|
119
120
|
p = p0
|
|
@@ -131,14 +132,14 @@ def _track(x, c0, c1, pad, sps, p0, mu, beta, power, n_valid, pos, err):
|
|
|
131
132
|
err[k] = e
|
|
132
133
|
k += 1
|
|
133
134
|
step = _clamp_step(sps * (1.0 + nu + mu * e_last), sps)
|
|
134
|
-
nu
|
|
135
|
+
nu = min(max(nu + beta * e_last, -nu_max), nu_max)
|
|
135
136
|
e_last = e
|
|
136
137
|
p += step
|
|
137
138
|
return k
|
|
138
139
|
|
|
139
140
|
|
|
140
141
|
@numba.njit(cache=True, fastmath=True, nogil=True, parallel=True)
|
|
141
|
-
def gardner_loop(x, pad, sps, p0, mu, beta, power, joint, n_valid, num_out):
|
|
142
|
+
def gardner_loop(x, pad, sps, p0, mu, beta, nu_max, power, joint, n_valid, num_out):
|
|
142
143
|
"""Track the symbol strobes of every channel (or one joint loop).
|
|
143
144
|
|
|
144
145
|
Parameters
|
|
@@ -155,6 +156,9 @@ def gardner_loop(x, pad, sps, p0, mu, beta, power, joint, n_valid, num_out):
|
|
|
155
156
|
mu, beta : numpy.ndarray
|
|
156
157
|
Proportional and integral gains per row, float64: the strobe spacing
|
|
157
158
|
is ``sps · (1 + ν + μ·e)`` and ``ν += β·e``.
|
|
159
|
+
nu_max : float
|
|
160
|
+
Bound of the integrator ``|ν|``, the largest clock offset tracked
|
|
161
|
+
(fraction, not ppm).
|
|
158
162
|
power, joint : bool
|
|
159
163
|
Power detector; one loop driven by the mean error of all channels.
|
|
160
164
|
n_valid : int
|
|
@@ -180,6 +184,18 @@ def gardner_loop(x, pad, sps, p0, mu, beta, power, joint, n_valid, num_out):
|
|
|
180
184
|
c0 = 0 if joint else r
|
|
181
185
|
c1 = C if joint else r + 1
|
|
182
186
|
count[r] = _track(
|
|
183
|
-
x,
|
|
187
|
+
x,
|
|
188
|
+
c0,
|
|
189
|
+
c1,
|
|
190
|
+
pad,
|
|
191
|
+
sps,
|
|
192
|
+
p0[r],
|
|
193
|
+
mu[r],
|
|
194
|
+
beta[r],
|
|
195
|
+
nu_max,
|
|
196
|
+
power,
|
|
197
|
+
n_valid,
|
|
198
|
+
pos[r],
|
|
199
|
+
err[r],
|
|
184
200
|
)
|
|
185
201
|
return pos, err, count
|
|
@@ -40,6 +40,12 @@ _CAL_SYMBOLS = 2048
|
|
|
40
40
|
# Below this first-harmonic amplitude of the S-curve (unit-power input) the
|
|
41
41
|
# detector sees no clock tone.
|
|
42
42
|
_MIN_CURVE_AMPLITUDE = 1e-3
|
|
43
|
+
# Cycle-slip check: the timing may not move by more than _SLIP_THRESHOLD
|
|
44
|
+
# symbol periods, beyond the record's mean drift, within _SLIP_WINDOW loop
|
|
45
|
+
# time constants 1/(B·T). The loop's own jitter stays below 0.5 symbol over
|
|
46
|
+
# that window up to the bandwidths where slips begin; a slip moves it by 1.
|
|
47
|
+
_SLIP_WINDOW = 8.0
|
|
48
|
+
_SLIP_THRESHOLD = 0.75
|
|
43
49
|
|
|
44
50
|
|
|
45
51
|
def _get_numba_gardner() -> Any:
|
|
@@ -74,8 +80,10 @@ class Gardner:
|
|
|
74
80
|
``β = 4(BT)² / K_d``, with the detector gain ``K_d`` measured on the
|
|
75
81
|
input (see Notes). Used when ``proportional_gain`` is not given.
|
|
76
82
|
The detector's self-noise sets the timing jitter: 0.01 symbol rms at
|
|
77
|
-
``1e-3`` for 16-QAM, 0.
|
|
78
|
-
|
|
83
|
+
``1e-3`` for 16-QAM (classical detector, rolloff 0.1), 0.006 at
|
|
84
|
+
``1e-4``. Too wide a loop slips whole symbols: the classical
|
|
85
|
+
detector at a rolloff of 0.1 from ``1e-2`` on, the power detector
|
|
86
|
+
at 0.1 from ``2e-2`` and at 0.5 from ``3e-3`` (16-QAM, 20 dB).
|
|
79
87
|
proportional_gain : float, optional
|
|
80
88
|
Raw proportional gain ``μ`` on the detector output of the input
|
|
81
89
|
scaled to unit power; overrides ``normalized_bandwidth``.
|
|
@@ -83,6 +91,12 @@ class Gardner:
|
|
|
83
91
|
Raw integral gain ``β``; requires ``proportional_gain``. Defaults
|
|
84
92
|
to 0 (a first-order loop, which keeps a timing lag ``ε / μK_d``
|
|
85
93
|
under a clock offset) with ``proportional_gain``.
|
|
94
|
+
max_offset_ppm : float, default 1000.0
|
|
95
|
+
Largest clock offset the loop tracks, in ppm, in ``(0, 90000)``:
|
|
96
|
+
the integrator ``ν`` is held within ``±max_offset_ppm · 1e-6``.
|
|
97
|
+
Crystal clocks are within about 100 ppm. The bound keeps a loop
|
|
98
|
+
that has lost lock from wandering off in frequency, which it
|
|
99
|
+
otherwise does without limit (see Notes).
|
|
86
100
|
joint_channels : bool, default False
|
|
87
101
|
MIMO: one loop driven by the mean detector output of all channels,
|
|
88
102
|
for channels sampled by one clock.
|
|
@@ -109,6 +123,19 @@ class Gardner:
|
|
|
109
123
|
pulse) while the power detector's stays at 0.34. The strobe spacing is
|
|
110
124
|
held within ±10 % of ``sps``.
|
|
111
125
|
|
|
126
|
+
A loop that slips leaves the S-curve's stable point, and while its
|
|
127
|
+
timing runs through the symbols the mean detector output is zero:
|
|
128
|
+
nothing pulls the integrator back, and its noise walks it away.
|
|
129
|
+
Unbounded, a classical loop at ``B·T = 1e-2`` on a 0.1-rolloff signal
|
|
130
|
+
reached tens of thousands of ppm on a 50 ppm clock; held within
|
|
131
|
+
``max_offset_ppm`` it keeps returning to lock. Two checks log a WARNING after the loop: the
|
|
132
|
+
timing moving by more than 0.75 symbol within ``8 / (B·T)`` symbols,
|
|
133
|
+
beyond the record's mean drift (a cycle slip, after which symbol ``k``
|
|
134
|
+
of the output is no longer the ``k``-th transmitted one), and an
|
|
135
|
+
estimated offset within 5 % of ``max_offset_ppm`` (a larger offset,
|
|
136
|
+
which the proportional path follows only with a static timing lag, or
|
|
137
|
+
a loop that lost lock). ``B·T`` is ``μ K_d / 4`` for raw gains.
|
|
138
|
+
|
|
112
139
|
Numba-compiled on the CPU; GPU input makes one host round trip.
|
|
113
140
|
|
|
114
141
|
References
|
|
@@ -120,6 +147,7 @@ class Gardner:
|
|
|
120
147
|
normalized_bandwidth: float = 1e-3
|
|
121
148
|
proportional_gain: float | None = None
|
|
122
149
|
integral_gain: float | None = None
|
|
150
|
+
max_offset_ppm: float = 1000.0
|
|
123
151
|
joint_channels: bool = False
|
|
124
152
|
method: Literal["classical", "power"] = "classical"
|
|
125
153
|
|
|
@@ -130,6 +158,11 @@ class Gardner:
|
|
|
130
158
|
f"Gardner: normalized_bandwidth must be in (0, 0.5) (a fraction "
|
|
131
159
|
f"of the symbol rate), got {bt}."
|
|
132
160
|
)
|
|
161
|
+
if not (0.0 < self.max_offset_ppm < 9e4):
|
|
162
|
+
raise ValueError(
|
|
163
|
+
"Gardner: max_offset_ppm must be in (0, 90000), got "
|
|
164
|
+
f"{self.max_offset_ppm}."
|
|
165
|
+
)
|
|
133
166
|
if self.integral_gain is not None and self.proportional_gain is None:
|
|
134
167
|
raise ValueError("Gardner: integral_gain requires proportional_gain.")
|
|
135
168
|
for name in ("proportional_gain", "integral_gain"):
|
|
@@ -224,6 +257,7 @@ def _track(
|
|
|
224
257
|
pad + tau0 * sps,
|
|
225
258
|
np.ascontiguousarray(gains[:, 0]),
|
|
226
259
|
np.ascontiguousarray(gains[:, 1]),
|
|
260
|
+
how.max_offset_ppm * 1e-6,
|
|
227
261
|
power,
|
|
228
262
|
rows != C,
|
|
229
263
|
n,
|
|
@@ -232,6 +266,7 @@ def _track(
|
|
|
232
266
|
k = int(count.min())
|
|
233
267
|
pos = pos[:, :k] - pad
|
|
234
268
|
err = err[:, :k]
|
|
269
|
+
_check_lock(pos / sps - np.arange(k), gains[:, 0] * kd / 4.0, how)
|
|
235
270
|
if rows != C:
|
|
236
271
|
pos = np.repeat(pos, C, axis=0)
|
|
237
272
|
err = np.repeat(err, C, axis=0)
|
|
@@ -239,6 +274,47 @@ def _track(
|
|
|
239
274
|
return pos, kd, err
|
|
240
275
|
|
|
241
276
|
|
|
277
|
+
def _first_slip(trajectory: np.ndarray, bandwidth: np.ndarray) -> np.ndarray:
|
|
278
|
+
"""Per row, the first symbol where the timing moves by more than
|
|
279
|
+
_SLIP_THRESHOLD within _SLIP_WINDOW / bandwidth symbols beyond the
|
|
280
|
+
row's median drift over that window; -1 where it never does."""
|
|
281
|
+
k = trajectory.shape[-1]
|
|
282
|
+
first = np.full(trajectory.shape[0], -1, dtype=np.int64)
|
|
283
|
+
for r, bt in enumerate(bandwidth):
|
|
284
|
+
if not bt > 0.0:
|
|
285
|
+
continue
|
|
286
|
+
span = int(max(64.0, _SLIP_WINDOW / bt))
|
|
287
|
+
if span >= k:
|
|
288
|
+
continue
|
|
289
|
+
moved = trajectory[r, span:] - trajectory[r, :-span]
|
|
290
|
+
bad = np.abs(moved - np.median(moved)) > _SLIP_THRESHOLD
|
|
291
|
+
if bad.any():
|
|
292
|
+
first[r] = int(np.argmax(bad))
|
|
293
|
+
return first
|
|
294
|
+
|
|
295
|
+
|
|
296
|
+
def _check_lock(trajectory: np.ndarray, bandwidth: np.ndarray, how: Gardner) -> None:
|
|
297
|
+
"""Log a WARNING for cycle slips and for an offset at the loop's bound."""
|
|
298
|
+
first = _first_slip(trajectory, bandwidth)
|
|
299
|
+
if np.any(first >= 0):
|
|
300
|
+
logger.warning(
|
|
301
|
+
"Gardner clock recovery slipped whole symbols (first near symbol "
|
|
302
|
+
"%d): after a slip, output symbols no longer match the transmitted "
|
|
303
|
+
"ones in order. Use a smaller normalized_bandwidth (B·T = %s).",
|
|
304
|
+
int(first[first >= 0].min()),
|
|
305
|
+
", ".join(f"{bt:.1e}" for bt in np.unique(bandwidth)),
|
|
306
|
+
)
|
|
307
|
+
ppm = _offset_ppm(trajectory)
|
|
308
|
+
if np.any(np.abs(ppm) > 0.95 * how.max_offset_ppm):
|
|
309
|
+
logger.warning(
|
|
310
|
+
"Gardner clock recovery: the offset %s ppm is at the loop's bound "
|
|
311
|
+
"max_offset_ppm=%g: the loop lost lock, or the clock is further off and "
|
|
312
|
+
"the timing lags; raise max_offset_ppm.",
|
|
313
|
+
np.array2string(ppm, precision=1),
|
|
314
|
+
how.max_offset_ppm,
|
|
315
|
+
)
|
|
316
|
+
|
|
317
|
+
|
|
242
318
|
def _offset_ppm(trajectory: np.ndarray) -> np.ndarray:
|
|
243
319
|
"""Least-squares slope of the trajectory over its last three quarters,
|
|
244
320
|
in ppm."""
|