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; use rayon::prelude::*; 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 : Instanciation de l'Encodeur (SIMD Bit-Packing)"); let start_enc = Instant::now(); let encoder = build_encoder(&mut code, EncodingMethod::Systematic)?; println!(" - Encodeur prêt 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.25, 1.5, 1.75, 2.0, 2.25, 2.5, 3.0, 3.5, 4.0]; let n_trials = 1000; println!( "\n[*] Étape 4 : Simulation sur Canal AWGN ({} trames par SNR, Multi-threadé)", 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 results: Vec<_> = (0..n_trials) .into_par_iter() .map(|_| { let mut rng = rand::thread_rng(); let message: Vec = (0..code.k()).map(|_| rng.gen::() & 1).collect(); let codeword = encoder.encode(&message).unwrap(); let received_llr = channel.transmit(&codeword, &mut rng); let mut t_sp_f = 0; let mut t_sp_b = 0; let mut t_ms_f = 0; let mut t_ms_b = 0; let mut t_bf_f = 0; let mut t_bf_b = 0; // 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 { t_sp_f = 1; t_sp_b = errs; } } else { t_sp_f = 1; t_sp_b = 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 { t_ms_f = 1; t_ms_b = errs; } } else { t_ms_f = 1; t_ms_b = 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 { t_bf_f = 1; t_bf_b = errs; } } else { t_bf_f = 1; t_bf_b = code.n(); } (t_sp_f, t_sp_b, t_ms_f, t_ms_b, t_bf_f, t_bf_b) }) .collect(); // Agrégation 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 res in results { err_sp_frames += res.0; err_sp_bits += res.1; err_ms_frames += res.2; err_ms_bits += res.3; err_bf_frames += res.4; err_bf_bits += res.5; } 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 { // 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 { 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); } } } }