This commit is contained in:
2026-05-07 18:32:36 +02:00
parent 2fcd368e34
commit b7faade0c8
25 changed files with 4209 additions and 4 deletions

268
src/benchmark.rs Normal file
View File

@ -0,0 +1,268 @@
use crate::channel::{AwgnChannel, Channel};
use crate::code::{CodeTopology, GenerationMethod, LdpcCode, LdpcParams};
use crate::decoder::{build_decoder, DecoderConfig, DecoderMethod};
use crate::encoder::{build_encoder, EncodingMethod};
use crate::Result as LdpcResult;
use rand::{Rng, SeedableRng};
use std::fs::File;
use std::io::{Read, Write};
use std::path::Path;
use std::time::Instant;
pub fn run_simulation(mut code: LdpcCode) -> LdpcResult<()> {
println!("[*] Étape 1 : Construction Mathématique et Graphe de Tanner");
println!(
" - Dimensions : n={}, k={}, m={} (Taux R={:.3})",
code.n(),
code.k(),
code.m(),
code.rate()
);
let methode_nom = match code.params.generation {
GenerationMethod::MacKayNeal { .. } => "MacKay-Neal",
GenerationMethod::Gallager => "Gallager",
};
println!(" - Topologie : Régulier via {}", methode_nom);
println!(" - Densité H : {:.2}%", code.h.density() * 100.0);
println!(" - Girth : {}", code.girth());
println!(
" - Cycles-4 : {}",
if code.graph.has_4_cycle() {
"Présents (Problématique)"
} else {
"Aucun (Optimal)"
}
);
println!("\n[*] Étape 2 : Extraction de G^T et Instanciation de l'Encodeur");
let start_enc = Instant::now();
let encoder = build_encoder(&mut code, EncodingMethod::Systematic)?;
println!(
" - Forme systématique calculée en {:.2?}",
start_enc.elapsed()
);
println!("\n[*] Étape 3 : Instanciation des Décodeurs sur Graphe");
let config = DecoderConfig {
max_iterations: 50,
early_stopping: true,
};
let dec_sp = build_decoder(&code, DecoderMethod::SumProduct, config.clone());
let dec_ms = build_decoder(
&code,
DecoderMethod::MinSum {
scaling_factor: 0.8,
},
config.clone(),
);
let dec_bf = build_decoder(&code, DecoderMethod::BitFlipping, config.clone());
println!(" - Moteurs prêts : Sum-Product, Min-Sum (α=0.8), Bit-Flipping");
let snr_range = [1.0, 1.5, 2.0, 2.5, 3.0, 3.5, 4.0, 4.5, 5.0];
let n_trials = 100;
let mut rng = rand::rngs::StdRng::seed_from_u64(42);
println!(
"\n[*] Étape 4 : Simulation sur Canal AWGN ({} trames par SNR)",
n_trials
);
println!("{:-<115}", "");
println!(
"{:>8} | {:>9} || {:>10} | {:>10} || {:>10} | {:>10} || {:>10} | {:>10} |",
"SNR (dB)",
"Capacité",
"FER (SP)",
"BER (SP)",
"FER (MS)",
"BER (MS)",
"FER (BF)",
"BER (BF)"
);
println!("{:-<115}", "");
for &snr in &snr_range {
let channel = AwgnChannel::new(snr, code.rate())?;
let mut err_sp_frames = 0;
let mut err_sp_bits = 0;
let mut err_ms_frames = 0;
let mut err_ms_bits = 0;
let mut err_bf_frames = 0;
let mut err_bf_bits = 0;
for _ in 0..n_trials {
let message: Vec<u8> = (0..code.k()).map(|_| rng.gen::<u8>() & 1).collect();
let codeword = encoder.encode(&message)?;
let received_llr = channel.transmit(&codeword, &mut rng);
// SP
let res_sp = dec_sp.decode(&received_llr);
if let Some(decoded) = res_sp.codeword() {
let errs = count_bit_errors(&codeword, decoded);
if errs > 0 {
err_sp_frames += 1;
err_sp_bits += errs;
}
} else {
err_sp_frames += 1;
err_sp_bits += code.n();
}
// MS
let res_ms = dec_ms.decode(&received_llr);
if let Some(decoded) = res_ms.codeword() {
let errs = count_bit_errors(&codeword, decoded);
if errs > 0 {
err_ms_frames += 1;
err_ms_bits += errs;
}
} else {
err_ms_frames += 1;
err_ms_bits += code.n();
}
// BF
let res_bf = dec_bf.decode(&received_llr);
if let Some(decoded) = res_bf.codeword() {
let errs = count_bit_errors(&codeword, decoded);
if errs > 0 {
err_bf_frames += 1;
err_bf_bits += errs;
}
} else {
err_bf_frames += 1;
err_bf_bits += code.n();
}
}
let total_bits = (n_trials * code.n()) as f64;
let total_frames = n_trials as f64;
println!(
"{:>8.2} | {:>9.4} || {:>9.2}% | {:>9.2}% || {:>9.2}% | {:>9.2}% || {:>9.2}% | {:>9.2}% |",
snr, channel.capacity(),
(err_sp_frames as f64 / total_frames) * 100.0, (err_sp_bits as f64 / total_bits) * 100.0,
(err_ms_frames as f64 / total_frames) * 100.0, (err_ms_bits as f64 / total_bits) * 100.0,
(err_bf_frames as f64 / total_frames) * 100.0, (err_bf_bits as f64 / total_bits) * 100.0
);
}
println!("{:-<115}", "");
Ok(())
}
#[inline]
fn count_bit_errors(transmitted: &[u8], decoded: &[u8]) -> usize {
transmitted
.iter()
.zip(decoded.iter())
.filter(|(a, b)| a != b)
.count()
}
pub fn generate_valid_code(
n: usize,
k: usize,
wc: usize,
wr: usize,
generation_method: GenerationMethod,
) -> LdpcResult<LdpcCode> {
let mut attempt = 0;
let start_gen = Instant::now();
loop {
attempt += 1;
let params = LdpcParams {
n,
k,
topology: CodeTopology::Regular { wc, wr },
generation: generation_method.clone(),
seed: Some(rand::random()),
};
if let Ok(mut code) = LdpcCode::new(params) {
if code.compute_systematic_form().is_ok() {
if attempt > 1 {
println!(
" -> Matrice inversible obtenue après {} tentatives.",
attempt
);
}
println!(" - Génération : Terminée en {:.2?}", start_gen.elapsed());
return Ok(code);
}
}
}
}
pub fn get_or_generate_cached_code(
n: usize,
k: usize,
wc: usize,
wr: usize,
generation_method: GenerationMethod,
) -> LdpcResult<LdpcCode> {
let method_str = match generation_method {
GenerationMethod::MacKayNeal { .. } => "MN",
GenerationMethod::Gallager => "GAL",
};
let cache_filename = format!("cache_ldpc_{}_n{}_k{}.bin", method_str, n, k);
let path = Path::new(&cache_filename);
// Chargement
if path.exists() {
let start_load = Instant::now();
let mut file = File::open(&path).expect("Impossible d'ouvrir le fichier de cache");
let mut buffer = Vec::new();
file.read_to_end(&mut buffer).unwrap();
let mut code: LdpcCode = bincode::deserialize(&buffer).expect(
"Erreur de désérialisation du cache LDPC. Supprimez le fichier .bin et réessayez.",
);
// Reconstruction du Graphe de Tanner
code.graph = crate::graph::TannerGraph::from_matrix(&code.h);
println!(
" -> Matrice chargée depuis le cache en {:.2?}",
start_load.elapsed()
);
return Ok(code);
}
// Génération
println!(" -> Aucun cache trouvé. Génération en cours ...");
let mut attempt = 0;
let start_gen = Instant::now();
loop {
attempt += 1;
let params = LdpcParams {
n,
k,
topology: CodeTopology::Regular { wc, wr },
generation: generation_method.clone(),
seed: Some(rand::random()),
};
if let Ok(mut code) = LdpcCode::new(params) {
if code.compute_systematic_form().is_ok() {
println!(
" - Génération et Pivot de Gauss terminés en {:.2?}",
start_gen.elapsed()
);
// sauvegarde cache
let encoded = bincode::serialize(&code).expect("Échec de la sérialisation");
let mut file =
File::create(&path).expect("Impossible de créer le fichier de cache");
file.write_all(&encoded)
.expect("Impossible d'écrire sur le disque");
println!(" -> Matrice sauvegardée ({})", cache_filename);
return Ok(code);
}
}
}
}