mirror of https://github.com/meshcore-dev/MeshCore
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
164 lines
7.4 KiB
164 lines
7.4 KiB
// scenario_adaptive.cpp
|
|
// Validates the ADAPTIVE density-aware routing strategy against DEFAULT and
|
|
// PATH_SNR_HYBRID baselines.
|
|
//
|
|
// Test matrix:
|
|
// Topologies: FullMesh (dense), Chain (sparse), Grid (mixed)
|
|
// Node counts: 10 (sparse), 25 (medium), 50 (dense), 100 (very dense)
|
|
// SNR: 8dB nominal, 3dB stress
|
|
// Strategies: DEFAULT, PATH_SNR_HYBRID, ADAPTIVE
|
|
//
|
|
// Expected: ADAPTIVE matches or beats DEFAULT on delivery,
|
|
// beats HYBRID on airtime in dense configs,
|
|
// beats DEFAULT on latency in sparse/chain configs.
|
|
|
|
#include "TestRunner.h"
|
|
#include <cstdio>
|
|
#include <vector>
|
|
|
|
using namespace sim;
|
|
|
|
int main() {
|
|
printf("MeshCore ADAPTIVE Strategy Validation\n");
|
|
printf("======================================\n\n");
|
|
|
|
FILE* csv = fopen("adaptive_results.csv", "w");
|
|
if (csv) {
|
|
fprintf(csv, "label,topo,nodes,snr,strategy,"
|
|
"avg_delivery_rate,avg_latency_ms,avg_hops,total_airtime_ms\n");
|
|
}
|
|
|
|
auto csv_row = [&](const char* label, const TestResult& r) {
|
|
if (!csv) return;
|
|
const char* topo = r.tc.topo == TestCase::TopoType::FULL_MESH ? "FullMesh" :
|
|
r.tc.topo == TestCase::TopoType::CHAIN ? "Chain" : "Grid";
|
|
const char* strat = r.tc.strategy == RoutingStrategy::DEFAULT ? "DEFAULT" :
|
|
r.tc.strategy == RoutingStrategy::PATH_SNR_HYBRID ? "PATH_SNR_HYBRID" :
|
|
r.tc.strategy == RoutingStrategy::ADAPTIVE ? "ADAPTIVE" :
|
|
"SNR_WEIGHTED";
|
|
fprintf(csv, "%s,%s,%d,%.1f,%s,%.4f,%.2f,%.3f,%lld\n",
|
|
label, topo, r.tc.num_nodes, r.tc.channel_snr, strat,
|
|
r.stats.avg_delivery_rate, r.stats.avg_latency_ms,
|
|
r.stats.avg_hops, (long long)r.stats.total_airtime_ms);
|
|
};
|
|
|
|
TestRunner runner;
|
|
|
|
// -----------------------------------------------------------------------
|
|
// Helper: print a result row + delta vs baseline
|
|
// -----------------------------------------------------------------------
|
|
auto print_row = [](const char* label, const TestResult& r,
|
|
const TestResult* base = nullptr) {
|
|
const char* topo = r.tc.topo == TestCase::TopoType::FULL_MESH ? "FullMesh" :
|
|
r.tc.topo == TestCase::TopoType::CHAIN ? "Chain" : "Grid";
|
|
const char* strat = r.tc.strategy == RoutingStrategy::DEFAULT ? "DEFAULT " :
|
|
r.tc.strategy == RoutingStrategy::PATH_SNR_HYBRID ? "HYBRID " :
|
|
r.tc.strategy == RoutingStrategy::ADAPTIVE ? "ADAPTIVE " :
|
|
"SNR_W ";
|
|
printf(" %-28s | strat=%s | topo=%-8s | n=%-3d | snr=%+4.0f | dr=%5.1f%% | lat=%6.0fms | air=%7lldms",
|
|
label, strat, topo, r.tc.num_nodes, r.tc.channel_snr,
|
|
r.stats.avg_delivery_rate * 100.0f,
|
|
r.stats.avg_latency_ms,
|
|
(long long)r.stats.total_airtime_ms);
|
|
if (base) {
|
|
float dr_d = (r.stats.avg_delivery_rate - base->stats.avg_delivery_rate) * 100.0f;
|
|
float lat_pct = base->stats.avg_latency_ms > 0
|
|
? (r.stats.avg_latency_ms - base->stats.avg_latency_ms)
|
|
/ base->stats.avg_latency_ms * 100.0f : 0.0f;
|
|
float air_pct = base->stats.total_airtime_ms > 0
|
|
? (float)((long long)r.stats.total_airtime_ms - (long long)base->stats.total_airtime_ms)
|
|
/ (float)base->stats.total_airtime_ms * 100.0f : 0.0f;
|
|
printf(" Δdr=%+4.1f%% Δlat=%+5.0f%% Δair=%+5.0f%%",
|
|
dr_d, lat_pct, air_pct);
|
|
}
|
|
printf("\n");
|
|
};
|
|
|
|
// -----------------------------------------------------------------------
|
|
// Test block: run DEFAULT, HYBRID, ADAPTIVE for a given config
|
|
// -----------------------------------------------------------------------
|
|
struct Config {
|
|
TestCase::TopoType topo;
|
|
int nodes, rows, cols;
|
|
float snr;
|
|
const char* tag;
|
|
};
|
|
|
|
Config configs[] = {
|
|
// FullMesh — where density detection matters most
|
|
{ TestCase::TopoType::FULL_MESH, 10, 0, 0, 8.0f, "FM10_snr8" },
|
|
{ TestCase::TopoType::FULL_MESH, 25, 0, 0, 8.0f, "FM25_snr8" },
|
|
{ TestCase::TopoType::FULL_MESH, 50, 0, 0, 8.0f, "FM50_snr8" },
|
|
{ TestCase::TopoType::FULL_MESH, 100, 0, 0, 8.0f, "FM100_snr8" },
|
|
// FullMesh at poor SNR — density still dense, but collisions compound
|
|
{ TestCase::TopoType::FULL_MESH, 50, 0, 0, 3.0f, "FM50_snr3" },
|
|
// Chain — sparse; ADAPTIVE should promote SNR_WEIGHTED
|
|
{ TestCase::TopoType::CHAIN, 10, 0, 0, 8.0f, "CH10_snr8" },
|
|
{ TestCase::TopoType::CHAIN, 20, 0, 0, 8.0f, "CH20_snr8" },
|
|
{ TestCase::TopoType::CHAIN, 20, 0, 0, 3.0f, "CH20_snr3" },
|
|
// Grid — medium density; ADAPTIVE should hit MEDIUM tier
|
|
{ TestCase::TopoType::GRID, 16, 4, 4, 8.0f, "GR4x4_snr8" },
|
|
{ TestCase::TopoType::GRID, 25, 5, 5, 8.0f, "GR5x5_snr8" },
|
|
{ TestCase::TopoType::GRID, 25, 5, 5, 6.0f, "GR5x5_snr6" },
|
|
};
|
|
|
|
RoutingStrategy strats[] = {
|
|
RoutingStrategy::DEFAULT,
|
|
RoutingStrategy::PATH_SNR_HYBRID,
|
|
RoutingStrategy::ADAPTIVE,
|
|
};
|
|
|
|
for (auto& cfg : configs) {
|
|
printf("\n--- %s (topo=%s nodes=%d snr=%.0f) ---\n",
|
|
cfg.tag,
|
|
cfg.topo == TestCase::TopoType::FULL_MESH ? "FullMesh" :
|
|
cfg.topo == TestCase::TopoType::CHAIN ? "Chain" : "Grid",
|
|
cfg.nodes, cfg.snr);
|
|
|
|
TestResult base_result{};
|
|
bool have_base = false;
|
|
std::vector<TestResult> group;
|
|
|
|
for (auto& strat : strats) {
|
|
TestCase tc{};
|
|
char lbl[64];
|
|
snprintf(lbl, sizeof(lbl), "%s_%s", cfg.tag,
|
|
strat == RoutingStrategy::DEFAULT ? "DEFAULT" :
|
|
strat == RoutingStrategy::PATH_SNR_HYBRID ? "HYBRID" : "ADAPTIVE");
|
|
tc.name = lbl;
|
|
tc.num_nodes = cfg.nodes;
|
|
tc.channel_snr = cfg.snr;
|
|
tc.num_floods = 20;
|
|
tc.strategy = strat;
|
|
tc.topo = cfg.topo;
|
|
tc.grid_rows = cfg.rows;
|
|
tc.grid_cols = cfg.cols;
|
|
|
|
auto result = runner.run({tc})[0];
|
|
group.push_back(result);
|
|
|
|
if (strat == RoutingStrategy::DEFAULT) {
|
|
base_result = result;
|
|
have_base = true;
|
|
print_row(lbl, result);
|
|
} else {
|
|
print_row(lbl, result, have_base ? &base_result : nullptr);
|
|
}
|
|
csv_row(lbl, result);
|
|
}
|
|
}
|
|
|
|
// -----------------------------------------------------------------------
|
|
// Summary: how does ADAPTIVE perform vs DEFAULT across all configs?
|
|
// -----------------------------------------------------------------------
|
|
printf("\n======================================\n");
|
|
printf(" ADAPTIVE vs DEFAULT — aggregate\n");
|
|
printf(" See adaptive_results.csv for full data\n");
|
|
printf("======================================\n");
|
|
|
|
if (csv) {
|
|
fclose(csv);
|
|
printf("CSV written to adaptive_results.csv\n");
|
|
}
|
|
return 0;
|
|
}
|
|
|