@@ -275,6 +275,44 @@ class SDMEC : public details::SameTypeProcessor<SDMEC<T>, T> {
275275 */
276276 T operator ()(const T& in) noexcept ;
277277
278+ /* *
279+ * @brief Calculate channel capacity in bits per symbol
280+ * @return Channel capacity C in bits per symbol
281+ *
282+ * Computes the Shannon capacity of the SDMEC channel using the formula for q-ary
283+ * symmetric channels with optional erasures. The capacity represents the maximum
284+ * achievable rate of reliable communication through this channel.
285+ *
286+ * **Mathematical Formula**:
287+ *
288+ * For errors-only channel (px = 0):
289+ * - C = log₂(q) + (1-pe)·log₂(1-pe) + pe·log₂(pe) - pe·log₂(q-1)
290+ *
291+ * For errors-and-erasures channel:
292+ * - C = (1-px) · C_error
293+ * - where C_error uses the conditional error probability pe/(1-px)
294+ *
295+ * For erasures-only channel:
296+ * - C = (1-px)·log₂(q)
297+ *
298+ * **Implementation Notes**:
299+ * - Uses internal error probability pe/(1-px) from error_dist.p()
300+ * - Handles edge cases pe=0 and pe=1 using limit: lim_{x→0} x·log₂(x) = 0
301+ * - For CECCO_ERASURE_SUPPORT, scales result by (1-px) to account for erasures
302+ * - Returns capacity in bits per channel use (symbol)
303+ *
304+ * **Usage Example**:
305+ * @code{.cpp}
306+ * SDMEC<F4> channel(0.05, 0.1); // 5% errors, 10% erasures over F₄
307+ * double capacity = channel.get_capacity();
308+ * std::cout << "Channel capacity: " << capacity << " bits/symbol" << std::endl;
309+ * // Result: approximately 1.52 bits/symbol (for F₄ with these parameters)
310+ * @endcode
311+ *
312+ * @note Returned value is in bits (log base 2).
313+ */
314+ double get_capacity () const noexcept ;
315+
278316 private:
279317 std::geometric_distribution<unsigned int > error_dist;
280318 unsigned int error_trials{0 };
@@ -338,6 +376,24 @@ T SDMEC<T>::operator()(const T& in) noexcept {
338376 return res;
339377}
340378
379+ template <FiniteFieldType T>
380+ double SDMEC<T>::get_capacity() const noexcept {
381+ const double pe = error_dist.p ();
382+ const double q = static_cast <double >(T::get_size ());
383+
384+ // Handle edge cases: lim_{x→0} x·log₂(x) = 0
385+ const double term1 = (pe > 0.0 && pe < 1.0 ) ? pe * log2 (pe) : 0.0 ;
386+ const double term2 = (pe > 0.0 && pe < 1.0 ) ? (1 - pe) * log2 (1 - pe) : 0.0 ;
387+
388+ double res = log2 (q) + term2 + term1 - pe * log2 (q - 1 );
389+
390+ #ifdef CECCO_ERASURE_SUPPORT
391+ const double px = erasure_dist.p ();
392+ res *= (1 - px);
393+ #endif
394+ return res;
395+ }
396+
341397/* *
342398 * @brief Symmetric Discrete Memoryless Channel (SDMC)
343399 * @tparam T Finite field type for channel input/output symbols
@@ -699,6 +755,73 @@ double AWGN::calculate_pe(double Eb, double constellation_distance, double EbNod
699755 return 0.5 * erfc (sqrt (constellation_snr));
700756}
701757
758+
759+ /* *
760+ * @brief Binary Input - Additive White Gaussian Noise (BI-AWGN) channel
761+ *
762+ * Combines NRZ modulation and AWGN transmission into a single block that maps binary inputs
763+ * to noisy complex-valued channel outputs. Compatible with NRZDecoder for hard decisions
764+ * or LLRCalculator for soft decisions.
765+ *
766+ * **Usage Example**:
767+ * @code{.cpp}
768+ * // BPSK-AWGN at 6 dB (default parameters)
769+ * BI_AWGN channel(6.0); // BPSK: a=0, b=2
770+ * Vector<Fp<2>> c = {1, 0, 1, 0};
771+ * Vector<std::complex<double>> y;
772+ * Vector<Fp<2>> r;
773+ * c >> channel >> y >> NRZDecoder(channel.get_encoder()) >> r;
774+ *
775+ * // Custom NRZ constellation
776+ * BI_AWGN ook(8.0, 1.0, 2.0); // OOK: a=1, b=2
777+ * @endcode
778+ *
779+ * @note Default parameters implement BPSK (a=0, b=2)
780+ * @see @ref CECCO::NRZEncoder for constellation parameters
781+ * @see @ref CECCO::AWGN for transmission/noise model
782+ * @see @ref CECCO::NRZDecoder for hard-decision
783+ * @see @ref CECCO::LLRCalculator for soft-decision
784+ */
785+ class BI_AWGN : public details ::BlockProcessor<BI_AWGN, Fp<2 >, std::complex <double >> {
786+ public:
787+ using details::BlockProcessor<BI_AWGN, Fp<2 >, std::complex <double >>::operator ();
788+
789+ /* *
790+ * @brief Construct BI-AWGN channel with SNR and optional NRZ parameters
791+ * @param EbN0dB Signal-to-noise ratio (Eb/N0) in decibels
792+ * @param a DC offset parameter for NRZ encoder (default: 0.0 for BPSK)
793+ * @param b Constellation distance parameter for NRZ encoder (default: 2.0 for BPSK)
794+ *
795+ * Default parameters (a=0, b=2) implement BPSK modulation with constellation {-1, +1}.
796+ * For other modulation schemes (e.g., OOK with a=1, b=2), specify custom parameters.
797+ */
798+ BI_AWGN (double EbN0dB, double a = 0.0 , double b = 2.0 )
799+ : encoder(a, b), transmission(encoder.get_Eb(), encoder.get_constellation_distance(), EbN0dB) {}
800+
801+ /* *
802+ * @brief Process single bit through BI-AWGN channel
803+ * @param in Input bit
804+ * @return Noisy complex-valued channel output
805+ */
806+ std::complex <double > operator ()(const Fp<2 >& in) noexcept { return transmission (encoder (in)); }
807+
808+ /* *
809+ * @brief Get theoretical bit error probability for hard decisions
810+ * @return Pe for this channel configuration
811+ */
812+ double get_pe () const noexcept { return transmission.get_pe (); }
813+
814+ /* *
815+ * @brief Get reference to internal NRZ encoder
816+ * @return Const reference to NRZ encoder (needed for NRZDecoder construction)
817+ */
818+ const NRZEncoder& get_encoder () const noexcept { return encoder; }
819+
820+ private:
821+ NRZEncoder encoder;
822+ AWGN transmission;
823+ };
824+
702825/* *
703826 * @brief Non-Return-to-Zero (NRZ) hard-decision decoder
704827 *
0 commit comments