simd opti

This commit is contained in:
2026-05-11 12:08:06 +02:00
parent 98a3422fc3
commit 9c3fb79c00
23 changed files with 1102 additions and 179 deletions
Generated
+331
View File
@@ -8,6 +8,56 @@ version = "2.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "320119579fcad9c21884f5c4861d16174d0e06250625266f50fe6898340abefa" checksum = "320119579fcad9c21884f5c4861d16174d0e06250625266f50fe6898340abefa"
[[package]]
name = "anstream"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "824a212faf96e9acacdbd09febd34438f8f711fb84e09a8916013cd7815ca28d"
dependencies = [
"anstyle",
"anstyle-parse",
"anstyle-query",
"anstyle-wincon",
"colorchoice",
"is_terminal_polyfill",
"utf8parse",
]
[[package]]
name = "anstyle"
version = "1.0.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "940b3a0ca603d1eade50a4846a2afffd5ef57a9feac2c0e2ec2e14f9ead76000"
[[package]]
name = "anstyle-parse"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "52ce7f38b242319f7cabaa6813055467063ecdc9d355bbb4ce0c68908cd8130e"
dependencies = [
"utf8parse",
]
[[package]]
name = "anstyle-query"
version = "1.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "40c48f72fd53cd289104fc64099abca73db4166ad86ea0b4341abe65af83dadc"
dependencies = [
"windows-sys",
]
[[package]]
name = "anstyle-wincon"
version = "3.0.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "291e6a250ff86cd4a820112fb8898808a366d8f9f58ce16d1f538353ad55747d"
dependencies = [
"anstyle",
"once_cell_polyfill",
"windows-sys",
]
[[package]] [[package]]
name = "approx" name = "approx"
version = "0.5.1" version = "0.5.1"
@@ -44,6 +94,12 @@ version = "1.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
[[package]]
name = "bumpalo"
version = "3.20.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5d20789868f4b01b2f2caec9f5c4e0213b41e3e5702a50157d699ae31ced2fcb"
[[package]] [[package]]
name = "bytemuck" name = "bytemuck"
version = "1.25.0" version = "1.25.0"
@@ -62,12 +118,70 @@ version = "1.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801" checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801"
[[package]]
name = "clap"
version = "4.6.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1ddb117e43bbf7dacf0a4190fef4d345b9bad68dfc649cb349e7d17d28428e51"
dependencies = [
"clap_builder",
"clap_derive",
]
[[package]]
name = "clap_builder"
version = "4.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "714a53001bf66416adb0e2ef5ac857140e7dc3a0c48fb28b2f10762fc4b5069f"
dependencies = [
"anstream",
"anstyle",
"clap_lex",
"strsim",
]
[[package]]
name = "clap_derive"
version = "4.6.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f2ce8604710f6733aa641a2b3731eaa1e8b3d9973d5e3565da11800813f997a9"
dependencies = [
"heck",
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "clap_lex"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c8d4a3bb8b1e0c1050499d1815f5ab16d04f0959b233085fb31653fbfc9d98f9"
[[package]] [[package]]
name = "color_quant" name = "color_quant"
version = "1.1.0" version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3d7b894f5411737b7867f4827955924d7c254fc9f4d91a6aad6b097804b1018b" checksum = "3d7b894f5411737b7867f4827955924d7c254fc9f4d91a6aad6b097804b1018b"
[[package]]
name = "colorchoice"
version = "1.0.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1d07550c9036bf2ae0c684c4297d503f838287c83c53686d05370d0e139ae570"
[[package]]
name = "console"
version = "0.16.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d64e8af5551369d19cf50138de61f1c42074ab970f74e99be916646777f8fc87"
dependencies = [
"encode_unicode",
"libc",
"unicode-width",
"windows-sys",
]
[[package]] [[package]]
name = "crc32fast" name = "crc32fast"
version = "1.5.0" version = "1.5.0"
@@ -114,6 +228,12 @@ version = "1.15.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719" checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719"
[[package]]
name = "encode_unicode"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "34aa73646ffb006b8f5147f3dc182bd4bcb190227ce861fc4a4844bf8e3cb2c0"
[[package]] [[package]]
name = "exr" name = "exr"
version = "1.74.0" version = "1.74.0"
@@ -148,6 +268,30 @@ dependencies = [
"miniz_oxide", "miniz_oxide",
] ]
[[package]]
name = "futures-core"
version = "0.3.32"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7e3450815272ef58cec6d564423f6e755e25379b217b0bc688e295ba24df6b1d"
[[package]]
name = "futures-task"
version = "0.3.32"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "037711b3d59c33004d3856fbdc83b99d4ff37a24768fa1be9ce3538a1cde4393"
[[package]]
name = "futures-util"
version = "0.3.32"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "389ca41296e6190b48053de0321d02a77f32f8a5d2461dd38762c0593805c6d6"
dependencies = [
"futures-core",
"futures-task",
"pin-project-lite",
"slab",
]
[[package]] [[package]]
name = "getrandom" name = "getrandom"
version = "0.2.17" version = "0.2.17"
@@ -180,6 +324,18 @@ dependencies = [
"zerocopy", "zerocopy",
] ]
[[package]]
name = "heck"
version = "0.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea"
[[package]]
name = "hermit-abi"
version = "0.5.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fc0fef456e4baa96da950455cd02c081ca953b141298e41db3fc7e36b1da849c"
[[package]] [[package]]
name = "image" name = "image"
version = "0.24.9" version = "0.24.9"
@@ -198,6 +354,25 @@ dependencies = [
"tiff", "tiff",
] ]
[[package]]
name = "indicatif"
version = "0.18.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "25470f23803092da7d239834776d653104d551bc4d7eacaf31e6837854b8e9eb"
dependencies = [
"console",
"portable-atomic",
"unicode-width",
"unit-prefix",
"web-time",
]
[[package]]
name = "is_terminal_polyfill"
version = "1.70.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a6cb138bb79a146c1bd460005623e142ef0181e3d0219cb493e02f7d08a35695"
[[package]] [[package]]
name = "jpeg-decoder" name = "jpeg-decoder"
version = "0.3.2" version = "0.3.2"
@@ -207,15 +382,31 @@ dependencies = [
"rayon", "rayon",
] ]
[[package]]
name = "js-sys"
version = "0.3.98"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "67df7112613f8bfd9150013a0314e196f4800d3201ae742489d999db2f979f08"
dependencies = [
"cfg-if",
"futures-util",
"once_cell",
"wasm-bindgen",
]
[[package]] [[package]]
name = "ldpc" name = "ldpc"
version = "0.1.0" version = "0.1.0"
dependencies = [ dependencies = [
"approx", "approx",
"bincode", "bincode",
"clap",
"image", "image",
"indicatif",
"num_cpus",
"rand", "rand",
"rand_distr", "rand_distr",
"rayon",
"serde", "serde",
"thiserror", "thiserror",
] ]
@@ -258,6 +449,34 @@ dependencies = [
"libm", "libm",
] ]
[[package]]
name = "num_cpus"
version = "1.17.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "91df4bbde75afed763b708b7eee1e8e7651e02d97f6d5dd763e89367e957b23b"
dependencies = [
"hermit-abi",
"libc",
]
[[package]]
name = "once_cell"
version = "1.21.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9f7c3e4beb33f85d45ae3e3a1792185706c8e16d043238c593331cc7cd313b50"
[[package]]
name = "once_cell_polyfill"
version = "1.70.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "384b8ab6d37215f3c5301a95a4accb5d64aa607f1fcb26a11b5303878451b4fe"
[[package]]
name = "pin-project-lite"
version = "0.2.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a89322df9ebe1c1578d689c92318e070967d1042b512afbe49518723f4e6d5cd"
[[package]] [[package]]
name = "png" name = "png"
version = "0.17.16" version = "0.17.16"
@@ -271,6 +490,12 @@ dependencies = [
"miniz_oxide", "miniz_oxide",
] ]
[[package]]
name = "portable-atomic"
version = "1.13.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c33a9471896f1c69cecef8d20cbe2f7accd12527ce60845ff44c153bb2a21b49"
[[package]] [[package]]
name = "ppv-lite86" name = "ppv-lite86"
version = "0.2.21" version = "0.2.21"
@@ -367,6 +592,12 @@ dependencies = [
"crossbeam-utils", "crossbeam-utils",
] ]
[[package]]
name = "rustversion"
version = "1.0.22"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d"
[[package]] [[package]]
name = "serde" name = "serde"
version = "1.0.228" version = "1.0.228"
@@ -403,12 +634,24 @@ version = "0.3.9"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "703d5c7ef118737c72f1af64ad2f6f8c5e1921f818cdcb97b8fe6fc69bf66214" checksum = "703d5c7ef118737c72f1af64ad2f6f8c5e1921f818cdcb97b8fe6fc69bf66214"
[[package]]
name = "slab"
version = "0.4.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0c790de23124f9ab44544d7ac05d60440adc586479ce501c1d6d7da3cd8c9cf5"
[[package]] [[package]]
name = "smallvec" name = "smallvec"
version = "1.15.1" version = "1.15.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03" checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03"
[[package]]
name = "strsim"
version = "0.11.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f"
[[package]] [[package]]
name = "syn" name = "syn"
version = "2.0.117" version = "2.0.117"
@@ -457,18 +700,106 @@ version = "1.0.24"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75" checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75"
[[package]]
name = "unicode-width"
version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b4ac048d71ede7ee76d585517add45da530660ef4390e49b098733c6e897f254"
[[package]]
name = "unit-prefix"
version = "0.5.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "81e544489bf3d8ef66c953931f56617f423cd4b5494be343d9b9d3dda037b9a3"
[[package]]
name = "utf8parse"
version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821"
[[package]] [[package]]
name = "wasi" name = "wasi"
version = "0.11.1+wasi-snapshot-preview1" version = "0.11.1+wasi-snapshot-preview1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b" checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b"
[[package]]
name = "wasm-bindgen"
version = "0.2.121"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "49ace1d07c165b0864824eee619580c4689389afa9dc9ed3a4c75040d82e6790"
dependencies = [
"cfg-if",
"once_cell",
"rustversion",
"wasm-bindgen-macro",
"wasm-bindgen-shared",
]
[[package]]
name = "wasm-bindgen-macro"
version = "0.2.121"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8e68e6f4afd367a562002c05637acb8578ff2dea1943df76afb9e83d177c8578"
dependencies = [
"quote",
"wasm-bindgen-macro-support",
]
[[package]]
name = "wasm-bindgen-macro-support"
version = "0.2.121"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d95a9ec35c64b2a7cb35d3fead40c4238d0940c86d107136999567a4703259f2"
dependencies = [
"bumpalo",
"proc-macro2",
"quote",
"syn",
"wasm-bindgen-shared",
]
[[package]]
name = "wasm-bindgen-shared"
version = "0.2.121"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c4e0100b01e9f0d03189a92b96772a1fb998639d981193d7dbab487302513441"
dependencies = [
"unicode-ident",
]
[[package]]
name = "web-time"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5a6580f308b1fad9207618087a65c04e7a10bc77e02c8e84e9b00dd4b12fa0bb"
dependencies = [
"js-sys",
"wasm-bindgen",
]
[[package]] [[package]]
name = "weezl" name = "weezl"
version = "0.1.12" version = "0.1.12"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a28ac98ddc8b9274cb41bb4d9d4d5c425b6020c50c46f25559911905610b4a88" checksum = "a28ac98ddc8b9274cb41bb4d9d4d5c425b6020c50c46f25559911905610b4a88"
[[package]]
name = "windows-link"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5"
[[package]]
name = "windows-sys"
version = "0.61.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ae137229bcbd6cdf0f7b80a31df61766145077ddf49416a728b02cb3921ff3fc"
dependencies = [
"windows-link",
]
[[package]] [[package]]
name = "zerocopy" name = "zerocopy"
version = "0.8.48" version = "0.8.48"
+4
View File
@@ -10,6 +10,10 @@ rand_distr = "0.4.3"
thiserror = "1.0" thiserror = "1.0"
serde = { version = "1.0", features = ["derive"] } serde = { version = "1.0", features = ["derive"] }
bincode = "1.3" bincode = "1.3"
rayon = "1.12.0"
indicatif = "0.18.4"
clap = {version = "4.6.1", features = ["derive"]}
num_cpus = "1.17.0"
[dev-dependencies] [dev-dependencies]
approx = "0.5" approx = "0.5"
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
View File
Binary file not shown.

Before

Width:  |  Height:  |  Size: 25 KiB

After

Width:  |  Height:  |  Size: 1.0 MiB

BIN
View File
Binary file not shown.

Before

Width:  |  Height:  |  Size: 52 KiB

After

Width:  |  Height:  |  Size: 2.3 MiB

+85 -63
View File
@@ -3,7 +3,8 @@ use crate::code::{CodeTopology, GenerationMethod, LdpcCode, LdpcParams};
use crate::decoder::{build_decoder, DecoderConfig, DecoderMethod}; use crate::decoder::{build_decoder, DecoderConfig, DecoderMethod};
use crate::encoder::{build_encoder, EncodingMethod}; use crate::encoder::{build_encoder, EncodingMethod};
use crate::Result as LdpcResult; use crate::Result as LdpcResult;
use rand::{Rng, SeedableRng}; use rand::Rng;
use rayon::prelude::*;
use std::fs::File; use std::fs::File;
use std::io::{Read, Write}; use std::io::{Read, Write};
use std::path::Path; use std::path::Path;
@@ -37,13 +38,10 @@ pub fn run_simulation(mut code: LdpcCode) -> LdpcResult<()> {
} }
); );
println!("\n[*] Étape 2 : Extraction de G^T et Instanciation de l'Encodeur"); println!("\n[*] Étape 2 : Instanciation de l'Encodeur (SIMD Bit-Packing)");
let start_enc = Instant::now(); let start_enc = Instant::now();
let encoder = build_encoder(&mut code, EncodingMethod::Systematic)?; let encoder = build_encoder(&mut code, EncodingMethod::Systematic)?;
println!( println!(" - Encodeur prêt en {:.2?}", start_enc.elapsed());
" - Forme systématique calculée en {:.2?}",
start_enc.elapsed()
);
println!("\n[*] Étape 3 : Instanciation des Décodeurs sur Graphe"); println!("\n[*] Étape 3 : Instanciation des Décodeurs sur Graphe");
let config = DecoderConfig { let config = DecoderConfig {
@@ -62,12 +60,11 @@ pub fn run_simulation(mut code: LdpcCode) -> LdpcResult<()> {
let dec_bf = build_decoder(&code, DecoderMethod::BitFlipping, config.clone()); let dec_bf = build_decoder(&code, DecoderMethod::BitFlipping, config.clone());
println!(" - Moteurs prêts : Sum-Product, Min-Sum (α=0.8), Bit-Flipping"); 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 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 = 100; let n_trials = 1000;
let mut rng = rand::rngs::StdRng::seed_from_u64(42);
println!( println!(
"\n[*] Étape 4 : Simulation sur Canal AWGN ({} trames par SNR)", "\n[*] Étape 4 : Simulation sur Canal AWGN ({} trames par SNR, Multi-threadé)",
n_trials n_trials
); );
println!("{:-<115}", ""); println!("{:-<115}", "");
@@ -86,29 +83,34 @@ pub fn run_simulation(mut code: LdpcCode) -> LdpcResult<()> {
for &snr in &snr_range { for &snr in &snr_range {
let channel = AwgnChannel::new(snr, code.rate())?; 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 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 message: Vec<u8> = (0..code.k()).map(|_| rng.gen::<u8>() & 1).collect();
let codeword = encoder.encode(&message)?; let codeword = encoder.encode(&message).unwrap();
let received_llr = channel.transmit(&codeword, &mut rng); 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 // SP
let res_sp = dec_sp.decode(&received_llr); let res_sp = dec_sp.decode(&received_llr);
if let Some(decoded) = res_sp.codeword() { if let Some(decoded) = res_sp.codeword() {
let errs = count_bit_errors(&codeword, decoded); let errs = count_bit_errors(&codeword, decoded);
if errs > 0 { if errs > 0 {
err_sp_frames += 1; t_sp_f = 1;
err_sp_bits += errs; t_sp_b = errs;
} }
} else { } else {
err_sp_frames += 1; t_sp_f = 1;
err_sp_bits += code.n(); t_sp_b = code.n();
} }
// MS // MS
@@ -116,12 +118,12 @@ pub fn run_simulation(mut code: LdpcCode) -> LdpcResult<()> {
if let Some(decoded) = res_ms.codeword() { if let Some(decoded) = res_ms.codeword() {
let errs = count_bit_errors(&codeword, decoded); let errs = count_bit_errors(&codeword, decoded);
if errs > 0 { if errs > 0 {
err_ms_frames += 1; t_ms_f = 1;
err_ms_bits += errs; t_ms_b = errs;
} }
} else { } else {
err_ms_frames += 1; t_ms_f = 1;
err_ms_bits += code.n(); t_ms_b = code.n();
} }
// BF // BF
@@ -129,13 +131,33 @@ pub fn run_simulation(mut code: LdpcCode) -> LdpcResult<()> {
if let Some(decoded) = res_bf.codeword() { if let Some(decoded) = res_bf.codeword() {
let errs = count_bit_errors(&codeword, decoded); let errs = count_bit_errors(&codeword, decoded);
if errs > 0 { if errs > 0 {
err_bf_frames += 1; t_bf_f = 1;
err_bf_bits += errs; t_bf_b = errs;
} }
} else { } else {
err_bf_frames += 1; t_bf_f = 1;
err_bf_bits += code.n(); 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_bits = (n_trials * code.n()) as f64;
@@ -162,39 +184,39 @@ fn count_bit_errors(transmitted: &[u8], decoded: &[u8]) -> usize {
.count() .count()
} }
pub fn generate_valid_code( // pub fn generate_valid_code(
n: usize, // n: usize,
k: usize, // k: usize,
wc: usize, // wc: usize,
wr: usize, // wr: usize,
generation_method: GenerationMethod, // generation_method: GenerationMethod,
) -> LdpcResult<LdpcCode> { // ) -> LdpcResult<LdpcCode> {
let mut attempt = 0; // let mut attempt = 0;
let start_gen = Instant::now(); // let start_gen = Instant::now();
loop { // loop {
attempt += 1; // attempt += 1;
let params = LdpcParams { // let params = LdpcParams {
n, // n,
k, // k,
topology: CodeTopology::Regular { wc, wr }, // topology: CodeTopology::Regular { wc, wr },
generation: generation_method.clone(), // generation: generation_method.clone(),
seed: Some(rand::random()), // seed: Some(rand::random()),
}; // };
//
if let Ok(mut code) = LdpcCode::new(params) { // if let Ok(mut code) = LdpcCode::new(params) {
if code.compute_systematic_form().is_ok() { // if code.compute_systematic_form().is_ok() {
if attempt > 1 { // if attempt > 1 {
println!( // println!(
" -> Matrice inversible obtenue après {} tentatives.", // " -> Matrice inversible obtenue après {} tentatives.",
attempt // attempt
); // );
} // }
println!(" - Génération : Terminée en {:.2?}", start_gen.elapsed()); // println!(" - Génération : Terminée en {:.2?}", start_gen.elapsed());
return Ok(code); // return Ok(code);
} // }
} // }
} // }
} // }
pub fn get_or_generate_cached_code( pub fn get_or_generate_cached_code(
n: usize, n: usize,
+228
View File
@@ -0,0 +1,228 @@
use crate::benchmark::get_or_generate_cached_code;
use crate::channel::{AwgnChannel, Channel};
use crate::code::GenerationMethod;
use crate::decoder::{build_decoder, DecoderConfig, DecoderMethod};
use crate::encoder::{build_encoder, EncodingMethod};
use crate::Result as LdpcResult;
use indicatif::{ProgressBar, ProgressStyle};
use rand::Rng;
use rayon::prelude::*;
use std::fs::OpenOptions;
use std::io::Write;
use std::time::Instant;
#[derive(Clone)]
pub struct CodeScenario {
pub n: usize,
pub k: usize,
pub wc: usize,
pub wr: usize,
pub method: GenerationMethod,
pub name: String,
}
pub struct CampaignConfig {
pub scenarios: Vec<CodeScenario>,
pub snr_range: Vec<f64>,
pub n_trials: usize,
pub max_iterations: usize,
pub output_csv: String,
pub export_graph: bool,
}
pub fn run_massive_campaign(config: CampaignConfig) -> LdpcResult<()> {
println!("TEST)");
println!("Fichier de sortie : {}", config.output_csv);
println!("Scenarios a tester: {}", config.scenarios.len());
println!("SNRs par scenario : {}", config.snr_range.len());
println!("Trames par point : {}\n", config.n_trials);
let file_exists = std::path::Path::new(&config.output_csv).exists();
let mut file = OpenOptions::new()
.create(true)
.append(true)
.open(&config.output_csv)
.expect("Impossible d'ouvrir le fichier CSV");
if !file_exists {
writeln!(
file,
"Scenario,N,K,Rate,Wc,Wr,Method,Girth,Density_pct,SNR_dB,Capacity,Decoder,FER_pct,BER_pct,Frames,Time_ms"
).unwrap();
}
let total_steps = (config.scenarios.len() * config.snr_range.len()) as u64;
let pb = ProgressBar::new(total_steps);
pb.set_style(
ProgressStyle::default_bar()
.template("{spinner:.cyan} [{elapsed_precise}] [{bar:40.green/blue}] {pos}/{len} ({eta}) - {msg}")
.unwrap()
.progress_chars("=>-"),
);
for scenario in config.scenarios {
pb.set_message(format!("Generation Matrice: {}", scenario.name));
let mut code = get_or_generate_cached_code(
scenario.n,
scenario.k,
scenario.wc,
scenario.wr,
scenario.method.clone(),
)?;
if config.export_graph {
let dot_filename = format!("{}_tanner.dot", scenario.name);
let mut dot_file = std::fs::File::create(&dot_filename).expect("Erreur creation .dot");
writeln!(dot_file, "graph TannerGraph {{").unwrap();
writeln!(dot_file, " rankdir=TB;").unwrap();
writeln!(dot_file, " nodesep=0.5;").unwrap();
writeln!(dot_file, " ranksep=2.0;").unwrap();
writeln!(dot_file, " node [style=filled, fontname=\"Arial\"];").unwrap();
writeln!(dot_file, " {{ rank=same;").unwrap();
for v in 0..code.n() {
writeln!(
dot_file,
" v{} [shape=circle, fillcolor=lightblue, label=\"v{}\"];",
v, v
)
.unwrap();
}
writeln!(dot_file, " }}").unwrap();
writeln!(dot_file, " {{ rank=same;").unwrap();
for c in 0..code.m() {
writeln!(
dot_file,
" c{} [shape=square, fillcolor=lightcoral, label=\"c{}\"];",
c, c
)
.unwrap();
}
writeln!(dot_file, " }}").unwrap();
for c in 0..code.m() {
for &v in code.graph.chk_neighbors(c) {
writeln!(dot_file, " v{} -- c{};", v, c).unwrap();
}
}
writeln!(dot_file, "}}").unwrap();
}
let rate = code.rate();
let girth = code.girth();
let density = code.h.density() * 100.0;
let method_str = match scenario.method {
GenerationMethod::Gallager => "Gallager",
GenerationMethod::MacKayNeal { .. } => "MacKayNeal",
};
pb.set_message(format!("Initialisation DSP: {}", scenario.name));
let encoder = build_encoder(&mut code, EncodingMethod::Systematic)?;
let dec_config = DecoderConfig {
max_iterations: config.max_iterations,
early_stopping: true,
};
let dec_sp = build_decoder(&code, DecoderMethod::SumProduct, dec_config.clone());
let dec_ms = build_decoder(
&code,
DecoderMethod::MinSum {
scaling_factor: 0.8,
},
dec_config.clone(),
);
let dec_bf = build_decoder(&code, DecoderMethod::BitFlipping, dec_config);
for &snr in &config.snr_range {
pb.set_message(format!("Test: {} | SNR: {:.2} dB", scenario.name, snr));
let channel = AwgnChannel::new(snr, rate)?;
let cap = channel.capacity();
let start_snr_time = Instant::now();
let results: Vec<_> = (0..config.n_trials)
.into_par_iter()
.map(|_| {
let mut rng = rand::thread_rng();
let message: Vec<u8> = (0..scenario.k).map(|_| rng.gen::<u8>() & 1).collect();
let codeword = encoder.encode(&message).unwrap();
let rx_llr = channel.transmit(&codeword, &mut rng);
let eval_decoder = |decoder: &dyn crate::decoder::Decoder| -> (usize, usize) {
if let Some(decoded) = decoder.decode(&rx_llr).codeword() {
let errs = codeword
.iter()
.zip(decoded.iter())
.filter(|(a, b)| a != b)
.count();
(if errs > 0 { 1 } else { 0 }, errs)
} else {
(1, scenario.n)
}
};
let (sp_f, sp_b) = eval_decoder(&*dec_sp);
let (ms_f, ms_b) = eval_decoder(&*dec_ms);
let (bf_f, bf_b) = eval_decoder(&*dec_bf);
(sp_f, sp_b, ms_f, ms_b, bf_f, bf_b)
})
.collect();
let mut sp_err = (0, 0);
let mut ms_err = (0, 0);
let mut bf_err = (0, 0);
for r in results {
sp_err.0 += r.0;
sp_err.1 += r.1;
ms_err.0 += r.2;
ms_err.1 += r.3;
bf_err.0 += r.4;
bf_err.1 += r.5;
}
let elapsed_ms = start_snr_time.elapsed().as_millis();
let total_f = config.n_trials as f64;
let total_b = (config.n_trials * scenario.n) as f64;
let mut write_csv_line = |dec_name: &str, f_err: usize, b_err: usize| {
writeln!(
file,
"{},{},{},{:.3},{},{},{},{},{:.4},{:.2},{:.4},{},{:.4},{:.4e},{},{}",
scenario.name,
scenario.n,
scenario.k,
rate,
scenario.wc,
scenario.wr,
method_str,
girth,
density,
snr,
cap,
dec_name,
(f_err as f64 / total_f) * 100.0,
(b_err as f64 / total_b) * 100.0,
config.n_trials,
elapsed_ms
)
.unwrap();
};
write_csv_line("SumProduct", sp_err.0, sp_err.1);
write_csv_line("MinSum_0.8", ms_err.0, ms_err.1);
write_csv_line("BitFlipping", bf_err.0, bf_err.1);
file.flush().unwrap();
pb.inc(1);
}
}
pb.finish_with_message("Campagne terminee avec succes !");
Ok(())
}
+118
View File
@@ -0,0 +1,118 @@
use clap::Parser;
use indicatif::{ProgressBar, ProgressStyle};
use ldpc::code::{CodeTopology, GenerationMethod, LdpcCode, LdpcParams};
use rayon::prelude::*;
use std::fs::File;
use std::io::Write;
use std::sync::{
atomic::{AtomicBool, AtomicU64, Ordering},
Arc,
};
use std::time::{Duration, Instant};
#[derive(Parser, Debug)]
#[command(author, version, about = "Generateur LDPC Haute Performance")]
struct Args {
#[arg(short, long)]
n: usize,
#[arg(short, long)]
k: usize,
#[arg(long, default_value_t = 3)]
wc: usize,
#[arg(long, default_value_t = 6)]
wr: usize,
#[arg(short, long, default_value = "mackay")]
method: String,
}
fn main() -> ldpc::Result<()> {
let args = Args::parse();
let m = args.n - args.k;
if (args.n * args.wc) % m != 0 || (args.n * args.wc) / m != args.wr {
println!("Erreur : Parametres impossibles (n*wc != m*wr)");
std::process::exit(1);
}
let found = Arc::new(AtomicBool::new(false));
let attempts = Arc::new(AtomicU64::new(0));
let start_global = Instant::now();
let pb = ProgressBar::new_spinner();
pb.enable_steady_tick(Duration::from_millis(80));
pb.set_style(
ProgressStyle::default_spinner()
.template(
"{spinner:.cyan} [{elapsed_precise}] {msg} | Tentatives: {pos} | {per_sec} mats/s",
)
.unwrap(),
);
pb.set_message("Recherche d'une matrice valide...");
let method = if args.method == "mackay" {
GenerationMethod::MacKayNeal { max_attempts: 1000 }
} else {
GenerationMethod::Gallager
};
let pb_clone = pb.clone();
let found_clone = Arc::clone(&found);
let attempts_clone = Arc::clone(&attempts);
let result = (0..num_cpus::get())
.into_par_iter()
.map(|_| {
while !found_clone.load(Ordering::Relaxed) {
let current_attempt = attempts_clone.fetch_add(1, Ordering::SeqCst);
pb_clone.set_position(current_attempt);
let params = LdpcParams {
n: args.n,
k: args.k,
topology: CodeTopology::Regular {
wc: args.wc,
wr: args.wr,
},
generation: method.clone(),
seed: Some(rand::random()),
};
if let Ok(mut code) = LdpcCode::new(params) {
// Cette etape est la plus longue (Gauss-Jordan)
if code.compute_systematic_form().is_ok() {
if !found_clone.swap(true, Ordering::SeqCst) {
return Some(code);
}
}
}
}
None
})
.find_any(|res| res.is_some())
.flatten();
if let Some(code) = result {
pb.finish_with_message(format!("Succes en {:.2?}", start_global.elapsed()));
let filename = format!(
"cache_ldpc_{}_n{}_k{}.bin",
args.method.to_uppercase(),
args.n,
args.k
);
let encoded = bincode::serialize(&code).expect("Erreur serialisation");
let mut file = File::create(&filename).expect("Erreur creation");
file.write_all(&encoded).expect("Erreur ecriture");
println!("\nFichier genere : {}", filename);
println!(
"Girth : {} | Densite : {:.2}%",
code.girth(),
code.h.density() * 100.0
);
} else {
pb.abandon_with_message("Recherche arretee.");
}
Ok(())
}
+57 -12
View File
@@ -1,5 +1,4 @@
use crate::code::LdpcCode; use crate::code::LdpcCode;
use crate::matrix::DenseMatrixGF2;
use crate::{BitVec, Gf2, LdpcError, Result}; use crate::{BitVec, Gf2, LdpcError, Result};
pub trait Encoder: Send + Sync { pub trait Encoder: Send + Sync {
@@ -27,7 +26,8 @@ pub enum EncodingMethod {
pub struct SystematicEncoder { pub struct SystematicEncoder {
k: usize, k: usize,
n: usize, n: usize,
g_t: DenseMatrixGF2, // g_t: DenseMatrixGF2,
packed_g_cols: Vec<Vec<u64>>,
perm_inv: Vec<usize>, perm_inv: Vec<usize>,
col_perm: Vec<usize>, col_perm: Vec<usize>,
} }
@@ -36,10 +36,30 @@ impl SystematicEncoder {
pub fn new(code: &mut LdpcCode) -> Result<Self> { pub fn new(code: &mut LdpcCode) -> Result<Self> {
code.compute_systematic_form()?; code.compute_systematic_form()?;
let sf = code.systematic_form.as_ref().unwrap(); let sf = code.systematic_form.as_ref().unwrap();
let k = code.k();
let n = code.n();
let g_t = &sf.g;
let num_blocks = (n + 63) / 64;
// Bitpacking
let mut packed_g_cols = vec![vec![0u64; num_blocks]; k];
for j in 0..k {
for i in 0..n {
if g_t.get(i, j) == 1 {
let block_idx = i / 64;
let bit_idx = i % 64;
packed_g_cols[j][block_idx] |= 1 << bit_idx;
}
}
}
Ok(Self { Ok(Self {
k: code.k(), k,
n: code.n(), n,
g_t: sf.g.clone(), packed_g_cols,
perm_inv: sf.col_perm_inv.clone(), perm_inv: sf.col_perm_inv.clone(),
col_perm: sf.col_perm.clone(), col_perm: sf.col_perm.clone(),
}) })
@@ -47,19 +67,44 @@ impl SystematicEncoder {
} }
impl Encoder for SystematicEncoder { impl Encoder for SystematicEncoder {
// fn encode(&self, message: &[Gf2]) -> Result<BitVec> {
// self.check_input(message)?;
//
// let c_perm = self.g_t.multiply_vec(message);
//
// // Retablir l'ordre initial des bits selon la permutation de H
// let mut c = vec![0u8; self.n];
// // for (i, &ci) in c_perm.iter().enumerate() {
// // c[self.perm_inv[i]] = ci;
// // }
//
// for i in 0..self.n {
// c[i] = c_perm[self.perm_inv[i]];
// }
//
// Ok(c)
// }
fn encode(&self, message: &[Gf2]) -> Result<BitVec> { fn encode(&self, message: &[Gf2]) -> Result<BitVec> {
self.check_input(message)?; self.check_input(message)?;
let c_perm = self.g_t.multiply_vec(message); let num_blocks = (self.n + 63) / 64;
let mut accum = vec![0u64; num_blocks];
for (j, &bit) in message.iter().enumerate() {
if bit == 1 {
for b in 0..num_blocks {
accum[b] ^= self.packed_g_cols[j][b];
}
}
}
// Retablir l'ordre initial des bits selon la permutation de H
let mut c = vec![0u8; self.n]; let mut c = vec![0u8; self.n];
// for (i, &ci) in c_perm.iter().enumerate() {
// c[self.perm_inv[i]] = ci;
// }
for i in 0..self.n { for i in 0..self.n {
c[i] = c_perm[self.perm_inv[i]]; let block_idx = i / 64;
let bit_idx = i % 64;
let val = ((accum[block_idx] >> bit_idx) & 1) as u8;
c[self.col_perm[i]] = val;
} }
Ok(c) Ok(c)
+147 -41
View File
@@ -5,7 +5,9 @@ use crate::{
Gf2, Result, Gf2, Result,
}; };
use image::{ImageBuffer, Rgb}; use image::{ImageBuffer, Rgb};
use rand::SeedableRng; use indicatif::{ProgressBar, ProgressStyle};
use rayon::prelude::*;
use std::time::Instant;
// Convertit un tableau d'octets en un flux de bits // Convertit un tableau d'octets en un flux de bits
pub fn bytes_to_bits(bytes: &[u8]) -> Vec<Gf2> { pub fn bytes_to_bits(bytes: &[u8]) -> Vec<Gf2> {
@@ -32,6 +34,103 @@ pub fn bits_to_bytes(bits: &[Gf2]) -> Vec<u8> {
} }
// Transmet une image à travers le canal avec codage LDPC // Transmet une image à travers le canal avec codage LDPC
// pub fn transmit_image(
// input_path: &str,
// noisy_out_path: &str,
// decoded_out_path: &str,
// encoder: &dyn Encoder,
// decoder: &dyn Decoder,
// channel: &AwgnChannel,
// ) -> Result<()> {
// println!("[*] Chargement de l'image : {}", input_path);
// let img = image::open(input_path)
// .expect("Erreur de chargement de l'image")
// .to_rgb8();
// let (width, height) = img.dimensions();
// let raw_bytes = img.into_raw();
//
// let mut bits = bytes_to_bits(&raw_bytes);
// let original_bit_len = bits.len();
//
// // Padding
// let k = encoder.message_len();
// let remainder = bits.len() % k;
// if remainder != 0 {
// bits.resize(bits.len() + (k - remainder), 0);
// }
//
// let num_blocks = bits.len() / k;
// println!(" - Taille: {}x{} pixels", width, height);
// println!(" - Blocs à transmettre (k={}): {}", k, num_blocks);
//
// let mut rng = rand::rngs::StdRng::seed_from_u64(42);
//
// let mut noisy_bits = Vec::with_capacity(num_blocks * k);
// let mut decoded_bits = Vec::with_capacity(num_blocks * k);
//
// let mut frame_errors = 0;
//
// println!("[*] Transmission et Décodage en cours...");
//
// for (i, block) in bits.chunks(k).enumerate() {
// if i % 100 == 0 && i > 0 {
// println!(" - Progession: {} / {} blocs...", i, num_blocks);
// }
//
// let codeword = encoder.encode(block)?;
//
// let rx_llr = channel.transmit(&codeword, &mut rng);
//
// // Sans correction LDPC
// let hard_codeword: Vec<Gf2> = rx_llr
// .iter()
// .map(|&l| if l < 0.0 { 1 } else { 0 })
// .collect();
// let noisy_block = encoder.extract_message(&hard_codeword);
// noisy_bits.extend_from_slice(&noisy_block);
//
// // Décodage LDPC
// let res = decoder.decode(&rx_llr);
// if let Some(decoded_codeword) = res.codeword() {
// let decoded_msg = encoder.extract_message(decoded_codeword);
// decoded_bits.extend_from_slice(&decoded_msg);
//
// if decoded_msg != block {
// frame_errors += 1;
// }
// } else {
// decoded_bits.extend_from_slice(&noisy_block);
// frame_errors += 1;
// }
// }
//
// println!(
// "[*] Transmission terminée. FER : {:.2}%",
// (frame_errors as f64 / num_blocks as f64) * 100.0
// );
//
// // Suppression du padding
// noisy_bits.truncate(original_bit_len);
// decoded_bits.truncate(original_bit_len);
//
// // Reconstitution des images
// let noisy_bytes = bits_to_bytes(&noisy_bits);
// let decoded_bytes = bits_to_bytes(&decoded_bits);
//
// let noisy_img = ImageBuffer::<Rgb<u8>, _>::from_raw(width, height, noisy_bytes)
// .expect("Erreur de reconstruction de l'image bruitée");
// noisy_img.save(noisy_out_path).unwrap();
//
// let decoded_img = ImageBuffer::<Rgb<u8>, _>::from_raw(width, height, decoded_bytes)
// .expect("Erreur de reconstruction de l'image décodée");
// decoded_img.save(decoded_out_path).unwrap();
//
// println!(
// "[*] Images sauvegardées : {} et {}",
// noisy_out_path, decoded_out_path
// );
// Ok(())
// }
pub fn transmit_image( pub fn transmit_image(
input_path: &str, input_path: &str,
noisy_out_path: &str, noisy_out_path: &str,
@@ -40,9 +139,9 @@ pub fn transmit_image(
decoder: &dyn Decoder, decoder: &dyn Decoder,
channel: &AwgnChannel, channel: &AwgnChannel,
) -> Result<()> { ) -> Result<()> {
println!("[*] Chargement de l'image : {}", input_path); println!("\n[*] Chargement de l'image : {}", input_path);
let img = image::open(input_path) let img = image::open(input_path)
.expect("Erreur de chargement de l'image") .expect("Erreur : Impossible de trouver ou lire l'image")
.to_rgb8(); .to_rgb8();
let (width, height) = img.dimensions(); let (width, height) = img.dimensions();
let raw_bytes = img.into_raw(); let raw_bytes = img.into_raw();
@@ -50,7 +149,6 @@ pub fn transmit_image(
let mut bits = bytes_to_bits(&raw_bytes); let mut bits = bytes_to_bits(&raw_bytes);
let original_bit_len = bits.len(); let original_bit_len = bits.len();
// Padding
let k = encoder.message_len(); let k = encoder.message_len();
let remainder = bits.len() % k; let remainder = bits.len() % k;
if remainder != 0 { if remainder != 0 {
@@ -58,60 +156,68 @@ pub fn transmit_image(
} }
let num_blocks = bits.len() / k; let num_blocks = bits.len() / k;
println!(" - Taille: {}x{} pixels", width, height); println!(" - Résolution : {}x{} pixels", width, height);
println!(" - Blocs à transmettre (k={}): {}", k, num_blocks); println!(
" - Poids total : {:.2} Mo",
raw_bytes.len() as f64 / 1_048_576.0
);
println!(" - Découpage en : {} blocs de {} bits\n", num_blocks, k);
let mut rng = rand::rngs::StdRng::seed_from_u64(42); let start_time = Instant::now();
let mut noisy_bits = Vec::with_capacity(num_blocks * k); let pb = ProgressBar::new(num_blocks as u64);
let mut decoded_bits = Vec::with_capacity(num_blocks * k); pb.set_style(
ProgressStyle::default_bar()
let mut frame_errors = 0; .template("{spinner:.green} [{elapsed_precise}] [{bar:40.cyan/blue}] {pos}/{len} blocs ({eta})")
.unwrap()
println!("[*] Transmission et Décodage en cours..."); .progress_chars("=>-")
);
for (i, block) in bits.chunks(k).enumerate() {
if i % 100 == 0 && i > 0 {
println!(" - Progession: {} / {} blocs...", i, num_blocks);
}
let codeword = encoder.encode(block)?;
let results: Vec<_> = bits
.par_chunks(k)
.map(|block| {
let mut rng = rand::thread_rng();
let codeword = encoder.encode(block).unwrap();
let rx_llr = channel.transmit(&codeword, &mut rng); let rx_llr = channel.transmit(&codeword, &mut rng);
// Sans correction LDPC
let hard_codeword: Vec<Gf2> = rx_llr let hard_codeword: Vec<Gf2> = rx_llr
.iter() .iter()
.map(|&l| if l < 0.0 { 1 } else { 0 }) .map(|&l| if l < 0.0 { 1 } else { 0 })
.collect(); .collect();
let noisy_block = encoder.extract_message(&hard_codeword); let noisy_block = encoder.extract_message(&hard_codeword);
noisy_bits.extend_from_slice(&noisy_block);
// Décodage LDPC
let res = decoder.decode(&rx_llr); let res = decoder.decode(&rx_llr);
if let Some(decoded_codeword) = res.codeword() {
let (decoded_block, has_error) = if let Some(decoded_codeword) = res.codeword() {
let decoded_msg = encoder.extract_message(decoded_codeword); let decoded_msg = encoder.extract_message(decoded_codeword);
decoded_bits.extend_from_slice(&decoded_msg); let err = if decoded_msg != block { 1 } else { 0 };
(decoded_msg, err)
if decoded_msg != block {
frame_errors += 1;
}
} else { } else {
decoded_bits.extend_from_slice(&noisy_block); (noisy_block.clone(), 1)
frame_errors += 1; };
} pb.inc(1);
(noisy_block, decoded_block, has_error)
})
.collect();
pb.finish_with_message("Décodage terminé !");
let mut noisy_bits = Vec::with_capacity(num_blocks * k);
let mut decoded_bits = Vec::with_capacity(num_blocks * k);
let mut frame_errors = 0;
for (n_block, d_block, err) in results {
noisy_bits.extend_from_slice(&n_block);
decoded_bits.extend_from_slice(&d_block);
frame_errors += err;
} }
println!( let duration = start_time.elapsed();
"[*] Transmission terminée. FER : {:.2}%", let fer = (frame_errors as f64 / num_blocks as f64) * 100.0;
(frame_errors as f64 / num_blocks as f64) * 100.0 println!("[*] Transmission terminée en {:.2?}", duration);
); println!(" - Taux d'Erreur Trame (FER) : {:.2}%", fer);
// Suppression du padding
noisy_bits.truncate(original_bit_len); noisy_bits.truncate(original_bit_len);
decoded_bits.truncate(original_bit_len); decoded_bits.truncate(original_bit_len);
// Reconstitution des images
let noisy_bytes = bits_to_bytes(&noisy_bits); let noisy_bytes = bits_to_bytes(&noisy_bits);
let decoded_bytes = bits_to_bytes(&decoded_bits); let decoded_bytes = bits_to_bytes(&decoded_bits);
@@ -124,7 +230,7 @@ pub fn transmit_image(
decoded_img.save(decoded_out_path).unwrap(); decoded_img.save(decoded_out_path).unwrap();
println!( println!(
"[*] Images sauvegardées : {} et {}", "[*] Succès ! Images sauvegardées : {} et {}",
noisy_out_path, decoded_out_path noisy_out_path, decoded_out_path
); );
Ok(()) Ok(())
+1
View File
@@ -1,4 +1,5 @@
pub mod benchmark; pub mod benchmark;
pub mod benchmark2;
pub mod channel; pub mod channel;
pub mod code; pub mod code;
pub mod decoder; pub mod decoder;
+102 -34
View File
@@ -5,49 +5,116 @@ use ldpc::decoder::{build_decoder, DecoderConfig, DecoderMethod};
use ldpc::encoder::{build_encoder, EncodingMethod}; use ldpc::encoder::{build_encoder, EncodingMethod};
use ldpc::image_sim::transmit_image; use ldpc::image_sim::transmit_image;
// fn main() -> ldpc::Result<()> {
// let n = 1944;
// let k = 972;
// let wc = 3;
// let wr = 6;
//
// println!("Transmission d'image via code LDPC");
//
// let code_mn = get_or_generate_cached_code(
// n,
// k,
// wc,
// wr,
// GenerationMethod::MacKayNeal { max_attempts: 5000 },
// )?;
//
// let mut code = code_mn;
// let encoder = build_encoder(&mut code, EncodingMethod::Systematic)?;
//
// let config = DecoderConfig {
// max_iterations: 50,
// early_stopping: true,
// };
//
// // Sum-Product
// let decoder = build_decoder(&code, DecoderMethod::SumProduct, config);
//
// let channel = AwgnChannel::new(2.0, code.rate())?;
//
// transmit_image(
// "test.png",
// "noisy_out.png",
// "decoded_out.png",
// &*encoder,
// &*decoder,
// &channel,
// )?;
//
// Ok(())
// }
use clap::Parser;
use ldpc::benchmark2::{run_massive_campaign, CampaignConfig, CodeScenario};
#[derive(Parser, Debug)]
#[command(author, version, about = "Laboratoire de test LDPC - TIPE")]
struct Args {
#[arg(short, long, default_value_t = 1000)]
trials: usize,
/// Générer les fichiers .dot pour visualiser les graphes de Tanner
#[arg(long, default_value_t = false)]
export_graph: bool,
}
fn main() -> ldpc::Result<()> { fn main() -> ldpc::Result<()> {
let n = 1944; let args = Args::parse();
let k = 972; let snrs = vec![1.0, 1.25, 1.5, 1.75, 2.0, 2.25, 2.5, 2.75, 3.0, 3.5, 4.0];
let wc = 3; let mut scenarios = Vec::new();
let wr = 6;
println!("Transmission d'image via code LDPC"); // -------------------------------------------------------------------------
// SCÉNARIO "TOY MODEL" : Spécial pour ton document Typst
// Un code minuscule R=1/2 avec n=24, k=12, m=12.
// Lisible sur une feuille A4.
// -------------------------------------------------------------------------
if args.export_graph {
scenarios.push(CodeScenario {
name: "ToyModel_Typst_N24".into(),
n: 24,
k: 12,
wc: 3,
wr: 6,
method: GenerationMethod::Gallager,
});
}
let code_mn = get_or_generate_cached_code( // Les vrais tests pour tes courbes de performances
n, scenarios.push(CodeScenario {
k, name: "Gallager_N1296_R05".into(),
wc, n: 1296,
wr, k: 648,
GenerationMethod::MacKayNeal { max_attempts: 5000 }, wc: 3,
)?; wr: 6,
method: GenerationMethod::Gallager,
});
let mut code = code_mn; scenarios.push(CodeScenario {
let encoder = build_encoder(&mut code, EncodingMethod::Systematic)?; name: "MacKay_N1296_R05".into(),
n: 1296,
k: 648,
wc: 3,
wr: 6,
method: GenerationMethod::MacKayNeal { max_attempts: 1000 },
});
let config = DecoderConfig { let config = CampaignConfig {
scenarios,
snr_range: snrs,
n_trials: args.trials,
max_iterations: 50, max_iterations: 50,
early_stopping: true, output_csv: "tipe_results.csv".into(),
export_graph: args.export_graph,
}; };
// Sum-Product run_massive_campaign(config)?;
let decoder = build_decoder(&code, DecoderMethod::SumProduct, config);
let channel = AwgnChannel::new(2.0, code.rate())?;
transmit_image(
"test.png",
"noisy_out.png",
"decoded_out.png",
&*encoder,
&*decoder,
&channel,
)?;
Ok(()) Ok(())
} }
// use ldpc::benchmark::{generate_valid_code, run_simulation}; // use ldpc::benchmark::run_simulation;
// use ldpc::code::GenerationMethod;
// //
// fn main() -> ldpc::Result<()> { // fn main() -> ldpc::Result<()> {
// let n = 1944; // let n = 1944;
@@ -59,17 +126,18 @@ fn main() -> ldpc::Result<()> {
// println!(); // println!();
// //
// println!("Test 1: Génération MacKayNeal\n"); // println!("Test 1: Génération MacKayNeal\n");
// let code_mn = generate_valid_code( // let code_mn = get_or_generate_cached_code(
// n, // n,
// k, // k,
// wc, // wc,
// wr, // wr,
// GenerationMethod::MacKayNeal { max_attempts: 1000 }, // GenerationMethod::MacKayNeal { max_attempts: 5000 },
// )?; // )?;
//
// run_simulation(code_mn)?; // run_simulation(code_mn)?;
// //
// println!("\nTest 2 : Génération Gallager\n"); // println!("\nTest 2 : Génération Gallager\n");
// let code_gal = generate_valid_code(n, k, wc, wr, GenerationMethod::Gallager)?; // let code_gal = get_or_generate_cached_code(n, k, wc, wr, GenerationMethod::Gallager)?;
// run_simulation(code_gal)?; // run_simulation(code_gal)?;
// //
// Ok(()) // Ok(())
BIN
View File
Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.7 KiB

After

Width:  |  Height:  |  Size: 984 KiB

BIN
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 5.7 KiB