commkit 3.1.0__tar.gz → 3.1.2__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.
Files changed (285) hide show
  1. {commkit-3.1.0 → commkit-3.1.2}/AGENTS.md +9 -5
  2. {commkit-3.1.0 → commkit-3.1.2}/CHANGELOG.md +61 -0
  3. {commkit-3.1.0 → commkit-3.1.2}/PKG-INFO +6 -6
  4. {commkit-3.1.0 → commkit-3.1.2}/README.md +5 -5
  5. {commkit-3.1.0 → commkit-3.1.2}/benchmarks/bench_coding.py +6 -6
  6. {commkit-3.1.0 → commkit-3.1.2}/benchmarks/bench_fiber.py +1 -0
  7. {commkit-3.1.0 → commkit-3.1.2}/benchmarks/bench_signal_pipeline.py +1 -1
  8. {commkit-3.1.0 → commkit-3.1.2}/commkit/__init__.py +1 -1
  9. commkit-3.1.2/commkit/_constants.py +3 -0
  10. {commkit-3.1.0 → commkit-3.1.2}/commkit/_dispersion.py +1 -3
  11. {commkit-3.1.0 → commkit-3.1.2}/commkit/_sequences.py +34 -34
  12. {commkit-3.1.0 → commkit-3.1.2}/commkit/analysis/_common.py +2 -2
  13. {commkit-3.1.0 → commkit-3.1.2}/commkit/analysis/allan.py +6 -6
  14. {commkit-3.1.0 → commkit-3.1.2}/commkit/analysis/linewidth/dsh.py +4 -4
  15. {commkit-3.1.0 → commkit-3.1.2}/commkit/analysis/linewidth/phase.py +8 -6
  16. {commkit-3.1.0 → commkit-3.1.2}/commkit/analysis/linewidth/result.py +4 -4
  17. {commkit-3.1.0 → commkit-3.1.2}/commkit/coding/algebraic.py +9 -9
  18. {commkit-3.1.0 → commkit-3.1.2}/commkit/coding/convolutional.py +23 -16
  19. {commkit-3.1.0 → commkit-3.1.2}/commkit/coding/crc.py +16 -16
  20. {commkit-3.1.0 → commkit-3.1.2}/commkit/coding/ldpc.py +13 -13
  21. {commkit-3.1.0 → commkit-3.1.2}/commkit/coding/polar.py +6 -6
  22. {commkit-3.1.0 → commkit-3.1.2}/commkit/coding/product.py +33 -36
  23. {commkit-3.1.0 → commkit-3.1.2}/commkit/coding/shaping.py +26 -28
  24. {commkit-3.1.0 → commkit-3.1.2}/commkit/coding/turbo.py +31 -22
  25. {commkit-3.1.0 → commkit-3.1.2}/commkit/core/_signal_adapter.py +27 -0
  26. {commkit-3.1.0 → commkit-3.1.2}/commkit/core/frame.py +121 -73
  27. {commkit-3.1.0 → commkit-3.1.2}/commkit/core/generation.py +3 -3
  28. {commkit-3.1.0 → commkit-3.1.2}/commkit/core/signal.py +13 -5
  29. {commkit-3.1.0 → commkit-3.1.2}/commkit/equalization/__init__.py +11 -13
  30. {commkit-3.1.0 → commkit-3.1.2}/commkit/equalization/blind.py +19 -111
  31. {commkit-3.1.0 → commkit-3.1.2}/commkit/equalization/linear.py +46 -44
  32. commkit-3.1.2/commkit/equalization/pilots.py +115 -0
  33. {commkit-3.1.0 → commkit-3.1.2}/commkit/equalization/polarization.py +247 -381
  34. {commkit-3.1.0 → commkit-3.1.2}/commkit/equalization/sequential/_blind.py +38 -71
  35. {commkit-3.1.0 → commkit-3.1.2}/commkit/equalization/sequential/_dd.py +29 -25
  36. {commkit-3.1.0 → commkit-3.1.2}/commkit/filtering/__init__.py +2 -14
  37. {commkit-3.1.0 → commkit-3.1.2}/commkit/filtering/fiber.py +16 -9
  38. {commkit-3.1.0 → commkit-3.1.2}/commkit/filtering/linear.py +26 -24
  39. {commkit-3.1.0 → commkit-3.1.2}/commkit/filtering/pulses.py +40 -48
  40. {commkit-3.1.0 → commkit-3.1.2}/commkit/frequency.py +41 -1
  41. {commkit-3.1.0 → commkit-3.1.2}/commkit/impairments/__init__.py +13 -3
  42. {commkit-3.1.0 → commkit-3.1.2}/commkit/impairments/channel/__init__.py +3 -1
  43. {commkit-3.1.0 → commkit-3.1.2}/commkit/impairments/channel/linear.py +37 -23
  44. {commkit-3.1.0 → commkit-3.1.2}/commkit/impairments/channel/nonlinear.py +7 -3
  45. {commkit-3.1.0 → commkit-3.1.2}/commkit/impairments/frontend.py +150 -21
  46. commkit-3.1.2/commkit/impairments/noise.py +229 -0
  47. {commkit-3.1.0 → commkit-3.1.2}/commkit/mapping/__init__.py +3 -3
  48. {commkit-3.1.0 → commkit-3.1.2}/commkit/mapping/bits.py +5 -5
  49. {commkit-3.1.0 → commkit-3.1.2}/commkit/metrics.py +160 -37
  50. {commkit-3.1.0 → commkit-3.1.2}/commkit/plotting/analysis.py +1 -1
  51. {commkit-3.1.0 → commkit-3.1.2}/commkit/plotting/equalizer.py +3 -3
  52. {commkit-3.1.0 → commkit-3.1.2}/commkit/plotting/filter_response.py +6 -6
  53. {commkit-3.1.0 → commkit-3.1.2}/commkit/plotting/spectral.py +2 -2
  54. {commkit-3.1.0 → commkit-3.1.2}/commkit/plotting/sync.py +11 -11
  55. {commkit-3.1.0 → commkit-3.1.2}/commkit/recovery/carrier_phase.py +4 -3
  56. {commkit-3.1.0 → commkit-3.1.2}/commkit/recovery/pilots.py +32 -1
  57. {commkit-3.1.0 → commkit-3.1.2}/commkit/recovery/pll.py +15 -10
  58. {commkit-3.1.0 → commkit-3.1.2}/commkit/recovery/tikhonov.py +11 -10
  59. {commkit-3.1.0 → commkit-3.1.2}/commkit/spectral.py +1 -1
  60. {commkit-3.1.0 → commkit-3.1.2}/commkit/timing.py +8 -5
  61. {commkit-3.1.0 → commkit-3.1.2}/examples/01-constellations-and-bit-mapping.ipynb +2 -2
  62. {commkit-3.1.0 → commkit-3.1.2}/examples/03-digital-filters.ipynb +9 -7
  63. {commkit-3.1.0 → commkit-3.1.2}/examples/06-awgn-and-performance-metrics.ipynb +1 -1
  64. {commkit-3.1.0 → commkit-3.1.2}/examples/07-channel-coding.ipynb +2 -2
  65. {commkit-3.1.0 → commkit-3.1.2}/examples/08-probabilistic-shaping-and-coded-modulation.ipynb +2 -2
  66. {commkit-3.1.0 → commkit-3.1.2}/examples/10-front-end-impairments.ipynb +16 -10
  67. {commkit-3.1.0 → commkit-3.1.2}/examples/11-frame-synchronization-and-timing.ipynb +4 -1
  68. {commkit-3.1.0 → commkit-3.1.2}/examples/13-blind-carrier-phase-recovery.ipynb +3 -3
  69. {commkit-3.1.0 → commkit-3.1.2}/examples/14-pilot-aided-carrier-recovery.ipynb +2 -2
  70. {commkit-3.1.0 → commkit-3.1.2}/examples/15-linear-channel-equalization.ipynb +15 -15
  71. {commkit-3.1.0 → commkit-3.1.2}/examples/16-adaptive-equalization.ipynb +2 -2
  72. {commkit-3.1.0 → commkit-3.1.2}/examples/18-polarization-multiplexing.ipynb +5 -8
  73. {commkit-3.1.0 → commkit-3.1.2}/examples/20-nonlinear-fiber-and-backpropagation.ipynb +3 -1
  74. {commkit-3.1.0 → commkit-3.1.2}/examples/22-coherent-transceiver-end-to-end.ipynb +19 -10
  75. {commkit-3.1.0 → commkit-3.1.2}/examples/23-carrier-phase-characterization.ipynb +1 -1
  76. {commkit-3.1.0 → commkit-3.1.2}/examples/24-laser-linewidth-self-heterodyne.ipynb +4 -4
  77. {commkit-3.1.0 → commkit-3.1.2}/examples/25-laser-linewidth-short-delay-and-flicker.ipynb +2 -2
  78. {commkit-3.1.0 → commkit-3.1.2}/examples/26-laser-linewidth-self-homodyne-iq.ipynb +2 -2
  79. {commkit-3.1.0 → commkit-3.1.2}/examples/27-measurement-template-self-heterodyne.ipynb +1 -1
  80. {commkit-3.1.0 → commkit-3.1.2}/examples/28-measurement-template-self-homodyne-iq.ipynb +1 -1
  81. {commkit-3.1.0 → commkit-3.1.2}/pyproject.toml +2 -2
  82. {commkit-3.1.0 → commkit-3.1.2}/tests/analysis/test_allan.py +2 -2
  83. {commkit-3.1.0 → commkit-3.1.2}/tests/analysis/test_linewidth.py +4 -4
  84. {commkit-3.1.0 → commkit-3.1.2}/tests/coding/test_convolutional.py +7 -2
  85. {commkit-3.1.0 → commkit-3.1.2}/tests/coding/test_crc.py +1 -1
  86. {commkit-3.1.0 → commkit-3.1.2}/tests/coding/test_ldpc.py +1 -1
  87. {commkit-3.1.0 → commkit-3.1.2}/tests/coding/test_ldpc_kernel.py +3 -3
  88. {commkit-3.1.0 → commkit-3.1.2}/tests/coding/test_ldpc_tables.py +1 -1
  89. {commkit-3.1.0 → commkit-3.1.2}/tests/coding/test_polar.py +9 -9
  90. {commkit-3.1.0 → commkit-3.1.2}/tests/coding/test_product.py +3 -3
  91. {commkit-3.1.0 → commkit-3.1.2}/tests/coding/test_shaping.py +16 -16
  92. {commkit-3.1.0 → commkit-3.1.2}/tests/coding/test_turbo.py +5 -3
  93. {commkit-3.1.0 → commkit-3.1.2}/tests/common/reference_impl.py +13 -5
  94. {commkit-3.1.0 → commkit-3.1.2}/tests/core/test_frame.py +67 -33
  95. {commkit-3.1.0 → commkit-3.1.2}/tests/core/test_generation.py +2 -2
  96. {commkit-3.1.0 → commkit-3.1.2}/tests/core/test_signal.py +7 -7
  97. {commkit-3.1.0 → commkit-3.1.2}/tests/core/test_signal_mimo.py +13 -13
  98. {commkit-3.1.0 → commkit-3.1.2}/tests/equalization/test_blind.py +5 -9
  99. {commkit-3.1.0 → commkit-3.1.2}/tests/equalization/test_cpr.py +5 -5
  100. {commkit-3.1.0 → commkit-3.1.2}/tests/equalization/test_equalizer_oracles.py +4 -4
  101. {commkit-3.1.0 → commkit-3.1.2}/tests/equalization/test_linear.py +20 -18
  102. {commkit-3.1.0 → commkit-3.1.2}/tests/equalization/test_polarization.py +89 -110
  103. {commkit-3.1.0 → commkit-3.1.2}/tests/equalization/test_sequential.py +43 -63
  104. {commkit-3.1.0 → commkit-3.1.2}/tests/equalization/test_state.py +9 -7
  105. {commkit-3.1.0 → commkit-3.1.2}/tests/impairments/channel/test_channel_linear.py +56 -4
  106. {commkit-3.1.0 → commkit-3.1.2}/tests/impairments/channel/test_channel_nonlinear.py +20 -2
  107. {commkit-3.1.0 → commkit-3.1.2}/tests/impairments/test_frontend.py +76 -0
  108. commkit-3.1.2/tests/impairments/test_noise.py +244 -0
  109. {commkit-3.1.0 → commkit-3.1.2}/tests/mapping/test_bits.py +10 -12
  110. {commkit-3.1.0 → commkit-3.1.2}/tests/mapping/test_llr.py +1 -1
  111. {commkit-3.1.0 → commkit-3.1.2}/tests/plotting/test_plot_analysis.py +1 -1
  112. {commkit-3.1.0 → commkit-3.1.2}/tests/plotting/test_plot_equalizer.py +2 -2
  113. {commkit-3.1.0 → commkit-3.1.2}/tests/plotting/test_plot_filter_response.py +3 -3
  114. {commkit-3.1.0 → commkit-3.1.2}/tests/plotting/test_plot_sync.py +4 -4
  115. {commkit-3.1.0 → commkit-3.1.2}/tests/recovery/test_corrections.py +3 -2
  116. {commkit-3.1.0 → commkit-3.1.2}/tests/recovery/test_joint.py +2 -3
  117. {commkit-3.1.0 → commkit-3.1.2}/tests/recovery/test_pilots.py +29 -4
  118. {commkit-3.1.0 → commkit-3.1.2}/tests/recovery/test_pll.py +7 -3
  119. {commkit-3.1.0 → commkit-3.1.2}/tests/recovery/test_tikhonov.py +36 -15
  120. {commkit-3.1.0 → commkit-3.1.2}/tests/recovery/test_viterbi_viterbi.py +6 -2
  121. {commkit-3.1.0 → commkit-3.1.2}/tests/test_api_contracts.py +18 -16
  122. {commkit-3.1.0 → commkit-3.1.2}/tests/test_dispersion.py +1 -1
  123. {commkit-3.1.0 → commkit-3.1.2}/tests/test_fiber.py +9 -0
  124. {commkit-3.1.0 → commkit-3.1.2}/tests/test_filtering.py +45 -33
  125. {commkit-3.1.0 → commkit-3.1.2}/tests/test_frequency.py +20 -0
  126. {commkit-3.1.0 → commkit-3.1.2}/tests/test_io.py +3 -3
  127. {commkit-3.1.0 → commkit-3.1.2}/tests/test_metrics.py +26 -0
  128. {commkit-3.1.0 → commkit-3.1.2}/tests/test_pulse_shaping.py +0 -30
  129. {commkit-3.1.0 → commkit-3.1.2}/tests/test_signal_pipeline.py +5 -5
  130. {commkit-3.1.0 → commkit-3.1.2}/tests/test_timing.py +7 -4
  131. {commkit-3.1.0 → commkit-3.1.2}/tests/test_working_memory.py +1 -1
  132. {commkit-3.1.0 → commkit-3.1.2}/uv.lock +1 -1
  133. commkit-3.1.0/commkit/impairments/noise.py +0 -108
  134. commkit-3.1.0/tests/impairments/test_noise.py +0 -97
  135. {commkit-3.1.0 → commkit-3.1.2}/.gitattributes +0 -0
  136. {commkit-3.1.0 → commkit-3.1.2}/.github/workflows/ci.yml +0 -0
  137. {commkit-3.1.0 → commkit-3.1.2}/.github/workflows/publish.yml +0 -0
  138. {commkit-3.1.0 → commkit-3.1.2}/.gitignore +0 -0
  139. {commkit-3.1.0 → commkit-3.1.2}/.python-version +0 -0
  140. {commkit-3.1.0 → commkit-3.1.2}/LICENSE +0 -0
  141. {commkit-3.1.0 → commkit-3.1.2}/benchmarks/baselines/Linux-CPython-3.14-64bit/0002_pre_v2.json +0 -0
  142. {commkit-3.1.0 → commkit-3.1.2}/benchmarks/baselines/Linux-CPython-3.14-64bit/0003_v2_0.json +0 -0
  143. {commkit-3.1.0 → commkit-3.1.2}/benchmarks/baselines/Linux-CPython-3.14-64bit/0004_fiber_dbp.json +0 -0
  144. {commkit-3.1.0 → commkit-3.1.2}/benchmarks/baselines/Linux-CPython-3.14-64bit/0005_gpu_decoder_path.json +0 -0
  145. {commkit-3.1.0 → commkit-3.1.2}/benchmarks/baselines/Linux-CPython-3.14-64bit/0006_perf_cleanup.json +0 -0
  146. {commkit-3.1.0 → commkit-3.1.2}/benchmarks/baselines/Linux-CPython-3.14-64bit/0007_v3_0.json +0 -0
  147. {commkit-3.1.0 → commkit-3.1.2}/benchmarks/baselines/Linux-CPython-3.14-64bit/0008_v3_1.json +0 -0
  148. {commkit-3.1.0 → commkit-3.1.2}/benchmarks/bench_block_blind.py +0 -0
  149. {commkit-3.1.0 → commkit-3.1.2}/benchmarks/bench_block_lms.py +0 -0
  150. {commkit-3.1.0 → commkit-3.1.2}/benchmarks/bench_bps.py +0 -0
  151. {commkit-3.1.0 → commkit-3.1.2}/benchmarks/bench_equalizers.py +0 -0
  152. {commkit-3.1.0 → commkit-3.1.2}/benchmarks/bench_filters.py +0 -0
  153. {commkit-3.1.0 → commkit-3.1.2}/benchmarks/bench_import.py +0 -0
  154. {commkit-3.1.0 → commkit-3.1.2}/benchmarks/bench_llr.py +0 -0
  155. {commkit-3.1.0 → commkit-3.1.2}/benchmarks/bench_mapping.py +0 -0
  156. {commkit-3.1.0 → commkit-3.1.2}/benchmarks/bench_sync_misc.py +0 -0
  157. {commkit-3.1.0 → commkit-3.1.2}/benchmarks/benchutils.py +0 -0
  158. {commkit-3.1.0 → commkit-3.1.2}/benchmarks/conftest.py +0 -0
  159. {commkit-3.1.0 → commkit-3.1.2}/benchmarks/record_baseline.py +0 -0
  160. {commkit-3.1.0 → commkit-3.1.2}/benchmarks/workloads.py +0 -0
  161. {commkit-3.1.0 → commkit-3.1.2}/commkit/_array.py +0 -0
  162. {commkit-3.1.0 → commkit-3.1.2}/commkit/_circular.py +0 -0
  163. {commkit-3.1.0 → commkit-3.1.2}/commkit/_cuda/__init__.py +0 -0
  164. {commkit-3.1.0 → commkit-3.1.2}/commkit/_cuda/compiler.py +0 -0
  165. {commkit-3.1.0 → commkit-3.1.2}/commkit/_cuda/graphs.py +0 -0
  166. {commkit-3.1.0 → commkit-3.1.2}/commkit/_cuda/src/algebraic.cu +0 -0
  167. {commkit-3.1.0 → commkit-3.1.2}/commkit/_cuda/src/bps_anchor.cu +0 -0
  168. {commkit-3.1.0 → commkit-3.1.2}/commkit/_cuda/src/bps_min_d2.cu +0 -0
  169. {commkit-3.1.0 → commkit-3.1.2}/commkit/_cuda/src/cs_block.cu +0 -0
  170. {commkit-3.1.0 → commkit-3.1.2}/commkit/_cuda/src/iir.cu +0 -0
  171. {commkit-3.1.0 → commkit-3.1.2}/commkit/_cuda/src/ldpc.cu +0 -0
  172. {commkit-3.1.0 → commkit-3.1.2}/commkit/_cuda/src/llr.cu +0 -0
  173. {commkit-3.1.0 → commkit-3.1.2}/commkit/_cuda/src/selftest.cu +0 -0
  174. {commkit-3.1.0 → commkit-3.1.2}/commkit/_fft.py +0 -0
  175. {commkit-3.1.0 → commkit-3.1.2}/commkit/_fiber.py +0 -0
  176. {commkit-3.1.0 → commkit-3.1.2}/commkit/_overlap_save.py +0 -0
  177. {commkit-3.1.0 → commkit-3.1.2}/commkit/_random.py +0 -0
  178. {commkit-3.1.0 → commkit-3.1.2}/commkit/analysis/__init__.py +0 -0
  179. {commkit-3.1.0 → commkit-3.1.2}/commkit/analysis/drift.py +0 -0
  180. {commkit-3.1.0 → commkit-3.1.2}/commkit/analysis/fm_noise.py +0 -0
  181. {commkit-3.1.0 → commkit-3.1.2}/commkit/analysis/interferometry.py +0 -0
  182. {commkit-3.1.0 → commkit-3.1.2}/commkit/analysis/linewidth/__init__.py +0 -0
  183. {commkit-3.1.0 → commkit-3.1.2}/commkit/analysis/trajectory.py +0 -0
  184. {commkit-3.1.0 → commkit-3.1.2}/commkit/backend.py +0 -0
  185. {commkit-3.1.0 → commkit-3.1.2}/commkit/coding/__init__.py +0 -0
  186. {commkit-3.1.0 → commkit-3.1.2}/commkit/coding/_galois.py +0 -0
  187. {commkit-3.1.0 → commkit-3.1.2}/commkit/coding/_gf2.py +0 -0
  188. {commkit-3.1.0 → commkit-3.1.2}/commkit/coding/_ldpc_tables.py +0 -0
  189. {commkit-3.1.0 → commkit-3.1.2}/commkit/coding/_numba_loops.py +0 -0
  190. {commkit-3.1.0 → commkit-3.1.2}/commkit/coding/base.py +0 -0
  191. {commkit-3.1.0 → commkit-3.1.2}/commkit/coding/interleaving.py +0 -0
  192. {commkit-3.1.0 → commkit-3.1.2}/commkit/coding/linear.py +0 -0
  193. {commkit-3.1.0 → commkit-3.1.2}/commkit/coding/ratematch.py +0 -0
  194. {commkit-3.1.0 → commkit-3.1.2}/commkit/core/__init__.py +0 -0
  195. {commkit-3.1.0 → commkit-3.1.2}/commkit/equalization/_block/__init__.py +0 -0
  196. {commkit-3.1.0 → commkit-3.1.2}/commkit/equalization/_block/_blind.py +0 -0
  197. {commkit-3.1.0 → commkit-3.1.2}/commkit/equalization/_block/_dd.py +0 -0
  198. {commkit-3.1.0 → commkit-3.1.2}/commkit/equalization/_block/_engine.py +0 -0
  199. {commkit-3.1.0 → commkit-3.1.2}/commkit/equalization/_common.py +0 -0
  200. {commkit-3.1.0 → commkit-3.1.2}/commkit/equalization/_kernels_numba.py +0 -0
  201. {commkit-3.1.0 → commkit-3.1.2}/commkit/equalization/_numba_loops.py +0 -0
  202. {commkit-3.1.0 → commkit-3.1.2}/commkit/equalization/result.py +0 -0
  203. {commkit-3.1.0 → commkit-3.1.2}/commkit/equalization/sequential/__init__.py +0 -0
  204. {commkit-3.1.0 → commkit-3.1.2}/commkit/equalization/sequential/_setup.py +0 -0
  205. {commkit-3.1.0 → commkit-3.1.2}/commkit/impairments/source.py +0 -0
  206. {commkit-3.1.0 → commkit-3.1.2}/commkit/io.py +0 -0
  207. {commkit-3.1.0 → commkit-3.1.2}/commkit/logger.py +0 -0
  208. {commkit-3.1.0 → commkit-3.1.2}/commkit/mapping/_numba_llr.py +0 -0
  209. {commkit-3.1.0 → commkit-3.1.2}/commkit/mapping/constellation.py +0 -0
  210. {commkit-3.1.0 → commkit-3.1.2}/commkit/mapping/gray.py +0 -0
  211. {commkit-3.1.0 → commkit-3.1.2}/commkit/mapping/llr.py +0 -0
  212. {commkit-3.1.0 → commkit-3.1.2}/commkit/mapping/shaping.py +0 -0
  213. {commkit-3.1.0 → commkit-3.1.2}/commkit/math.py +0 -0
  214. {commkit-3.1.0 → commkit-3.1.2}/commkit/multirate.py +0 -0
  215. {commkit-3.1.0 → commkit-3.1.2}/commkit/plotting/__init__.py +0 -0
  216. {commkit-3.1.0 → commkit-3.1.2}/commkit/plotting/constellation.py +0 -0
  217. {commkit-3.1.0 → commkit-3.1.2}/commkit/plotting/eye.py +0 -0
  218. {commkit-3.1.0 → commkit-3.1.2}/commkit/plotting/theme.py +0 -0
  219. {commkit-3.1.0 → commkit-3.1.2}/commkit/plotting/waveform.py +0 -0
  220. {commkit-3.1.0 → commkit-3.1.2}/commkit/py.typed +0 -0
  221. {commkit-3.1.0 → commkit-3.1.2}/commkit/recovery/__init__.py +0 -0
  222. {commkit-3.1.0 → commkit-3.1.2}/commkit/recovery/_common.py +0 -0
  223. {commkit-3.1.0 → commkit-3.1.2}/commkit/recovery/bps.py +0 -0
  224. {commkit-3.1.0 → commkit-3.1.2}/commkit/recovery/corrections.py +0 -0
  225. {commkit-3.1.0 → commkit-3.1.2}/commkit/recovery/viterbi_viterbi.py +0 -0
  226. {commkit-3.1.0 → commkit-3.1.2}/commkit/smoothing.py +0 -0
  227. {commkit-3.1.0 → commkit-3.1.2}/examples/00-getting-started.ipynb +0 -0
  228. {commkit-3.1.0 → commkit-3.1.2}/examples/02-spectral-analysis.ipynb +0 -0
  229. {commkit-3.1.0 → commkit-3.1.2}/examples/04-pulse-shaping-and-matched-filtering.ipynb +0 -0
  230. {commkit-3.1.0 → commkit-3.1.2}/examples/05-multirate-and-resampling.ipynb +0 -0
  231. {commkit-3.1.0 → commkit-3.1.2}/examples/09-laser-phase-noise.ipynb +0 -0
  232. {commkit-3.1.0 → commkit-3.1.2}/examples/12-frequency-offset-estimation.ipynb +0 -0
  233. {commkit-3.1.0 → commkit-3.1.2}/examples/17-blind-equalization.ipynb +0 -0
  234. {commkit-3.1.0 → commkit-3.1.2}/examples/19-chromatic-dispersion.ipynb +0 -0
  235. {commkit-3.1.0 → commkit-3.1.2}/examples/21-fec-in-optical-standards.ipynb +0 -0
  236. {commkit-3.1.0 → commkit-3.1.2}/examples/README.md +0 -0
  237. {commkit-3.1.0 → commkit-3.1.2}/tests/analysis/test_drift.py +0 -0
  238. {commkit-3.1.0 → commkit-3.1.2}/tests/analysis/test_fm_noise.py +0 -0
  239. {commkit-3.1.0 → commkit-3.1.2}/tests/analysis/test_interferometry.py +0 -0
  240. {commkit-3.1.0 → commkit-3.1.2}/tests/analysis/test_trajectory.py +0 -0
  241. {commkit-3.1.0 → commkit-3.1.2}/tests/coding/test_algebraic.py +0 -0
  242. {commkit-3.1.0 → commkit-3.1.2}/tests/coding/test_coding_base.py +0 -0
  243. {commkit-3.1.0 → commkit-3.1.2}/tests/coding/test_gf2.py +0 -0
  244. {commkit-3.1.0 → commkit-3.1.2}/tests/coding/test_interleaving.py +0 -0
  245. {commkit-3.1.0 → commkit-3.1.2}/tests/coding/test_linear_codes.py +0 -0
  246. {commkit-3.1.0 → commkit-3.1.2}/tests/coding/test_ratematch.py +0 -0
  247. {commkit-3.1.0 → commkit-3.1.2}/tests/common/__init__.py +0 -0
  248. {commkit-3.1.0 → commkit-3.1.2}/tests/common/coding.py +0 -0
  249. {commkit-3.1.0 → commkit-3.1.2}/tests/common/conversions.py +0 -0
  250. {commkit-3.1.0 → commkit-3.1.2}/tests/common/kernel_utils.py +0 -0
  251. {commkit-3.1.0 → commkit-3.1.2}/tests/common/memory.py +0 -0
  252. {commkit-3.1.0 → commkit-3.1.2}/tests/common/metrics.py +0 -0
  253. {commkit-3.1.0 → commkit-3.1.2}/tests/common/periodic.py +0 -0
  254. {commkit-3.1.0 → commkit-3.1.2}/tests/common/signals.py +0 -0
  255. {commkit-3.1.0 → commkit-3.1.2}/tests/conftest.py +0 -0
  256. {commkit-3.1.0 → commkit-3.1.2}/tests/core/test_psqam.py +0 -0
  257. {commkit-3.1.0 → commkit-3.1.2}/tests/core/test_signal_adapter.py +0 -0
  258. {commkit-3.1.0 → commkit-3.1.2}/tests/equalization/test_block.py +0 -0
  259. {commkit-3.1.0 → commkit-3.1.2}/tests/equalization/test_bps_kernel.py +0 -0
  260. {commkit-3.1.0 → commkit-3.1.2}/tests/equalization/test_cs_kernel.py +0 -0
  261. {commkit-3.1.0 → commkit-3.1.2}/tests/equalization/test_mimo.py +0 -0
  262. {commkit-3.1.0 → commkit-3.1.2}/tests/equalization/test_winit.py +0 -0
  263. {commkit-3.1.0 → commkit-3.1.2}/tests/impairments/test_source.py +0 -0
  264. {commkit-3.1.0 → commkit-3.1.2}/tests/mapping/test_constellation.py +0 -0
  265. {commkit-3.1.0 → commkit-3.1.2}/tests/mapping/test_gray.py +0 -0
  266. {commkit-3.1.0 → commkit-3.1.2}/tests/plotting/test_plot_constellation.py +0 -0
  267. {commkit-3.1.0 → commkit-3.1.2}/tests/plotting/test_plot_eye.py +0 -0
  268. {commkit-3.1.0 → commkit-3.1.2}/tests/plotting/test_plot_spectral.py +0 -0
  269. {commkit-3.1.0 → commkit-3.1.2}/tests/plotting/test_plot_theme.py +0 -0
  270. {commkit-3.1.0 → commkit-3.1.2}/tests/plotting/test_plot_waveform.py +0 -0
  271. {commkit-3.1.0 → commkit-3.1.2}/tests/plugins/__init__.py +0 -0
  272. {commkit-3.1.0 → commkit-3.1.2}/tests/plugins/precision_trace.py +0 -0
  273. {commkit-3.1.0 → commkit-3.1.2}/tests/plugins/transfer_trace.py +0 -0
  274. {commkit-3.1.0 → commkit-3.1.2}/tests/recovery/test_bps.py +0 -0
  275. {commkit-3.1.0 → commkit-3.1.2}/tests/recovery/test_recovery_oracles.py +0 -0
  276. {commkit-3.1.0 → commkit-3.1.2}/tests/test_array.py +0 -0
  277. {commkit-3.1.0 → commkit-3.1.2}/tests/test_backend.py +0 -0
  278. {commkit-3.1.0 → commkit-3.1.2}/tests/test_cuda_infra.py +0 -0
  279. {commkit-3.1.0 → commkit-3.1.2}/tests/test_examples.py +0 -0
  280. {commkit-3.1.0 → commkit-3.1.2}/tests/test_import_hygiene.py +0 -0
  281. {commkit-3.1.0 → commkit-3.1.2}/tests/test_logger.py +0 -0
  282. {commkit-3.1.0 → commkit-3.1.2}/tests/test_math.py +0 -0
  283. {commkit-3.1.0 → commkit-3.1.2}/tests/test_multirate.py +0 -0
  284. {commkit-3.1.0 → commkit-3.1.2}/tests/test_smoothing.py +0 -0
  285. {commkit-3.1.0 → commkit-3.1.2}/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 (`rrc_taps(sps=2, rolloff=0.1, span=10)`). The data-argument count is recorded per function in the contract registry.
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 `rrc_taps` and `fir_filter`, or `butterworth_sos` and `iir_filter`.
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`, `num_blocks` (code blocks, codewords of a FEC code), `num_channels`, `sampling_rate`, `symbol_rate`.
206
- - Units are in the docstring. Use `_db` and `_hz` suffixes where the unit is ambiguous.
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,67 @@ Notable changes to CommKit, newest first. Performance figures are from the refer
4
4
 
5
5
  ## [Unreleased]
6
6
 
7
+ ## [3.1.2] - 2026-10-29
8
+
9
+ `apply_awgn` without hidden assumptions about what a channel is: Es/N0 holds per channel, and the OSNR counts the ASE of the modes the medium guides instead of assuming the two polarizations of a single-mode fiber.
10
+
11
+ ### Changes that alter results
12
+
13
+ - **`apply_awgn(esn0_db=)` gives every channel the requested Es/N0.** The default noise level comes from each channel's own mean power; it used to come from the mean over all channels, so with unequal channel powers a stronger channel got a better Es/N0 than requested and a weaker one a worse. Equal powers give the same result as before. Pass a scalar `signal_power` for the old behavior, one absolute noise level shared by every channel.
14
+ - **`apply_awgn(osnr_db=)` counts the ASE of `noise_modes` modes, `max(C, 2)` by default.** One or two channels are unchanged (both polarizations of a single-mode fiber). More channels, such as the modes of a few-mode fiber or the cores of a multicore fiber, each count as a mode; they used to share the ASE of two polarizations, which put C/2 times too much noise on each.
15
+
16
+ ### Added
17
+
18
+ - `apply_awgn(noise_modes=)` for a medium that guides modes without signal, e.g. `noise_modes=6` for four channels in a three-mode fiber.
19
+ - `apply_awgn(signal_power=)` accepts one power per channel, `(C,)`.
20
+
21
+ ## [3.1.1] - 2026-10-29
22
+
23
+ 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.
24
+
25
+ ### Changes that alter results
26
+
27
+ - **`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.
28
+ - **`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.
29
+ - **`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`.
30
+ - **`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.
31
+ - **One exact speed of light** (299 792 458 m/s): dispersion used 2.998e8, which moves chromatic-dispersion results by 7e-5 relative.
32
+
33
+ ### Changes that may need code updates
34
+
35
+ - **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`.
36
+ - **`num_streams` is `num_channels`** in `SingleCarrierFrame`, `Preamble` and `Signal`.
37
+ - **One name per concept**:
38
+ - `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`.
39
+ - `nfft` in `estimate_transfer_function` (was `n_fft`) and `ols_fir_filter` (was `fft_size`).
40
+ - `regularization` in `estimate_transfer_function` (was `reg`) and `rls` (was `delta`).
41
+ - `zf_equalizer(center_tap=)` (was `center`); `ols_fir_filter(causal=False)` replaces `center=True`.
42
+ - `num_train_symbols` and `num_impulse_samples` in the equalizer plots (were `n_train`, `n_impulse`).
43
+ - `HardIterations(max_iterations=)` (was `iterations`, though it stops early); `BCJR` and `TurboIterations` take `method="maxlog" | "exact"` like `compute_llr` (was `exact=False | True`).
44
+ - `barker_sequence` and `zadoff_chu_sequence` take `num_symbols` like `Preamble` (was `length`).
45
+ - `AllanDeviation.taus` matches `allan_deviation(taus=)` (was `tau_s`); `LinewidthEstimate.num_segments` (was `n_segments`).
46
+ - **`estimate_transfer_function(received, reference)`**: the data under test first, as everywhere else (was `reference, received`).
47
+ - **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.
48
+ - **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`.
49
+ - **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`.
50
+ - **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.
51
+ - **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.
52
+
53
+ ### Added
54
+
55
+ - **`Signal.center_wavelength_nm`**, the wavelength of an optical Signal's carrier.
56
+ - **`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.
57
+ - **`EqualizerPilots.from_frame`, `PilotAided.from_frame` and `PilotSymbols.from_frame`** take the pilot positions, symbols and gain from a `SingleCarrierFrame`.
58
+ - **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.
59
+ - **`estimate_noise_variance`**, the data-aided noise variance that `compute_llr`, `gmi` and `mi` take, without the round trip through `snr`.
60
+ - **`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.
61
+ - `SingleCarrierFrame.pilot_mask` and `payload_mask`, the symbol masks over the whole frame.
62
+
63
+ ### Fixed
64
+
65
+ - `apply_awgn` rejects a non-positive `reference_bandwidth`.
66
+ - `zadoff_chu_sequence`'s length error names the argument.
67
+
7
68
  ## [3.1.0] - 2026-10-29
8
69
 
9
70
  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.0
3
+ Version: 3.1.2
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 `center_frequency`. 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.
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 streams), `extract_payload`. |
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 (with 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`. |
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`, polarization-tone demultiplexing. |
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 `center_frequency`. 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.
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 streams), `extract_payload`. |
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 (with 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`. |
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`, polarization-tone demultiplexing. |
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("exact", [False, True], ids=["maxlog", "exact"])
118
- def bench_turbo_decode(benchmark, backend_device, xp, sync, exact):
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, exact=exact), 32, 0.0)
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), block_length=20_000)
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)))
@@ -17,6 +17,7 @@ from commkit.impairments import Fiber, apply_fiber_propagation
17
17
  ROUNDS = dict(rounds=3, warmup_rounds=1, iterations=1)
18
18
  FS = 64e9
19
19
  LINK = dict(
20
+ center_wavelength_nm=1550.0,
20
21
  fiber=Fiber(),
21
22
  span_length_km=80.0,
22
23
  num_spans=4,
@@ -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
- payload_seed=42,
36
+ payload_rng=42,
37
37
  preamble=Preamble(sequence_type="barker", num_symbols=13),
38
38
  pilot_pattern="comb",
39
39
  pilot_period=32,
@@ -31,7 +31,7 @@ Matplotlib, change warning filters, or touch the GPU.
31
31
  import importlib
32
32
  from typing import Any
33
33
 
34
- __version__ = "3.1.0"
34
+ __version__ = "3.1.2"
35
35
 
36
36
  from .backend import PrecisionWarning
37
37
  from .core import (
@@ -0,0 +1,3 @@
1
+ """Physical constants shared across the package."""
2
+
3
+ SPEED_OF_LIGHT = 299_792_458.0 # m/s, exact (SI definition)
@@ -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(length: int) -> np.ndarray:
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
- length : {2, 3, 4, 5, 7, 11, 13}
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), ``(length,)``; move them with
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 the requested length is not a valid Barker length.
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 length not in _BARKER_SEQUENCES:
52
+ if num_symbols not in _BARKER_SEQUENCES:
53
53
  valid = sorted(_BARKER_SEQUENCES.keys())
54
- raise ValueError(f"No Barker sequence of length {length}. Valid: {valid}")
54
+ raise ValueError(f"No Barker sequence of length {num_symbols}. Valid: {valid}")
55
55
 
56
- seq = np.array(_BARKER_SEQUENCES[length], dtype="float32")
56
+ seq = np.array(_BARKER_SEQUENCES[num_symbols], dtype="float32")
57
57
 
58
- logger.debug("Generated Barker-%s sequence.", length)
58
+ logger.debug("Generated Barker-%s sequence.", num_symbols)
59
59
  return seq
60
60
 
61
61
 
62
- def zadoff_chu_sequence(length: int, *, root: int = 1) -> np.ndarray:
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
- length : int
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, length)``, coprime to ``length`` (not
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, ``(length,)``.
82
+ Host complex64 symbols of unit magnitude, ``(num_symbols,)``.
83
83
 
84
84
  Raises
85
85
  ------
86
86
  ValueError
87
- If ``length < 1`` or ``root`` is outside ``[1, length)``.
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 length < 1:
95
- raise ValueError("Length must be positive.")
96
- if root < 1 or root >= length:
97
- raise ValueError(f"Root must be in [1, {length - 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(length)
103
- if length % 2 == 0:
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 / length)
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) / length)
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: length=%s, root=%s.", length, root)
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(stream_idx: int, base_root: int, length: int) -> int:
117
- """Return the Zadoff-Chu root for TX stream ``stream_idx`` in a MIMO preamble.
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 TX stream by cycling through
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, length-1]``. For prime ``length`` all roots are valid CAZAC
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(length)`` at every lag.
123
+ magnitude ``1/sqrt(num_symbols)`` at every lag.
124
124
 
125
125
  Parameters
126
126
  ----------
127
- stream_idx : int
128
- TX stream index (0-based).
127
+ channel_index : int
128
+ Channel index (0-based).
129
129
  base_root : int
130
- ZC root assigned to stream 0. Must be in ``[1, length-1]``.
131
- length : int
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 stream ``stream_idx``, guaranteed in ``[1, length-1]``.
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 + stream_idx) % (length - 1)) + 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
- n_segments : int
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
- n_segments : int
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
- tau_s : numpy.ndarray
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
- tau_s: np.ndarray
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
- ``tau_s`` and ``adev``, ``(n_tau,)`` (SISO) or ``(C, n_tau)`` (MIMO),
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
- tau_s = ms * tau0
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
- tau_s_cpu = np.asarray(tau_s, dtype=np.float64)
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(tau_s=tau_s_cpu, adev=adev_out)
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 ``awgn_var``).
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
- n_segments=k_seg,
477
+ num_segments=k_seg,
478
478
  flicker=to_report_scalar(theta[:, 1], was_1d=was_1d),
479
- awgn_var=to_report_scalar(theta[:, 2], was_1d=was_1d),
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
- awgn_var=to_report_scalar(intercept, was_1d=was_1d),
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
- awgn_var=to_report_scalar(intercept, was_1d=was_1d),
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
- awgn_var = sigma_n2 * xp.mean(pair_mean, axis=-1)
288
+ angle_noise_var = sigma_n2 * xp.mean(pair_mean, axis=-1)
289
289
  else:
290
- awgn_var = sigma_n2.copy()
291
- linewidth = xp.maximum(var1 - awgn_var, 0.0) / (2.0 * np.pi * t_sym)
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
- awgn_var=to_report_scalar(to_device(awgn_var, "cpu"), was_1d=was_1d),
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
- n_segments=k_seg,
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,