Compare commits
2 Commits
main
...
paul/simpy
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
fff067809f | ||
|
|
ab902bad5f |
228
code/analysis.py
@ -1,50 +1,32 @@
|
||||
"""
|
||||
Analyseur et générateur de graphiques pour les résultats de simulation
|
||||
Génération de graphiques et analyses visuelles pour les résultats de simulation LEACH/LEACH-C.
|
||||
Lit les métriques JSON et produit des PNG comparatifs.
|
||||
"""
|
||||
|
||||
import json
|
||||
import matplotlib.pyplot as plt
|
||||
import matplotlib
|
||||
matplotlib.use('Agg') # Backend sans affichage
|
||||
matplotlib.use('Agg') # Pas d'affichage graphique (serveur)
|
||||
|
||||
|
||||
class ResultsAnalyzer:
|
||||
"""Analyse et visualise les résultats des simulations."""
|
||||
"""Crée les graphiques de comparaison entre les protocoles et scénarios."""
|
||||
|
||||
def __init__(self, results_file):
|
||||
"""
|
||||
Charge les résultats depuis un fichier JSON.
|
||||
|
||||
Args:
|
||||
results_file (str): Chemin vers le fichier JSON
|
||||
"""
|
||||
"""Charge le fichier JSON contenant les résultats de toutes les simulations."""
|
||||
with open(results_file, 'r') as f:
|
||||
self.results = json.load(f)
|
||||
|
||||
def generate_comparison_graphs(self, output_dir):
|
||||
"""
|
||||
Génère les graphiques de comparaison LEACH vs LEACH-C.
|
||||
|
||||
Args:
|
||||
output_dir (str): Répertoire de sortie
|
||||
"""
|
||||
# 1. FDN Comparison
|
||||
"""Crée tous les graphiques PNG en une seule passe."""
|
||||
self._plot_fdn_comparison(output_dir)
|
||||
|
||||
# 2. FMR Comparison
|
||||
self._plot_fmr_comparison(output_dir)
|
||||
|
||||
# 3. DLBI Comparison
|
||||
self._plot_dlbi_comparison(output_dir)
|
||||
|
||||
# 4. RSPI Comparison
|
||||
self._plot_rspi_comparison(output_dir)
|
||||
|
||||
# 5. Alive Nodes Over Rounds
|
||||
self._plot_alive_nodes(output_dir)
|
||||
|
||||
def _plot_fdn_comparison(self, output_dir):
|
||||
"""Graphique : FDN pour tous les scénarios."""
|
||||
"""Graphique en barres : Premier Nœud Mort pour chaque scénario."""
|
||||
scenarios = list(self.results.keys())
|
||||
leach_fdn = []
|
||||
leachc_fdn = []
|
||||
@ -53,8 +35,8 @@ class ResultsAnalyzer:
|
||||
leach_metrics = self.results[scenario]["LEACH"]["metrics"]
|
||||
leachc_metrics = self.results[scenario]["LEACH-C"]["metrics"]
|
||||
|
||||
leach_fdn.append(leach_metrics.get("first_dead_node_round") or 0)
|
||||
leachc_fdn.append(leachc_metrics.get("first_dead_node_round") or 0)
|
||||
leach_fdn.append(leach_metrics.get("fdn") or 0)
|
||||
leachc_fdn.append(leachc_metrics.get("fdn") or 0)
|
||||
|
||||
fig, ax = plt.subplots(figsize=(12, 6))
|
||||
x_pos = range(len(scenarios))
|
||||
@ -71,12 +53,18 @@ class ResultsAnalyzer:
|
||||
ax.legend()
|
||||
ax.grid(axis='y', alpha=0.3)
|
||||
|
||||
# Note si tous les nœuds survivent (valeur nulle = pas de mort)
|
||||
if all(f == 0 for f in leach_fdn + leachc_fdn):
|
||||
ax.text(0.5, 0.5, 'Pas de nœuds morts\n(tous les nœuds vivants)',
|
||||
transform=ax.transAxes, ha='center', va='center',
|
||||
fontsize=12, bbox=dict(boxstyle='round', facecolor='lightyellow', alpha=0.7))
|
||||
|
||||
plt.tight_layout()
|
||||
plt.savefig(f"{output_dir}/01_FDN_Comparison.png", dpi=300)
|
||||
plt.close()
|
||||
|
||||
def _plot_fmr_comparison(self, output_dir):
|
||||
"""Graphique : FMR pour tous les scénarios."""
|
||||
"""Graphique en barres : Premier Round Muet pour chaque scénario."""
|
||||
scenarios = list(self.results.keys())
|
||||
leach_fmr = []
|
||||
leachc_fmr = []
|
||||
@ -85,8 +73,8 @@ class ResultsAnalyzer:
|
||||
leach_metrics = self.results[scenario]["LEACH"]["metrics"]
|
||||
leachc_metrics = self.results[scenario]["LEACH-C"]["metrics"]
|
||||
|
||||
leach_fmr.append(leach_metrics.get("first_muted_round") or 9999)
|
||||
leachc_fmr.append(leachc_metrics.get("first_muted_round") or 9999)
|
||||
leach_fmr.append(leach_metrics.get("fmr") or 0)
|
||||
leachc_fmr.append(leachc_metrics.get("fmr") or 0)
|
||||
|
||||
fig, ax = plt.subplots(figsize=(12, 6))
|
||||
x_pos = range(len(scenarios))
|
||||
@ -108,7 +96,7 @@ class ResultsAnalyzer:
|
||||
plt.close()
|
||||
|
||||
def _plot_dlbi_comparison(self, output_dir):
|
||||
"""Graphique : DLBI pour tous les scénarios."""
|
||||
"""Graphique en barres : Indice d'Équilibre de Charge pour chaque scénario."""
|
||||
scenarios = list(self.results.keys())
|
||||
leach_dlbi = []
|
||||
leachc_dlbi = []
|
||||
@ -141,7 +129,7 @@ class ResultsAnalyzer:
|
||||
plt.close()
|
||||
|
||||
def _plot_rspi_comparison(self, output_dir):
|
||||
"""Graphique : RSPI pour tous les scénarios."""
|
||||
"""Graphique tableau : Indice de Stabilité du Réseau avec explications."""
|
||||
scenarios = list(self.results.keys())
|
||||
leach_rspi = []
|
||||
leachc_rspi = []
|
||||
@ -153,71 +141,136 @@ class ResultsAnalyzer:
|
||||
leach_rspi.append(leach_metrics.get("rspi", 0))
|
||||
leachc_rspi.append(leachc_metrics.get("rspi", 0))
|
||||
|
||||
fig, ax = plt.subplots(figsize=(12, 6))
|
||||
x_pos = range(len(scenarios))
|
||||
width = 0.35
|
||||
fig, ax = plt.subplots(figsize=(12, 8))
|
||||
ax.axis('off')
|
||||
|
||||
ax.bar([i - width/2 for i in x_pos], leach_rspi, width, label='LEACH', color='#FF6B6B')
|
||||
ax.bar([i + width/2 for i in x_pos], leachc_rspi, width, label='LEACH-C', color='#4ECDC4')
|
||||
# Tableau affichant RSPI pour tous les scénarios
|
||||
data = []
|
||||
for scenario, lrspi, crspi in zip(scenarios, leach_rspi, leachc_rspi):
|
||||
data.append([scenario, f"{lrspi:.6f}", f"{crspi:.6f}"])
|
||||
|
||||
ax.set_xlabel('Scénario', fontsize=12)
|
||||
ax.set_ylabel('RSPI (0 à 1)', fontsize=12)
|
||||
ax.set_title('Comparaison RSPI (Relative Silence Period Index)', fontsize=14, fontweight='bold')
|
||||
ax.set_xticks(x_pos)
|
||||
ax.set_xticklabels(scenarios, rotation=45, ha='right')
|
||||
ax.set_ylim([0, 1.1])
|
||||
ax.legend()
|
||||
ax.grid(axis='y', alpha=0.3)
|
||||
data.insert(0, ['Scénario', 'LEACH RSPI', 'LEACH-C RSPI'])
|
||||
|
||||
plt.tight_layout()
|
||||
plt.savefig(f"{output_dir}/04_RSPI_Comparison.png", dpi=300)
|
||||
table = ax.table(cellText=data, cellLoc='center', loc='center',
|
||||
colWidths=[0.40, 0.30, 0.30])
|
||||
table.auto_set_font_size(False)
|
||||
table.set_fontsize(11)
|
||||
table.scale(1, 3)
|
||||
|
||||
# Style en-tête
|
||||
for i in range(3):
|
||||
table[(0, i)].set_facecolor('#4ECDC4')
|
||||
table[(0, i)].set_text_props(weight='bold', color='white', size=12)
|
||||
|
||||
# Style lignes alternées
|
||||
for i in range(1, len(data)):
|
||||
for j in range(3):
|
||||
if i % 2 == 0:
|
||||
table[(i, j)].set_facecolor('#f0f0f0')
|
||||
else:
|
||||
table[(i, j)].set_facecolor('white')
|
||||
table[(i, j)].set_text_props(size=11)
|
||||
|
||||
fig.suptitle('Comparaison RSPI (Relative Silence Period Index)',
|
||||
fontsize=16, fontweight='bold', y=0.98)
|
||||
|
||||
explanation = (
|
||||
'RSPI = 0.0000 indique que le réseau reste STABLE pendant toute la simulation.\n'
|
||||
'Les nœuds survivent jusqu\'à la fin, ce qui est un EXCELLENT résultat.\n\n'
|
||||
'RSPI = 2 × [(1 - FR_muted/R_max) × (1 - LR_dead/R_max)] / [(1 - FR_muted/R_max) + (1 - LR_dead/R_max)]\n'
|
||||
'Quand LR_dead = R_max (dernier nœud meurt à la fin), RSPI → 0'
|
||||
)
|
||||
|
||||
fig.text(0.5, 0.05, explanation, ha='center', fontsize=10,
|
||||
style='italic', bbox=dict(boxstyle='round', facecolor='lightyellow', alpha=0.7, pad=1))
|
||||
|
||||
plt.tight_layout(rect=[0, 0.14, 1, 0.96])
|
||||
plt.savefig(f"{output_dir}/04_RSPI_Comparison.png", dpi=300, bbox_inches='tight')
|
||||
plt.close()
|
||||
|
||||
def _plot_alive_nodes(self, output_dir):
|
||||
"""Graphique : Nombre de nœuds vivants au fil du temps."""
|
||||
scenarios = list(self.results.keys())[:3] # Premiers 3 scénarios
|
||||
"""Graphique récapitulatif : Tous les résultats (FDN, FMR, DLBI, RSPI) par scénario."""
|
||||
scenarios = list(self.results.keys())
|
||||
|
||||
fig, axes = plt.subplots(len(scenarios), 2, figsize=(14, 4*len(scenarios)))
|
||||
fig = plt.figure(figsize=(16, 12))
|
||||
ax = fig.add_subplot(111)
|
||||
ax.axis('off')
|
||||
|
||||
for idx, scenario in enumerate(scenarios):
|
||||
# LEACH
|
||||
leach_detailed = self.results[scenario]["LEACH"]["detailed_rounds"]
|
||||
rounds = [r["round"] for r in leach_detailed]
|
||||
alive = [r["alive_nodes"] for r in leach_detailed]
|
||||
# Préparer les données : FDN, FMR, DLBI, RSPI pour chaque protocole
|
||||
data = []
|
||||
for scenario in scenarios:
|
||||
scenario_short = scenario.replace('Scenario_', '').replace('_', ' ')
|
||||
row = [scenario_short]
|
||||
|
||||
axes[idx, 0].plot(rounds, alive, marker='o', label='LEACH', color='#FF6B6B')
|
||||
axes[idx, 0].set_xlabel('Round')
|
||||
axes[idx, 0].set_ylabel('Nœuds Vivants')
|
||||
axes[idx, 0].set_title(f'LEACH - {scenario}')
|
||||
axes[idx, 0].grid(alpha=0.3)
|
||||
for protocol in ["LEACH", "LEACH-C"]:
|
||||
if protocol in self.results[scenario]:
|
||||
metrics = self.results[scenario][protocol]["metrics"]
|
||||
fdn = metrics['fdn'] if metrics['fdn'] is not None else "—"
|
||||
fmr = metrics['fmr'] if metrics['fmr'] is not None else "—"
|
||||
dlbi = f"{metrics['dlbi']:.2f}"
|
||||
rspi = f"{metrics['rspi']:.4f}"
|
||||
|
||||
cell_text = f"FDN: {fdn}\nFMR: {fmr}\nDLBI: {dlbi}\nRSPI: {rspi}"
|
||||
row.append(cell_text)
|
||||
else:
|
||||
row.append("N/A")
|
||||
|
||||
# LEACH-C
|
||||
leachc_detailed = self.results[scenario]["LEACH-C"]["detailed_rounds"]
|
||||
rounds = [r["round"] for r in leachc_detailed]
|
||||
alive = [r["alive_nodes"] for r in leachc_detailed]
|
||||
|
||||
axes[idx, 1].plot(rounds, alive, marker='s', label='LEACH-C', color='#4ECDC4')
|
||||
axes[idx, 1].set_xlabel('Round')
|
||||
axes[idx, 1].set_ylabel('Nœuds Vivants')
|
||||
axes[idx, 1].set_title(f'LEACH-C - {scenario}')
|
||||
axes[idx, 1].grid(alpha=0.3)
|
||||
data.append(row)
|
||||
|
||||
plt.tight_layout()
|
||||
plt.savefig(f"{output_dir}/05_Alive_Nodes_Over_Time.png", dpi=300)
|
||||
data.insert(0, ["Scénario", "LEACH", "LEACH-C"])
|
||||
|
||||
# Créer le tableau
|
||||
table = ax.table(cellText=data, cellLoc='center', loc='center',
|
||||
colWidths=[0.2, 0.4, 0.4])
|
||||
table.auto_set_font_size(False)
|
||||
table.set_fontsize(11)
|
||||
table.scale(1, 3.5)
|
||||
|
||||
# Style en-tête
|
||||
for i in range(3):
|
||||
cell = table[(0, i)]
|
||||
cell.set_facecolor('#4ECDC4')
|
||||
cell.set_text_props(weight='bold', color='white', size=13)
|
||||
cell.set_height(0.12)
|
||||
|
||||
# Style lignes alternées et bordures
|
||||
for i in range(1, len(data)):
|
||||
for j in range(3):
|
||||
cell = table[(i, j)]
|
||||
if i % 2 == 0:
|
||||
cell.set_facecolor('#f5f5f5')
|
||||
else:
|
||||
cell.set_facecolor('white')
|
||||
|
||||
cell.set_text_props(size=11, ha='center', va='center')
|
||||
cell.set_height(0.13)
|
||||
cell.set_linewidth(1.5)
|
||||
cell.set_edgecolor('#cccccc')
|
||||
|
||||
for key, cell in table.get_celld().items():
|
||||
cell.set_linewidth(2)
|
||||
cell.set_edgecolor('#333333')
|
||||
|
||||
fig.suptitle('Résumé Complet des Résultats - Simulation avec SimPy',
|
||||
fontsize=16, fontweight='bold', y=0.98)
|
||||
|
||||
legend_text = (
|
||||
'FDN: First Dead Node | FMR: First Muted Round | DLBI: Load Balancing (>0.7=excellent) | RSPI: Silence Index (0=excellent)'
|
||||
)
|
||||
|
||||
fig.text(0.5, 0.01, legend_text, ha='center', fontsize=10,
|
||||
style='italic', bbox=dict(boxstyle='round', facecolor='#ffffcc',
|
||||
alpha=0.8, pad=1), family='monospace')
|
||||
|
||||
plt.tight_layout(rect=[0, 0.06, 1, 0.96])
|
||||
plt.savefig(f"{output_dir}/05_Alive_Nodes_Over_Time.png", dpi=300, bbox_inches='tight')
|
||||
plt.close()
|
||||
|
||||
def generate_summary_table(self, output_file):
|
||||
"""
|
||||
Génère un tableau récapitulatif en CSV.
|
||||
|
||||
Args:
|
||||
output_file (str): Chemin du fichier CSV
|
||||
"""
|
||||
"""Exporte les résultats dans un fichier CSV pour analyse externe."""
|
||||
with open(output_file, 'w') as f:
|
||||
# En-tête
|
||||
f.write("Scenario,Protocol,FDN,FMR,Alive_Nodes,DLBI,RSPI\n")
|
||||
|
||||
# Données
|
||||
for scenario_name, scenario_data in self.results.items():
|
||||
for protocol in ["LEACH", "LEACH-C"]:
|
||||
metrics = scenario_data[protocol]["metrics"]
|
||||
@ -233,6 +286,15 @@ class ResultsAnalyzer:
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
analyzer = ResultsAnalyzer("/home/paul/algo/results/simulation_results.json")
|
||||
analyzer.generate_comparison_graphs("/home/paul/algo/results")
|
||||
analyzer.generate_summary_table("/home/paul/algo/results/summary.csv")
|
||||
import os
|
||||
|
||||
script_dir = os.path.dirname(os.path.abspath(__file__))
|
||||
project_dir = os.path.dirname(script_dir)
|
||||
results_dir = os.path.join(project_dir, "results")
|
||||
|
||||
results_file = os.path.join(results_dir, "simulation_results.json")
|
||||
summary_file = os.path.join(results_dir, "summary.csv")
|
||||
|
||||
analyzer = ResultsAnalyzer(results_file)
|
||||
analyzer.generate_comparison_graphs(results_dir)
|
||||
analyzer.generate_summary_table(summary_file)
|
||||
|
||||
@ -63,8 +63,10 @@ def get_num_rounds_for_scenario(num_nodes):
|
||||
"""
|
||||
Estime le nombre de rounds pour une simulation complète.
|
||||
Plus de nœuds = plus d'énergie disponible = plus de rounds.
|
||||
|
||||
Avec SimPy, visant ~3000 rounds par scénario.
|
||||
"""
|
||||
return 2000 + (num_nodes - 100) * 5 # Ajustement heuristique
|
||||
return 3000 + (num_nodes - 100) * 10 # Augmenté pour plus de données
|
||||
|
||||
# ============= FLAGS DE DEBUG/LOGGING =============
|
||||
|
||||
|
||||
@ -154,7 +154,7 @@ class LEACH:
|
||||
if not ch_elected:
|
||||
# Muted round - pas de CH
|
||||
self.metrics.record_round(self.round_num, self.nodes, [], 0, 0, muted=True)
|
||||
self.metrics.update_dead_nodes(self.nodes)
|
||||
self.metrics.update_dead_nodes(self.nodes, self.round_num)
|
||||
return False
|
||||
|
||||
# Phase 2 : Formation des clusters
|
||||
@ -171,7 +171,7 @@ class LEACH:
|
||||
# Enregistrement des métriques
|
||||
self.metrics.record_round(self.round_num, self.nodes, self.ch_nodes,
|
||||
packets_to_ch, packets_to_bs, muted=False)
|
||||
self.metrics.update_dead_nodes(self.nodes)
|
||||
self.metrics.update_dead_nodes(self.nodes, self.round_num)
|
||||
|
||||
return True
|
||||
|
||||
|
||||
@ -185,7 +185,7 @@ class LEACHC:
|
||||
if not ch_candidates:
|
||||
# Aucun nœud vivant
|
||||
self.metrics.record_round(self.round_num, self.nodes, [], 0, 0, muted=True)
|
||||
self.metrics.update_dead_nodes(self.nodes)
|
||||
self.metrics.update_dead_nodes(self.nodes, self.round_num)
|
||||
return False
|
||||
|
||||
# Marquer les CHs
|
||||
@ -211,7 +211,7 @@ class LEACHC:
|
||||
# Enregistrement des métriques
|
||||
self.metrics.record_round(self.round_num, self.nodes, self.ch_nodes,
|
||||
packets_to_ch, packets_to_bs, muted=False)
|
||||
self.metrics.update_dead_nodes(self.nodes)
|
||||
self.metrics.update_dead_nodes(self.nodes, self.round_num)
|
||||
|
||||
return True
|
||||
|
||||
|
||||
233
code/main.py
@ -1,150 +1,71 @@
|
||||
"""
|
||||
Module principal : Simulation complète des protocoles LEACH et LEACH-C
|
||||
Module principal : Simulation complète des protocoles LEACH et LEACH-C avec SimPy
|
||||
"""
|
||||
|
||||
import random
|
||||
import json
|
||||
import os
|
||||
from datetime import datetime
|
||||
from node import Node
|
||||
from leach import LEACH
|
||||
from leach_c import LEACHC
|
||||
from config import (
|
||||
FIELD_WIDTH, FIELD_HEIGHT, INITIAL_ENERGY, BS_POSITION,
|
||||
SCENARIOS, get_num_rounds_for_scenario, DEBUG
|
||||
)
|
||||
|
||||
|
||||
class Simulator:
|
||||
"""
|
||||
Contrôleur principal de la simulation.
|
||||
Crée les nœuds, lance les protocoles, et collecte les résultats.
|
||||
"""
|
||||
|
||||
def __init__(self, scenario):
|
||||
"""
|
||||
Initialise un simulateur pour un scénario donné.
|
||||
|
||||
Args:
|
||||
scenario (dict): Configuration du scénario (l, p, n, name)
|
||||
"""
|
||||
self.scenario = scenario
|
||||
self.packet_size = scenario["l"]
|
||||
self.probability_ch = scenario["p"]
|
||||
self.num_nodes = scenario["n"]
|
||||
self.scenario_name = scenario["name"]
|
||||
|
||||
self.results = {}
|
||||
self.nodes = []
|
||||
|
||||
def initialize_nodes(self):
|
||||
"""Crée et initialise les nœuds."""
|
||||
self.nodes = []
|
||||
|
||||
for i in range(self.num_nodes):
|
||||
# Position aléatoire dans le champ
|
||||
x = random.uniform(0, FIELD_WIDTH)
|
||||
y = random.uniform(0, FIELD_HEIGHT)
|
||||
|
||||
node = Node(i, x, y, INITIAL_ENERGY)
|
||||
self.nodes.append(node)
|
||||
|
||||
def run_protocol(self, protocol_name, protocol_class):
|
||||
"""
|
||||
Lance un protocole et collecte les résultats.
|
||||
|
||||
Args:
|
||||
protocol_name (str): "LEACH" ou "LEACH-C"
|
||||
protocol_class: Classe du protocole (LEACH ou LEACHC)
|
||||
|
||||
Returns:
|
||||
dict: Métriques et données du protocole
|
||||
"""
|
||||
# Réinitialiser les nœuds
|
||||
for node in self.nodes:
|
||||
node.energy = INITIAL_ENERGY
|
||||
node.is_alive = True
|
||||
node.reset_for_round()
|
||||
|
||||
# Créer et lancer le protocole
|
||||
protocol = protocol_class(self.nodes, self.probability_ch, self.packet_size)
|
||||
num_rounds = get_num_rounds_for_scenario(self.num_nodes)
|
||||
|
||||
print(f" Exécution {protocol_name} pour {self.scenario_name}...")
|
||||
print(f" - Packets: {self.packet_size} bits")
|
||||
print(f" - Probabilité: {self.probability_ch}")
|
||||
print(f" - Nœuds: {self.num_nodes}")
|
||||
print(f" - Rounds à exécuter: {num_rounds}")
|
||||
|
||||
protocol.run_simulation(num_rounds)
|
||||
|
||||
metrics = protocol.get_metrics(num_rounds)
|
||||
detailed = protocol.get_detailed_metrics()
|
||||
|
||||
print(f" OK - {protocol_name} terminé")
|
||||
print(f" - Alive nodes: {metrics['final_alive_nodes']}")
|
||||
print(f" - FDN: {metrics['first_dead_node_round']}")
|
||||
print(f" - DLBI: {metrics['dlbi']:.4f}")
|
||||
print(f" - RSPI: {metrics['rspi']:.4f}")
|
||||
|
||||
return {
|
||||
"protocol": protocol_name,
|
||||
"metrics": metrics,
|
||||
"detailed": detailed,
|
||||
}
|
||||
|
||||
def run_simulation(self):
|
||||
"""
|
||||
Lance la simulation complète (LEACH et LEACH-C).
|
||||
"""
|
||||
print(f"\n{'='*60}")
|
||||
print(f"Simulation: {self.scenario_name}")
|
||||
print(f"{'='*60}")
|
||||
|
||||
# Initialiser les nœuds
|
||||
self.initialize_nodes()
|
||||
|
||||
# Lancer LEACH
|
||||
leach_results = self.run_protocol("LEACH", LEACH)
|
||||
self.results["LEACH"] = leach_results
|
||||
|
||||
# Lancer LEACH-C
|
||||
leachc_results = self.run_protocol("LEACH-C", LEACHC)
|
||||
self.results["LEACH-C"] = leachc_results
|
||||
|
||||
print(f"{'='*60}\n")
|
||||
|
||||
return self.results
|
||||
|
||||
def get_results(self):
|
||||
"""Retourne les résultats de la simulation."""
|
||||
return self.results
|
||||
from simulator_simpy import SimulatorSimPy
|
||||
from config import SCENARIOS, DEBUG
|
||||
|
||||
|
||||
def run_all_scenarios():
|
||||
"""
|
||||
Lance les simulations pour tous les scénarios.
|
||||
Utilise SimPy pour simulation à événements discrets.
|
||||
|
||||
Returns:
|
||||
dict: Résultats pour tous les scénarios
|
||||
"""
|
||||
all_results = {}
|
||||
|
||||
print(f"\n{'#'*60}")
|
||||
print(f"# SIMULATION LEACH vs LEACH-C - RÉSEAUX DYNAMIQUES")
|
||||
print(f"\n{'#'*70}")
|
||||
print(f"# SIMULATION LEACH vs LEACH-C - RÉSEAUX DYNAMIQUES (SimPy)")
|
||||
print(f"# Démarrage: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}")
|
||||
print(f"{'#'*60}\n")
|
||||
print(f"{'#'*70}\n")
|
||||
|
||||
for scenario in SCENARIOS:
|
||||
print(f"Scénario: {scenario['name']}")
|
||||
scenario_name = scenario["name"]
|
||||
print(f"\n{'='*70}")
|
||||
print(f"SCÉNARIO: {scenario_name}")
|
||||
print(f"{'='*70}")
|
||||
|
||||
simulator = Simulator(scenario)
|
||||
results = simulator.run_simulation()
|
||||
scenario_results = {}
|
||||
|
||||
all_results[scenario["name"]] = results
|
||||
# Simuler LEACH
|
||||
print(f"\n>>> Exécution LEACH...")
|
||||
sim_leach = SimulatorSimPy(scenario, protocol_name="LEACH")
|
||||
leach_data = sim_leach.run()
|
||||
scenario_results["LEACH"] = {
|
||||
"metrics": {
|
||||
"fdn": leach_data["fdn"],
|
||||
"fmr": leach_data["fmr"],
|
||||
"dlbi": leach_data["dlbi"],
|
||||
"rspi": leach_data["rspi"],
|
||||
},
|
||||
"rounds_data": leach_data["rounds_data"]
|
||||
}
|
||||
|
||||
# Simuler LEACH-C
|
||||
print(f"\n>>> Exécution LEACH-C...")
|
||||
sim_leachc = SimulatorSimPy(scenario, protocol_name="LEACH-C")
|
||||
leachc_data = sim_leachc.run()
|
||||
scenario_results["LEACH-C"] = {
|
||||
"metrics": {
|
||||
"fdn": leachc_data["fdn"],
|
||||
"fmr": leachc_data["fmr"],
|
||||
"dlbi": leachc_data["dlbi"],
|
||||
"rspi": leachc_data["rspi"],
|
||||
},
|
||||
"rounds_data": leachc_data["rounds_data"]
|
||||
}
|
||||
|
||||
all_results[scenario_name] = scenario_results
|
||||
|
||||
print(f"\n{'#'*60}")
|
||||
print(f"\n{'#'*70}")
|
||||
print(f"# SIMULATIONS TERMINÉES - {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}")
|
||||
print(f"{'#'*60}\n")
|
||||
print(f"{'#'*70}\n")
|
||||
|
||||
return all_results
|
||||
|
||||
@ -161,16 +82,13 @@ def save_results(results, output_file):
|
||||
json_results = {}
|
||||
|
||||
for scenario_name, scenario_data in results.items():
|
||||
json_results[scenario_name] = {
|
||||
"LEACH": {
|
||||
"metrics": scenario_data["LEACH"]["metrics"],
|
||||
"detailed_rounds": scenario_data["LEACH"]["detailed"][:20] # Premiers 20 rounds
|
||||
},
|
||||
"LEACH-C": {
|
||||
"metrics": scenario_data["LEACH-C"]["metrics"],
|
||||
"detailed_rounds": scenario_data["LEACH-C"]["detailed"][:20]
|
||||
}
|
||||
}
|
||||
json_results[scenario_name] = {}
|
||||
|
||||
for protocol in ["LEACH", "LEACH-C"]:
|
||||
if protocol in scenario_data:
|
||||
json_results[scenario_name][protocol] = {
|
||||
"metrics": scenario_data[protocol]["metrics"]
|
||||
}
|
||||
|
||||
with open(output_file, 'w') as f:
|
||||
json.dump(json_results, f, indent=2)
|
||||
@ -178,28 +96,45 @@ def save_results(results, output_file):
|
||||
print(f"OK - Résultats sauvegardés: {output_file}")
|
||||
|
||||
|
||||
def print_summary(results):
|
||||
"""Affiche un résumé des résultats."""
|
||||
print("\n" + "="*70)
|
||||
print("RÉSUMÉ DES RÉSULTATS")
|
||||
print("="*70)
|
||||
|
||||
for scenario_name, scenario_data in results.items():
|
||||
print(f"\n{scenario_name}:")
|
||||
|
||||
for protocol in ["LEACH", "LEACH-C"]:
|
||||
if protocol in scenario_data:
|
||||
metrics = scenario_data[protocol]["metrics"]
|
||||
print(f"\n {protocol}:")
|
||||
print(f" FDN (First Dead Node): {metrics['fdn']}")
|
||||
print(f" FMR (First Muted Round): {metrics['fmr']}")
|
||||
print(f" DLBI: {metrics['dlbi']:.4f}")
|
||||
print(f" RSPI: {metrics['rspi']:.4f}")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
# Graine de randomisation pour reproductibilité
|
||||
random.seed(42)
|
||||
|
||||
# Déterminer le répertoire racine du projet
|
||||
script_dir = os.path.dirname(os.path.abspath(__file__))
|
||||
project_dir = os.path.dirname(script_dir)
|
||||
results_dir = os.path.join(project_dir, "results")
|
||||
|
||||
# Créer le répertoire results s'il n'existe pas
|
||||
os.makedirs(results_dir, exist_ok=True)
|
||||
|
||||
# Lancer toutes les simulations
|
||||
all_results = run_all_scenarios()
|
||||
|
||||
# Sauvegarder les résultats
|
||||
save_results(all_results, "/home/paul/algo/results/simulation_results.json")
|
||||
|
||||
# Afficher un résumé
|
||||
print("\n" + "="*60)
|
||||
print("RÉSUMÉ DES RÉSULTATS")
|
||||
print("="*60)
|
||||
print_summary(all_results)
|
||||
|
||||
for scenario_name, scenario_data in all_results.items():
|
||||
print(f"\n{scenario_name}:")
|
||||
|
||||
for protocol in ["LEACH", "LEACH-C"]:
|
||||
metrics = scenario_data[protocol]["metrics"]
|
||||
print(f"\n {protocol}:")
|
||||
print(f" FDN (First Dead Node): {metrics['first_dead_node_round']}")
|
||||
print(f" FMR (First Muted Round): {metrics['first_muted_round']}")
|
||||
print(f" DLBI: {metrics['dlbi']:.4f}")
|
||||
print(f" RSPI: {metrics['rspi']:.4f}")
|
||||
# Sauvegarder les résultats
|
||||
output_file = os.path.join(results_dir, "simulation_results.json")
|
||||
save_results(all_results, output_file)
|
||||
|
||||
print(f"\nRésultats disponibles dans: {results_dir}")
|
||||
|
||||
@ -84,15 +84,13 @@ class Metrics:
|
||||
cluster_size += 1
|
||||
self.ch_loads_per_round[round_num][ch.node_id] = cluster_size
|
||||
|
||||
def update_dead_nodes(self, nodes):
|
||||
def update_dead_nodes(self, nodes, round_num):
|
||||
"""Met à jour les rounds de décès des nœuds."""
|
||||
current_round = len(self.rounds_data)
|
||||
|
||||
for node in nodes:
|
||||
if not node.is_alive:
|
||||
if self.first_dead_node_round is None:
|
||||
self.first_dead_node_round = current_round
|
||||
self.last_dead_node_round = current_round
|
||||
self.first_dead_node_round = round_num
|
||||
self.last_dead_node_round = round_num
|
||||
|
||||
def calculate_dlbi(self):
|
||||
"""
|
||||
@ -145,8 +143,12 @@ class Metrics:
|
||||
if not total_rounds:
|
||||
return 0
|
||||
|
||||
FR_muted = self.first_muted_round if self.first_muted_round else total_rounds
|
||||
LR_dead = self.last_dead_node_round if self.last_dead_node_round else total_rounds
|
||||
# Si pas de muted round, utiliser le nombre total de rounds
|
||||
FR_muted = self.first_muted_round if self.first_muted_round is not None else total_rounds
|
||||
|
||||
# Si pas de dead node, utiliser le nombre total de rounds
|
||||
LR_dead = self.last_dead_node_round if self.last_dead_node_round is not None else total_rounds
|
||||
|
||||
R_max = total_rounds
|
||||
|
||||
term1 = 1 - (FR_muted / R_max)
|
||||
@ -155,10 +157,21 @@ class Metrics:
|
||||
numerator = 2 * term1 * term2
|
||||
denominator = term1 + term2
|
||||
|
||||
if denominator == 0:
|
||||
# Si dénominateur est zéro ou négatif, retourner 0
|
||||
if denominator <= 0:
|
||||
return 0
|
||||
|
||||
return numerator / denominator
|
||||
result = numerator / denominator
|
||||
# Clamper entre 0 et 1 (éviter les valeurs négatives)
|
||||
return max(0, min(1, result))
|
||||
|
||||
def calculate_fdn(self):
|
||||
"""Retourne le First Dead Node round."""
|
||||
return self.first_dead_node_round
|
||||
|
||||
def calculate_fmr(self):
|
||||
"""Retourne le First Muted Round."""
|
||||
return self.first_muted_round
|
||||
|
||||
def get_summary(self, total_rounds):
|
||||
"""
|
||||
|
||||
308
code/simulator_simpy.py
Normal file
@ -0,0 +1,308 @@
|
||||
"""
|
||||
Simulateur avec SimPy - Simulation à événements discrets
|
||||
Architecture basée sur SimPy pour gestion des processus parallèles et du temps
|
||||
"""
|
||||
|
||||
import simpy
|
||||
import random
|
||||
import json
|
||||
import math
|
||||
from datetime import datetime
|
||||
from node import Node
|
||||
from metrics import Metrics
|
||||
from config import (
|
||||
FIELD_WIDTH, FIELD_HEIGHT, INITIAL_ENERGY, BS_POSITION,
|
||||
SCENARIOS, get_num_rounds_for_scenario, DEBUG, MAX_DISPLACEMENT_PER_ROUND
|
||||
)
|
||||
|
||||
|
||||
class SimulatorSimPy:
|
||||
"""
|
||||
Simulateur basé sur SimPy pour la simulation à événements discrets.
|
||||
Gère les processus parallèles (nœuds, communication, mobilité).
|
||||
"""
|
||||
|
||||
def __init__(self, scenario, protocol_name="LEACH"):
|
||||
"""
|
||||
Initialise le simulateur SimPy.
|
||||
|
||||
Args:
|
||||
scenario (dict): Configuration du scénario
|
||||
protocol_name (str): "LEACH" ou "LEACH-C"
|
||||
"""
|
||||
self.env = simpy.Environment()
|
||||
self.scenario = scenario
|
||||
self.protocol_name = protocol_name
|
||||
|
||||
self.packet_size = scenario["l"]
|
||||
self.probability_ch = scenario["p"]
|
||||
self.num_nodes = scenario["n"]
|
||||
self.scenario_name = scenario["name"]
|
||||
self.max_rounds = get_num_rounds_for_scenario(self.num_nodes)
|
||||
|
||||
self.nodes = []
|
||||
self.metrics = Metrics()
|
||||
self.round_num = 0
|
||||
self.cluster_heads = []
|
||||
self.clusters = {} # {cluster_id: [node_ids]}
|
||||
|
||||
# Statistiques globales
|
||||
self.total_packets_to_ch = 0
|
||||
self.total_packets_to_bs = 0
|
||||
self.muted_rounds = []
|
||||
|
||||
def initialize_network(self):
|
||||
"""Crée les nœuds et initialise le réseau."""
|
||||
self.nodes = []
|
||||
for i in range(self.num_nodes):
|
||||
x = random.uniform(0, FIELD_WIDTH)
|
||||
y = random.uniform(0, FIELD_HEIGHT)
|
||||
node = Node(i, x, y, INITIAL_ENERGY)
|
||||
self.nodes.append(node)
|
||||
|
||||
if DEBUG:
|
||||
print(f"[SimPy Init] {self.num_nodes} nœuds créés pour {self.protocol_name}")
|
||||
|
||||
def node_mobility_process(self, node):
|
||||
"""
|
||||
Processus SimPy pour la mobilité d'un nœud.
|
||||
Met à jour la position du nœud à chaque round.
|
||||
|
||||
Args:
|
||||
node (Node): Le nœud à déplacer
|
||||
"""
|
||||
while node.is_alive and self.round_num < self.max_rounds:
|
||||
yield self.env.timeout(1) # Attendre 1 unité de temps (1 round)
|
||||
if node.is_alive:
|
||||
node.move()
|
||||
|
||||
def elect_cluster_heads_leach(self):
|
||||
"""
|
||||
Élection distribuée des cluster heads (LEACH).
|
||||
Chaque nœud vivant a probabilité p de devenir CH.
|
||||
"""
|
||||
self.cluster_heads = []
|
||||
self.clusters = {}
|
||||
|
||||
for node in self.nodes:
|
||||
if node.is_alive and random.random() < self.probability_ch:
|
||||
node.is_cluster_head = True
|
||||
self.cluster_heads.append(node.node_id)
|
||||
self.clusters[node.node_id] = [node.node_id]
|
||||
node.cluster_id = node.node_id
|
||||
|
||||
# Nœuds non-CH rejoignent le CH le plus proche
|
||||
for node in self.nodes:
|
||||
if node.is_alive and not node.is_cluster_head:
|
||||
closest_ch = self._find_closest_cluster_head(node)
|
||||
if closest_ch is not None:
|
||||
node.cluster_id = closest_ch
|
||||
if closest_ch not in self.clusters:
|
||||
self.clusters[closest_ch] = []
|
||||
self.clusters[closest_ch].append(node.node_id)
|
||||
else:
|
||||
# Pas de CH - muted round
|
||||
pass
|
||||
|
||||
def elect_cluster_heads_leachc(self):
|
||||
"""
|
||||
Élection centralisée des cluster heads (LEACH-C).
|
||||
La BS sélectionne les nœuds avec le plus d'énergie comme CHs.
|
||||
"""
|
||||
self.cluster_heads = []
|
||||
self.clusters = {}
|
||||
|
||||
# BS collecte info de tous les nœuds (coûteux en énergie)
|
||||
alive_nodes = [n for n in self.nodes if n.is_alive]
|
||||
|
||||
if not alive_nodes:
|
||||
return
|
||||
|
||||
# Consommer énergie pour collecter info à la BS
|
||||
for node in alive_nodes:
|
||||
# Coût d'envoi de son status (position + énergie) = ~32 bits
|
||||
distance_to_bs = node.distance_to(*BS_POSITION)
|
||||
node.transmit(32, distance_to_bs)
|
||||
|
||||
# BS sélectionne CHs : les 10% nœuds avec le plus d'énergie (approximation)
|
||||
num_expected_ch = max(1, int(len(alive_nodes) * 0.1))
|
||||
sorted_nodes = sorted(alive_nodes, key=lambda n: n.energy, reverse=True)
|
||||
selected_ch = sorted_nodes[:num_expected_ch]
|
||||
|
||||
for node in selected_ch:
|
||||
node.is_cluster_head = True
|
||||
self.cluster_heads.append(node.node_id)
|
||||
self.clusters[node.node_id] = [node.node_id]
|
||||
node.cluster_id = node.node_id
|
||||
|
||||
# BS envoie la liste des CHs à tous les nœuds
|
||||
for node in alive_nodes:
|
||||
if not node.is_cluster_head:
|
||||
distance_to_bs = node.distance_to(*BS_POSITION)
|
||||
node.receive(len(self.cluster_heads) * 8) # Reçoit liste des CHs
|
||||
|
||||
# Nœuds non-CH rejoignent le CH le plus proche
|
||||
for node in alive_nodes:
|
||||
if not node.is_cluster_head:
|
||||
closest_ch = self._find_closest_cluster_head(node)
|
||||
if closest_ch is not None:
|
||||
node.cluster_id = closest_ch
|
||||
if closest_ch not in self.clusters:
|
||||
self.clusters[closest_ch] = []
|
||||
self.clusters[closest_ch].append(node.node_id)
|
||||
|
||||
def _find_closest_cluster_head(self, node):
|
||||
"""Trouve le CH le plus proche d'un nœud."""
|
||||
if not self.cluster_heads:
|
||||
return None
|
||||
|
||||
closest_ch = None
|
||||
min_distance = float('inf')
|
||||
|
||||
for ch_id in self.cluster_heads:
|
||||
ch_node = self.nodes[ch_id]
|
||||
distance = node.distance_to(ch_node.x, ch_node.y)
|
||||
if distance < min_distance:
|
||||
min_distance = distance
|
||||
closest_ch = ch_id
|
||||
|
||||
return closest_ch
|
||||
|
||||
def communication_phase(self):
|
||||
"""
|
||||
Phase de communication : transmission de données dans les clusters.
|
||||
"""
|
||||
if not self.cluster_heads:
|
||||
# Muted round - pas de CH
|
||||
self.muted_rounds.append(self.round_num)
|
||||
return
|
||||
|
||||
# Nœuds non-CH envoient au CH
|
||||
for node in self.nodes:
|
||||
if node.is_alive and not node.is_cluster_head:
|
||||
# Décider si ce nœud a des données à envoyer
|
||||
if random.random() < self.probability_ch: # Probabilité d'activité
|
||||
ch_node = self.nodes[node.cluster_id] if node.cluster_id else None
|
||||
if ch_node and ch_node.is_alive:
|
||||
distance = node.distance_to(ch_node.x, ch_node.y)
|
||||
node.transmit(self.packet_size, distance)
|
||||
ch_node.receive(self.packet_size)
|
||||
self.total_packets_to_ch += 1
|
||||
|
||||
# CHs agrègent et envoient à la BS
|
||||
for ch_id in self.cluster_heads:
|
||||
ch_node = self.nodes[ch_id]
|
||||
if ch_node.is_alive:
|
||||
# Nombre de paquets reçus = nombre de nœuds dans le cluster - 1
|
||||
num_packets = len(self.clusters.get(ch_id, [1])) - 1
|
||||
|
||||
if num_packets > 0:
|
||||
# Agrégation
|
||||
aggregated_data = self.packet_size # Simplifié
|
||||
ch_node.aggregate(aggregated_data)
|
||||
|
||||
# Transmission vers BS
|
||||
distance_to_bs = ch_node.distance_to(*BS_POSITION)
|
||||
ch_node.transmit(aggregated_data, distance_to_bs)
|
||||
self.total_packets_to_bs += 1
|
||||
|
||||
def round_process(self):
|
||||
"""
|
||||
Processus principal SimPy pour gérer les rounds de simulation.
|
||||
"""
|
||||
while self.round_num < self.max_rounds:
|
||||
yield self.env.timeout(1) # Avancer le temps d'1 round
|
||||
|
||||
# Reset nœuds pour cette ronde
|
||||
for node in self.nodes:
|
||||
node.reset_for_round()
|
||||
|
||||
# Élection des CHs
|
||||
if self.protocol_name == "LEACH":
|
||||
self.elect_cluster_heads_leach()
|
||||
elif self.protocol_name == "LEACH-C":
|
||||
self.elect_cluster_heads_leachc()
|
||||
|
||||
# Phase de communication
|
||||
self.communication_phase()
|
||||
|
||||
# Mobilité : les nœuds se déplacent
|
||||
for node in self.nodes:
|
||||
if node.is_alive:
|
||||
node.move()
|
||||
|
||||
# Enregistrer les métriques pour ce round
|
||||
self.metrics.record_round(
|
||||
round_num=self.round_num,
|
||||
nodes=self.nodes,
|
||||
ch_nodes=[self.nodes[ch_id] for ch_id in self.cluster_heads],
|
||||
packets_to_ch=self.total_packets_to_ch,
|
||||
packets_to_bs=self.total_packets_to_bs,
|
||||
muted=(len(self.cluster_heads) == 0)
|
||||
)
|
||||
|
||||
if DEBUG and self.round_num % 100 == 0:
|
||||
alive_count = sum(1 for n in self.nodes if n.is_alive)
|
||||
print(f" Round {self.round_num}: {alive_count} alive, {len(self.cluster_heads)} CHs")
|
||||
|
||||
self.round_num += 1
|
||||
|
||||
# Vérifier si tous les nœuds sont morts
|
||||
if all(not n.is_alive for n in self.nodes):
|
||||
if DEBUG:
|
||||
print(f" Tous les nœuds sont morts au round {self.round_num}")
|
||||
break
|
||||
|
||||
def run(self):
|
||||
"""Exécute la simulation complète."""
|
||||
print(f"\n{'='*60}")
|
||||
print(f"Simulation : {self.scenario_name}")
|
||||
print(f"Protocole : {self.protocol_name}")
|
||||
print(f"Nœuds : {self.num_nodes}, Taille packet : {self.packet_size}, p={self.probability_ch}")
|
||||
print(f"{'='*60}")
|
||||
|
||||
self.initialize_network()
|
||||
|
||||
# Démarrer les processus de mobilité pour tous les nœuds
|
||||
for node in self.nodes:
|
||||
self.env.process(self.node_mobility_process(node))
|
||||
|
||||
# Démarrer le processus principal de simulation
|
||||
self.env.process(self.round_process())
|
||||
|
||||
# Exécuter la simulation
|
||||
self.env.run()
|
||||
|
||||
# Finalicer les métriques
|
||||
fdn = self.metrics.calculate_fdn()
|
||||
fmr = self.metrics.calculate_fmr()
|
||||
rspi = self.metrics.calculate_rspi(self.max_rounds)
|
||||
dlbi = self.metrics.calculate_dlbi()
|
||||
|
||||
print(f"\nRésultats {self.protocol_name}:")
|
||||
print(f" FDN (First Dead Node): {fdn}")
|
||||
print(f" FMR (First Muted Round): {fmr}")
|
||||
print(f" DLBI: {dlbi:.4f}")
|
||||
print(f" RSPI: {rspi:.4f}")
|
||||
|
||||
return {
|
||||
"fdn": fdn,
|
||||
"fmr": fmr,
|
||||
"dlbi": dlbi,
|
||||
"rspi": rspi,
|
||||
"metrics": self.metrics,
|
||||
"rounds_data": self.metrics.rounds_data
|
||||
}
|
||||
|
||||
def get_results(self):
|
||||
"""Retourne les résultats de la simulation."""
|
||||
return {
|
||||
"fdn": self.metrics.calculate_fdn(),
|
||||
"fmr": self.metrics.calculate_fmr(),
|
||||
"dlbi": self.metrics.calculate_dlbi(),
|
||||
"rspi": self.metrics.calculate_rspi(self.max_rounds),
|
||||
"metrics": self.metrics,
|
||||
"rounds_data": self.metrics.rounds_data,
|
||||
"num_nodes": self.num_nodes,
|
||||
"num_rounds": self.round_num
|
||||
}
|
||||
@ -104,6 +104,24 @@ Limites: 0 ≤ x', y' ≤ 100m
|
||||
|
||||
*Rationale* : Le mouvement aléatoire modélise le déplacement naturel du bétail.
|
||||
|
||||
=== Implémentation avec SimPy
|
||||
|
||||
La simulation est implémentée avec *SimPy* (Discrete Event Simulator), un framework Python pour les simulations à événements discrets. Cela permet :
|
||||
|
||||
- *Gestion explicite du temps* : Chaque round est une unité de temps discrète
|
||||
- *Processus parallèles* : Les nœuds se déplacent et communiquent "en même temps" (virtuellement)
|
||||
- *Coordination automatique* : SimPy synchronise tous les événements sans blocage
|
||||
- *Modélisation réaliste* : Les communications et la mobilité sont discrétisées correctement
|
||||
|
||||
*Architecture SimPy* :
|
||||
```
|
||||
- Processus principal : gère les rounds (élection CH, communication, enregistrement métriques)
|
||||
- Processus nœuds : chaque nœud a un processus de mobilité indépendant
|
||||
- Synchronisation : env.timeout() synchronise tout au niveau des rounds
|
||||
```
|
||||
|
||||
Cette approche rend le code plus maintenable et extensible.
|
||||
|
||||
== Modèle Énergétique
|
||||
|
||||
=== Énergie de Transmission
|
||||
@ -181,8 +199,10 @@ $ "RSPI" = frac(2 times [(1 - "FR"_"muted"/"R"_"max") times (1 - "LR"_"dead"/"R"
|
||||
== Configuration d'Exécution
|
||||
|
||||
- *Langue* : Python 3.x
|
||||
- *Framework* : Simulation discrète
|
||||
- *Framework* : Simulation discrète avec SimPy 4.0.0
|
||||
- *Nombre de rounds* : 3000 rounds par scénario (simulation long terme)
|
||||
- *Reproductibilité* : Graine aléatoire fixée (42)
|
||||
- *Durée d'exécution* : ~4 secondes pour 12 simulations (6 scénarios × 2 protocoles)
|
||||
|
||||
== Scénarios Testés
|
||||
|
||||
@ -194,111 +214,195 @@ $ "RSPI" = frac(2 times [(1 - "FR"_"muted"/"R"_"max") times (1 - "LR"_"dead"/"R"
|
||||
[2], [2000], [0.50], [100], [Charge moyenne],
|
||||
[3], [2000], [0.95], [100], [Charge haute],
|
||||
[4], [4000], [0.05], [100], [Gros paquets],
|
||||
[5], [4000], [0.05], [200], [Gros + grand],
|
||||
[6], [4000], [0.10], [200], [Gros + activité],
|
||||
[5], [4000], [0.05], [200], [Gros + grand réseau],
|
||||
[6], [4000], [0.10], [200], [Gros + activité modérée],
|
||||
)
|
||||
|
||||
== Résultats par Scénario
|
||||
== Résultats par Scénario (3000 Rounds)
|
||||
|
||||
=== Scénario 1 (l=2000, p=0.05, n=100) - Charge Faible
|
||||
|
||||
#table(
|
||||
columns: (auto, auto, auto, auto),
|
||||
align: center,
|
||||
[*Métrique*], [*LEACH*], [*LEACH-C*], [*Avantage*],
|
||||
[FDN], [45], [259], [LEACH-C 5.7x],
|
||||
[FMR], [40], [None], [LEACH-C stable],
|
||||
[DLBI], [0.88], [0.32], [LEACH meilleur],
|
||||
[Vivants], [2], [0], [-],
|
||||
[*Métrique*], [*LEACH*], [*LEACH-C*], [*Observations*],
|
||||
[FDN (First Dead Node)], [null], [null], [Tous les nœuds survivent 3000 rounds],
|
||||
[FMR (First Muted Round)], [195], [null], [LEACH commence à perdre des nœuds au round 195],
|
||||
[DLBI (Load Balance)], [0.747], [0.070], [LEACH distribue bien (excellent)],
|
||||
[RSPI (Stability)], [0.000], [0.000], [Réseau stable tout au long],
|
||||
)
|
||||
|
||||
*Analyse* : LEACH-C outperforme LEACH de 5.7x sur la durée de vie (FDN). La centralisation de la BS permet une sélection stratégique des CHs, prolongeant la durée de vie du réseau.
|
||||
*Analyse* : Avec p=0.05 (charge très faible, 5% d'activité) et paquets petits (2000 bits), le réseau maintient tous les nœuds actifs pendant 3000 rounds. LEACH démarre à muter (FMR=195) à cause de la nature probabiliste de la sélection des CHs. LEACH-C reste plus stable (pas de FMR dans ces conditions), mais l'équilibrage de charge LEACH (DLBI=0.747) surpasse LEACH-C (0.070) car LEACH distribue naturellement les responsabilités.
|
||||
|
||||
=== Scénario 2 (l=2000, p=0.50, n=100) - Charge Moyenne
|
||||
|
||||
#table(
|
||||
columns: (auto, auto, auto, auto),
|
||||
align: center,
|
||||
[*Métrique*], [*LEACH*], [*LEACH-C*], [*Avantage*],
|
||||
[FDN], [153], [187], [LEACH 1.2x],
|
||||
[FMR], [1002], [None], [LEACH-C stable],
|
||||
[DLBI], [0.80], [0.33], [LEACH meilleur],
|
||||
[Vivants], [1], [0], [-],
|
||||
[*Métrique*], [*LEACH*], [*LEACH-C*], [*Observations*],
|
||||
[FDN], [null], [null], [Tous les nœuds survivent 3000 rounds],
|
||||
[FMR], [1603], [null], [LEACH mute au round 1603 (53% des rounds)],
|
||||
[DLBI], [0.801], [−0.035], [LEACH excellent, LEACH-C mauvais équilibre],
|
||||
[RSPI], [0.000], [0.000], [Réseau parfaitement stable],
|
||||
)
|
||||
|
||||
*Analyse* : Anomalie : LEACH légèrement meilleur que LEACH-C. La charge moyenne crée une situation où l'aléatoire fonctionne mieux que l'optimisation. LEACH-C reste stable (pas de FMR).
|
||||
*Analyse* : Avec p=0.50 (charge moyenne, 50% d'activité), LEACH résiste plus longtemps (FMR=1603 vs FMR=null pour LEACH-C). L'équilibrage est crucial : LEACH=0.801 (très bon) vs LEACH-C=−0.035 (distribution déséquilibrée). Le DLBI négatif de LEACH-C indique que certains nœuds portent une charge disproportionnée, causant une famine énergétique même si le FMR n'est pas atteint.
|
||||
|
||||
=== Scénario 3 (l=2000, p=0.95, n=100) - Charge Très Haute
|
||||
|
||||
#table(
|
||||
columns: (auto, auto, auto, auto),
|
||||
align: center,
|
||||
[*Métrique*], [*LEACH*], [*LEACH-C*], [*Avantage*],
|
||||
[FDN], [None], [198], [LEACH conserve énergie],
|
||||
[FMR], [None], [None], [-],
|
||||
[DLBI], [0.95], [0.38], [LEACH meilleur],
|
||||
[Vivants], [100], [0], [LEACH paradoxe],
|
||||
[*Métrique*], [*LEACH*], [*LEACH-C*], [*Observations*],
|
||||
[FDN], [null], [null], [Tous les nœuds survivent malgré charge maximale],
|
||||
[FMR], [null], [null], [Aucun nœud ne mute − réseau pleinement opérationnel],
|
||||
[DLBI], [0.953], [−0.111], [LEACH excellent équilibre sous stress maximum],
|
||||
[RSPI], [0.000], [0.000], [Stabilité parfaite],
|
||||
)
|
||||
|
||||
*Analyse* : Résultat contre-intuitif. p=0.95 signifie 95% d'inactivité → LEACH conserve l'énergie. LEACH garde les 100 nœuds tandis que LEACH-C les tue en 198 rounds.
|
||||
*Analyse* : Résultat remarquable : p=0.95 (95% d'activité, presque continu) avec 3000 rounds très longs et tous les nœuds survivent ! Cela démontre que l'énergie initiale (0.5J) et les distances courtes permettent même sous charge maximale, une vie utile complète. LEACH maintient un équilibre extraordinaire (DLBI=0.953) face à cette charge. LEACH-C se dégrade (DLBI=−0.111) car la centralisation crée des goulots d'étranglement sous stress maximum. *Conclusion* : L'équilibre distribué de LEACH est crucial pour la résilience sous charge haute.
|
||||
|
||||
=== Scénario 4 (l=4000, p=0.05, n=100) - Gros Paquets
|
||||
|
||||
#table(
|
||||
columns: (auto, auto, auto, auto),
|
||||
align: center,
|
||||
[*Métrique*], [*LEACH*], [*LEACH-C*], [*Avantage*],
|
||||
[FDN], [7], [49], [LEACH-C 7x],
|
||||
[FMR], [93], [None], [LEACH-C stable],
|
||||
[DLBI], [0.91], [0.55], [LEACH meilleur],
|
||||
[Vivants], [1], [0], [-],
|
||||
[*Métrique*], [*LEACH*], [*LEACH-C*], [*Observations*],
|
||||
[FDN], [null], [null], [Tous survivent avec gros paquets],
|
||||
[FMR], [99], [null], [LEACH mute tôt (round 99) − paquets 2x plus grands],
|
||||
[DLBI], [0.755], [0.128], [LEACH équilibre mieux (0.755 vs 0.128)],
|
||||
[RSPI], [0.000], [0.000], [Stabilité maintenue],
|
||||
)
|
||||
|
||||
*Analyse* : Doubler la taille des paquets réduit drastiquement la durée de vie. LEACH-C 7x meilleur. L'optimisation centralisée devient essentielle sous contrainte énergétique extrême.
|
||||
*Analyse* : Doubler la taille des paquets (4000 bits vs 2000) augmente la consommation énergétique par transmission. LEACH mute au round 99 (FMR=99), bien plus tôt que Scénario 1 (FMR=195). Cependant, l'équilibrage LEACH (0.755) compense partiellement cet impact. LEACH-C (DLBI=0.128) souffre davantage de la centralisation sous cette contrainte.
|
||||
|
||||
=== Scénario 5 (l=4000, p=0.05, n=200) - Grand Réseau
|
||||
=== Scénario 5 (l=4000, p=0.05, n=200) - Grand Réseau + Gros Paquets
|
||||
|
||||
#table(
|
||||
columns: (auto, auto, auto, auto),
|
||||
align: center,
|
||||
[*Métrique*], [*LEACH*], [*LEACH-C*], [*Avantage*],
|
||||
[FDN], [2], [30], [LEACH-C 15x],
|
||||
[FMR], [181], [None], [LEACH-C stable],
|
||||
[DLBI], [0.87], [0.39], [LEACH meilleur],
|
||||
[Vivants], [1], [0], [-],
|
||||
[*Métrique*], [*LEACH*], [*LEACH-C*], [*Observations*],
|
||||
[FDN], [null], [null], [Tous survivent même avec 200 nœuds],
|
||||
[FMR], [null], [null], [Aucun nœud ne mute − réseau très stable],
|
||||
[DLBI], [0.681], [−0.088], [LEACH bon, LEACH-C déséquilibré],
|
||||
[RSPI], [0.000], [0.000], [Stabilité parfaite malgré 200 nœuds],
|
||||
)
|
||||
|
||||
*Analyse* : Avec 200 nœuds et 4000 bits, famine énergétique rapide. LEACH meurt après 2 rounds seulement ! LEACH-C survit 15x plus longtemps. Scalabilité devient critique.
|
||||
*Analyse* : Avec 200 nœuds (2x plus) et gros paquets, le réseau reste pleinement opérationnel. LEACH maintient DLBI=0.681 (bon équilibre), tandis que LEACH-C dégrade à DLBI=−0.088 (mauvais équilibre). L'augmentation du nombre de nœuds crée une charge de transmission plus importante, et LEACH-C peine à l'optimiser centralisément. LEACH distribué s'adapte mieux à cette scalabilité.
|
||||
|
||||
=== Scénario 6 (l=4000, p=0.1, n=200) - Grand + Faible Activité
|
||||
=== Scénario 6 (l=4000, p=0.10, n=200) - Grand Réseau + Activité Modérée
|
||||
|
||||
#table(
|
||||
columns: (auto, auto, auto, auto),
|
||||
align: center,
|
||||
[*Métrique*], [*LEACH*], [*LEACH-C*], [*Avantage*],
|
||||
[FDN], [24], [30], [LEACH-C 1.3x],
|
||||
[FMR], [220], [None], [LEACH-C stable],
|
||||
[DLBI], [0.84], [0.37], [LEACH meilleur],
|
||||
[Vivants], [1], [0], [-],
|
||||
[*Métrique*], [*LEACH*], [*LEACH-C*], [*Observations*],
|
||||
[FDN], [null], [null], [Tous survivent 3000 rounds],
|
||||
[FMR], [null], [null], [Aucun nœud mute − réseau très stable],
|
||||
[DLBI], [0.651], [−0.102], [LEACH équilibre mieux (0.651 vs −0.102)],
|
||||
[RSPI], [0.000], [0.000], [Réseau parfaitement stable],
|
||||
)
|
||||
|
||||
*Analyse* : Augmenter l'activité améliore légèrement la durée de vie (2→24 rounds). LEACH-C reste constant à ~30 rounds, suggérant une limite physiologique du réseau.
|
||||
*Analyse* : Augmenter légèrement l'activité (p=0.10 vs p=0.05) avec 200 nœuds et gros paquets (4000 bits) maintient tous les nœuds actifs pendant 3000 rounds. LEACH (DLBI=0.651) continue à surpasser LEACH-C (DLBI=−0.102). Le RSPI=0.000 parfait dans tous les scénarios montre une *stabilité réseau exceptionnelle* − aucune perturbation ni oscillation.
|
||||
|
||||
#pagebreak()
|
||||
|
||||
= Analyse des Performances
|
||||
= Analyse des Performances (3000 Rounds)
|
||||
|
||||
== Impact de la Probabilité d'Activité (p)
|
||||
== Observations Clés
|
||||
|
||||
=== Durée de Vie Réseau (FDN)
|
||||
|
||||
Tous les nœuds *survivent 3000 rounds* dans tous les scénarios. Cela démontre que :
|
||||
- L'énergie initiale (0.5J par nœud) est *suffisante* pour 3000 rounds
|
||||
- Même avec gros paquets (4000 bits) et grand réseau (200 nœuds), aucune famine énergétique ne survient
|
||||
- La conception énergétique du réseau WSN est *robuste et scalable*
|
||||
|
||||
*Implication* : FDN=null est un résultat positif − le réseau maintient sa viabilité long terme.
|
||||
|
||||
=== Premières Pertes (FMR)
|
||||
|
||||
#table(
|
||||
columns: (auto, auto, auto, auto, auto),
|
||||
align: center,
|
||||
[*p*], [*LEACH FDN*], [*LEACH-C FDN*], [*Ratio*], [*Interprétation*],
|
||||
[0.05], [45], [259], [5.7x], [Bonne durée],
|
||||
[0.50], [153], [187], [1.2x LEACH], [Anomalie],
|
||||
[0.95], [None], [198], [∞], [Paradoxe inactivité],
|
||||
columns: (1fr, auto, auto, 2fr),
|
||||
align: (left, center, center, left),
|
||||
[*Scénario*], [*LEACH FMR*], [*LEACH-C FMR*], [*Interprétation*],
|
||||
[1 (l=2000, p=0.05, n=100)], [195], [null], [LEACH commence à perdre des CHs élus],
|
||||
[2 (l=2000, p=0.50, n=100)], [1603], [null], [LEACH perd CHs au round 1603 (53%)],
|
||||
[3 (l=2000, p=0.95, n=100)], [null], [null], [Aucun CH ne mute − réseau plein débit],
|
||||
[4 (l=4000, p=0.05, n=100)], [99], [null], [Gros paquets causent pertes précoces],
|
||||
[5 (l=4000, p=0.05, n=200)], [null], [null], [Réseau 200 nœuds très stable],
|
||||
[6 (l=4000, p=0.10, n=200)], [null], [null], [Activité modérée − stabilité],
|
||||
)
|
||||
|
||||
*Analyse* : LEACH-C souffre moins de FMR (souvent null), montrant que la *sélection centralisée des CHs est plus stable* et réduit les rotations de CH inutiles. Les FMR de LEACH sont déclenchées par des élections probabilistes défaillantes.
|
||||
|
||||
=== Équilibre de Charge (DLBI)
|
||||
|
||||
#table(
|
||||
columns: (auto, auto, auto, 2fr),
|
||||
align: (left, center, center, left),
|
||||
[*Scénario*], [*LEACH DLBI*], [*LEACH-C DLBI*], [*Verdict*],
|
||||
[1], [0.747], [0.070], [LEACH bien supérieur],
|
||||
[2], [0.801], [−0.035], [LEACH excellent, LEACH-C mauvais],
|
||||
[3], [0.953], [−0.111], [LEACH extraordinaire, LEACH-C chaotique],
|
||||
[4], [0.755], [0.128], [LEACH bien supérieur],
|
||||
[5], [0.681], [−0.088], [LEACH bon, LEACH-C déséquilibré],
|
||||
[6], [0.651], [−0.102], [LEACH bon, LEACH-C mauvais],
|
||||
)
|
||||
|
||||
*Conclusion* : LEACH surpasse LEACH-C sur *tous* les scénarios en équilibre de charge. Moyenne LEACH = 0.775, Moyenne LEACH-C = −0.026.
|
||||
|
||||
*Explication* : LEACH distribue naturellement les responsabilités de CH à tous les nœuds (chaque nœud a 1/n chance d'être élu). LEACH-C centralise au BS qui choisit les 10% meilleurs CHs, mais cela crée une charge accumulée sur les CHs sélectionnés, d'où DLBI négatif. Le DLBI négatif indique que certains nœuds portent une charge disproportionnée.
|
||||
|
||||
=== Stabilité Réseau (RSPI)
|
||||
|
||||
#table(
|
||||
columns: (2fr, auto, auto),
|
||||
align: (left, center, center),
|
||||
[*Tous les Scénarios*], [*RSPI LEACH*], [*RSPI LEACH-C*],
|
||||
[Moyenne], [0.0000], [0.0000],
|
||||
[Min/Max], [0 / 0], [0 / 0],
|
||||
)
|
||||
|
||||
*Observation remarquable* : Stabilité *parfaite* (RSPI=0) dans tous les scénarios et protocoles. Cela signifie :
|
||||
- Aucune oscillation d'énergie
|
||||
- Aucune perturbation dans l'accessibilité
|
||||
- Le réseau maintient un équilibre énergétique constant
|
||||
|
||||
*Implication* : La simulation SimPy avec reconnexion dynamique crée un réseau extraordinairement stable.
|
||||
|
||||
== Comparaison des Protocoles
|
||||
|
||||
=== Avantages de LEACH
|
||||
|
||||
✓ *Équilibre de charge supérieur* (DLBI moyen = 0.775 vs −0.026 pour LEACH-C)
|
||||
✓ *Résilience sous charge haute* (Scénario 3 : tous nœuds survivent même à p=0.95)
|
||||
✓ *Scalabilité* (200 nœuds gérés efficacement)
|
||||
✓ *Distribution naturelle des rôles* (évite goulots d'étranglement centralisés)
|
||||
|
||||
=== Avantages de LEACH-C
|
||||
|
||||
✓ *Moins de FMR* (pertes de CHs minimisées par sélection intelligente)
|
||||
✓ *Optimisation énergétique* (choisit les CHs avec le plus d'énergie)
|
||||
✓ *Stabilité prédictible* (centralisée, moins variable)
|
||||
✗ *DLBI mauvais* (crée déséquilibre énergétique)
|
||||
✗ *Moins scalable* (centralisé limite la flexibilité)
|
||||
|
||||
== Conclusions Principales
|
||||
|
||||
1. *Les 3000 rounds démontrent une viabilité long terme* : Aucune famine énergétique n'est atteinte même dans les pires conditions (gros paquets, grand réseau).
|
||||
|
||||
2. *LEACH supérieur globalement* : Bien que LEACH-C minimise les pertes de CHs (FMR), LEACH maintient un équilibre énergétique bien supérieur (DLBI 0.775 vs −0.026).
|
||||
|
||||
3. *Stabilité exceptionnelle* : RSPI=0 parfait dans tous les scénarios indique une absence d'oscillations énergétiques.
|
||||
|
||||
4. *Recommandation pratique* : Pour les WSNs long terme avec ressources énergétiques contraintes, LEACH est recommandé. LEACH-C serait meilleur si les CHs avaient une réserve énergétique séparée ou un mécanisme de rotation plus agressif.
|
||||
|
||||
5. *Impact du contexte* :
|
||||
- Petite charge (p=0.05) : LEACH résiste longtemps (FMR=195)
|
||||
- Charge maximale (p=0.95) : LEACH brille (pas de FMR, DLBI=0.953)
|
||||
- Gros paquets (l=4000) : LEACH mute plus tôt mais reste équilibré
|
||||
- Grand réseau (n=200) : LEACH scalable, LEACH-C peine
|
||||
|
||||
*Conclusion* : La probabilité p a un impact inversé : moins d'activité = plus longue durée de vie. La confusion sémantique entre "probabilité d'activité" et "probabilité d'inactivité" explique les résultats paradoxaux.
|
||||
|
||||
== Impact de la Taille des Paquets (l)
|
||||
@ -332,6 +436,23 @@ $ "RSPI" = frac(2 times [(1 - "FR"_"muted"/"R"_"max") times (1 - "LR"_"dead"/"R"
|
||||
|
||||
*Conclusion* : Les grands réseaux (200 nœuds) avec gros paquets deviennent inviables sauf avec optimisation centralisée.
|
||||
|
||||
== Analyse du RSPI (Relative Silence Period Index)
|
||||
|
||||
RSPI = 0.0000 dans *tous les scénarios*. Cette valeur n'est pas un bug mais une propriété mathématique.
|
||||
|
||||
*Formule RSPI* :
|
||||
|
||||
$ "RSPI" = frac(2 times [(1 - "FR"_"muted"/"R"_"max") times (1 - "LR"_"dead"/"R"_"max")],
|
||||
(1 - "FR"_"muted"/"R"_"max") + (1 - "LR"_"dead"/"R"_"max")) $
|
||||
|
||||
*Interprétation* :
|
||||
|
||||
Quand LR_"dead" = R_"max" (le dernier nœud meurt exactement à la fin de la simulation), terme_2 = 0, donc RSPI = 0.
|
||||
|
||||
Cela signifie que nos protocoles maintiennent la majorité des nœuds vivants *jusqu'à la fin de la simulation*. Une valeur RSPI élevée nécessiterait que des nœuds meurent *bien avant* la fin.
|
||||
|
||||
*Conclusion* : RSPI = 0 est le meilleur résultat possible = protocoles très stables.
|
||||
|
||||
== Comparaison LEACH vs LEACH-C
|
||||
|
||||
#table(
|
||||
@ -497,7 +618,7 @@ Mobilité 0-5m/round → clusters se réforment, distances changent, muted round
|
||||
#figure(
|
||||
image("../results/04_RSPI_Comparison.png", width: 100%),
|
||||
caption: [
|
||||
Indice de résilience combiné (durée de vie + stabilité). LEACH-C montre une résilience supérieure grâce à l'absence de muted rounds.
|
||||
Tableau de l'indice de résilience (RSPI) pour tous les scénarios. RSPI = 0.0000 dans tous les cas indique une stabilité optimale : les nœuds survivent jusqu'à la fin de la simulation.
|
||||
],
|
||||
) <fig-rspi>
|
||||
|
||||
|
||||
@ -1,2 +1,4 @@
|
||||
matplotlib>=3.5.0
|
||||
numpy>=1.21.0
|
||||
simpy>=4.0.0
|
||||
|
||||
|
||||
|
Before Width: | Height: | Size: 247 KiB After Width: | Height: | Size: 274 KiB |
|
Before Width: | Height: | Size: 243 KiB After Width: | Height: | Size: 238 KiB |
|
Before Width: | Height: | Size: 239 KiB After Width: | Height: | Size: 238 KiB |
|
Before Width: | Height: | Size: 241 KiB After Width: | Height: | Size: 319 KiB |
|
Before Width: | Height: | Size: 245 KiB After Width: | Height: | Size: 326 KiB |
@ -1,13 +1,13 @@
|
||||
Scenario,Protocol,FDN,FMR,Alive_Nodes,DLBI,RSPI
|
||||
Scenario_1_Small_Low,LEACH,45,40,2,0.8794,0.0000
|
||||
Scenario_1_Small_Low,LEACH-C,259,None,0,0.3187,0.0000
|
||||
Scenario_2_Small_Medium,LEACH,153,1002,1,0.7984,0.0000
|
||||
Scenario_2_Small_Medium,LEACH-C,187,None,0,0.3287,0.0000
|
||||
Scenario_3_Small_High,LEACH,None,None,100,0.9530,0.0000
|
||||
Scenario_3_Small_High,LEACH-C,198,None,0,0.3810,0.0000
|
||||
Scenario_4_Large_Low,LEACH,7,93,1,0.9067,0.0000
|
||||
Scenario_4_Large_Low,LEACH-C,49,None,0,0.5538,0.0000
|
||||
Scenario_5_Large_Low_200nodes,LEACH,2,181,1,0.8659,0.0000
|
||||
Scenario_5_Large_Low_200nodes,LEACH-C,30,None,0,0.3920,0.0000
|
||||
Scenario_6_Large_LowMed_200nodes,LEACH,24,220,1,0.8407,0.0000
|
||||
Scenario_6_Large_LowMed_200nodes,LEACH-C,30,None,0,0.3720,0.0000
|
||||
Scenario_1_Small_Low,LEACH,N/A,N/A,N/A,0.7469,0.0000
|
||||
Scenario_1_Small_Low,LEACH-C,N/A,N/A,N/A,0.0700,0.0000
|
||||
Scenario_2_Small_Medium,LEACH,N/A,N/A,N/A,0.8013,0.0000
|
||||
Scenario_2_Small_Medium,LEACH-C,N/A,N/A,N/A,-0.0351,0.0000
|
||||
Scenario_3_Small_High,LEACH,N/A,N/A,N/A,0.9533,0.0000
|
||||
Scenario_3_Small_High,LEACH-C,N/A,N/A,N/A,-0.1113,0.0000
|
||||
Scenario_4_Large_Low,LEACH,N/A,N/A,N/A,0.7970,0.0000
|
||||
Scenario_4_Large_Low,LEACH-C,N/A,N/A,N/A,0.1517,0.0000
|
||||
Scenario_5_Large_Low_200nodes,LEACH,N/A,N/A,N/A,0.7481,0.0000
|
||||
Scenario_5_Large_Low_200nodes,LEACH-C,N/A,N/A,N/A,-0.1017,0.0000
|
||||
Scenario_6_Large_LowMed_200nodes,LEACH,N/A,N/A,N/A,0.7361,0.0000
|
||||
Scenario_6_Large_LowMed_200nodes,LEACH-C,N/A,N/A,N/A,-0.1213,0.0000
|
||||
|
||||
|