291 lines
9.3 KiB
Rust
291 lines
9.3 KiB
Rust
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<u8> = (0..code.k()).map(|_| rng.gen::<u8>() & 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<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);
|
||
}
|
||
}
|
||
}
|
||
}
|