better routing solution
This commit is contained in:
1 parent
56cc4fced5
commit
37598479cc
10 files changed
+469
-66
No files matched your search
+16
-2
@@ -1,4 +1,4 @@
|
|||||||
function [obj] = initialize(obj, domain, agents, barrierGain, barrierExponent, minAlt, timestep, maxIter, obstacles, makePlots, makeVideo, useDoubleIntegrator, dampingCoeff, useFixedTopology, optimizeSensorPointing, useSinrComms, sinrThreshold, pathLossExponent, ambientTemp, centerFreq, bandwidth)
|
function [obj] = initialize(obj, domain, agents, barrierGain, barrierExponent, minAlt, timestep, maxIter, obstacles, makePlots, makeVideo, useDoubleIntegrator, dampingCoeff, useFixedTopology, optimizeSensorPointing, useSinrComms, sinrThreshold, pathLossExponent, ambientTemp, centerFreq, bandwidth, useRoutingTopology, routingFlowThreshold)
|
||||||
arguments (Input)
|
arguments (Input)
|
||||||
obj (1, 1) {mustBeA(obj, "miSim")};
|
obj (1, 1) {mustBeA(obj, "miSim")};
|
||||||
domain (1, 1) {mustBeGeometry};
|
domain (1, 1) {mustBeGeometry};
|
||||||
@@ -21,6 +21,8 @@ function [obj] = initialize(obj, domain, agents, barrierGain, barrierExponent, m
|
|||||||
ambientTemp (1, 1) double = 290.0;
|
ambientTemp (1, 1) double = 290.0;
|
||||||
centerFreq (1, 1) double = 2.4e9;
|
centerFreq (1, 1) double = 2.4e9;
|
||||||
bandwidth (1, 1) double = 20e6;
|
bandwidth (1, 1) double = 20e6;
|
||||||
|
useRoutingTopology (1, 1) logical = false;
|
||||||
|
routingFlowThreshold (1, 1) double = 0.02;
|
||||||
end
|
end
|
||||||
arguments (Output)
|
arguments (Output)
|
||||||
obj (1, 1) {mustBeA(obj, "miSim")};
|
obj (1, 1) {mustBeA(obj, "miSim")};
|
||||||
@@ -112,12 +114,24 @@ function [obj] = initialize(obj, domain, agents, barrierGain, barrierExponent, m
|
|||||||
obj.centerFreq = centerFreq;
|
obj.centerFreq = centerFreq;
|
||||||
obj.bandwidth = bandwidth;
|
obj.bandwidth = bandwidth;
|
||||||
|
|
||||||
% Compute adjacency matrix and network topology
|
% Set topology-selection algorithm and routing parameters
|
||||||
|
obj.useRoutingTopology = useRoutingTopology;
|
||||||
|
obj.routingFlowThreshold = routingFlowThreshold;
|
||||||
|
|
||||||
|
% Compute adjacency matrix and network topology. Lesser-neighbor always
|
||||||
|
% provides the low-level connectivity topology (basic traffic must be
|
||||||
|
% routable between any two drones at all times); routing mode overlays
|
||||||
|
% the bulk-data links on top of it. The routing LP uses linprog (no
|
||||||
|
% codegen support); the compiled path never sets the flag, so linprog
|
||||||
|
% is never compiled.
|
||||||
obj = obj.updateAdjacency();
|
obj = obj.updateAdjacency();
|
||||||
if obj.useFixedTopology
|
if obj.useFixedTopology
|
||||||
obj.constraintAdjacencyMatrix = obj.adjacency;
|
obj.constraintAdjacencyMatrix = obj.adjacency;
|
||||||
else
|
else
|
||||||
obj = obj.lesserNeighbor();
|
obj = obj.lesserNeighbor();
|
||||||
|
if coder.target('MATLAB') && obj.useRoutingTopology
|
||||||
|
obj = obj.routeTopology();
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
% Set up times to iterate over
|
% Set up times to iterate over
|
||||||
|
|||||||
@@ -147,6 +147,18 @@ else
|
|||||||
BANDWIDTH = 20e6;
|
BANDWIDTH = 20e6;
|
||||||
end
|
end
|
||||||
|
|
||||||
|
% Routing topology selection (optional columns; MATLAB sim only)
|
||||||
|
if isfield(scenario, 'useRoutingTopology')
|
||||||
|
USE_ROUTING_TOPOLOGY = logical(scenario.useRoutingTopology);
|
||||||
|
else
|
||||||
|
USE_ROUTING_TOPOLOGY = false;
|
||||||
|
end
|
||||||
|
if isfield(scenario, 'routingFlowThreshold')
|
||||||
|
ROUTING_FLOW_THRESHOLD = scenario.routingFlowThreshold;
|
||||||
|
else
|
||||||
|
ROUTING_FLOW_THRESHOLD = 0.02;
|
||||||
|
end
|
||||||
|
|
||||||
% ---- Build domain --------------------------------------------------------
|
% ---- Build domain --------------------------------------------------------
|
||||||
dom = rectangularPrism;
|
dom = rectangularPrism;
|
||||||
dom = dom.initialize([DOMAIN_MIN; DOMAIN_MAX], REGION_TYPE.DOMAIN, "Guidance Domain");
|
dom = dom.initialize([DOMAIN_MIN; DOMAIN_MAX], REGION_TYPE.DOMAIN, "Guidance Domain");
|
||||||
@@ -195,6 +207,7 @@ end
|
|||||||
obj = obj.initialize(dom, agentList, BARRIER_GAIN, BARRIER_EXPONENT, ...
|
obj = obj.initialize(dom, agentList, BARRIER_GAIN, BARRIER_EXPONENT, ...
|
||||||
MIN_ALT, TIMESTEP, MAX_ITER, obstacleList, false, false, ...
|
MIN_ALT, TIMESTEP, MAX_ITER, obstacleList, false, false, ...
|
||||||
USE_DOUBLE_INTEGRATOR, DAMPING_COEFF, USE_FIXED_TOPOLOGY, false, ...
|
USE_DOUBLE_INTEGRATOR, DAMPING_COEFF, USE_FIXED_TOPOLOGY, false, ...
|
||||||
USE_SINR_COMMS, SINR_THRESHOLD, PATH_LOSS_EXPONENT, AMBIENT_TEMP, CENTER_FREQ, BANDWIDTH);
|
USE_SINR_COMMS, SINR_THRESHOLD, PATH_LOSS_EXPONENT, AMBIENT_TEMP, CENTER_FREQ, BANDWIDTH, ...
|
||||||
|
USE_ROUTING_TOPOLOGY, ROUTING_FLOW_THRESHOLD);
|
||||||
|
|
||||||
end
|
end
|
||||||
@@ -111,11 +111,22 @@ if isfield(inits, 'bandwidth')
|
|||||||
else
|
else
|
||||||
bandwidth = 20e6;
|
bandwidth = 20e6;
|
||||||
end
|
end
|
||||||
|
if isfield(inits, 'useRoutingTopology')
|
||||||
|
useRoutingTopology = logical(inits.useRoutingTopology);
|
||||||
|
else
|
||||||
|
useRoutingTopology = false;
|
||||||
|
end
|
||||||
|
if isfield(inits, 'routingFlowThreshold')
|
||||||
|
routingFlowThreshold = inits.routingFlowThreshold;
|
||||||
|
else
|
||||||
|
routingFlowThreshold = 0.02;
|
||||||
|
end
|
||||||
|
|
||||||
% ---- Initialize simulation (plots and video disabled) ------------------------
|
% ---- Initialize simulation (plots and video disabled) ------------------------
|
||||||
obj = obj.initialize(dom, agentList, inits.barrierGain, inits.barrierExponent, ...
|
obj = obj.initialize(dom, agentList, inits.barrierGain, inits.barrierExponent, ...
|
||||||
inits.minAlt, inits.timestep, inits.maxIter, obstacleList, ...
|
inits.minAlt, inits.timestep, inits.maxIter, obstacleList, ...
|
||||||
false, false, useDoubleIntegrator, dampingCoeff, useFixedTopology, false, ...
|
false, false, useDoubleIntegrator, dampingCoeff, useFixedTopology, false, ...
|
||||||
useSinrComms, sinrThreshold, pathLossExponent, ambientTemp, centerFreq, bandwidth);
|
useSinrComms, sinrThreshold, pathLossExponent, ambientTemp, centerFreq, bandwidth, ...
|
||||||
|
useRoutingTopology, routingFlowThreshold);
|
||||||
|
|
||||||
end
|
end
|
||||||
+10
-1
@@ -30,6 +30,14 @@ classdef miSim
|
|||||||
ambientTemp = 290.0; % ambient temperature (K) for thermal noise N = k_B*T*B
|
ambientTemp = 290.0; % ambient temperature (K) for thermal noise N = k_B*T*B
|
||||||
centerFreq = 2.4e9; % carrier frequency (Hz); sets path-loss offset K = (4*pi*f_c/c)^2
|
centerFreq = 2.4e9; % carrier frequency (Hz); sets path-loss offset K = (4*pi*f_c/c)^2
|
||||||
bandwidth = 20e6; % channel bandwidth (Hz) for thermal noise N = k_B*T*B
|
bandwidth = 20e6; % channel bandwidth (Hz) for thermal noise N = k_B*T*B
|
||||||
|
% Topology-selection algorithm (requires useSinrComms; MATLAB sim only).
|
||||||
|
% false -> lesser-neighbor; true -> min-max-workload LP routing, which
|
||||||
|
% picks the links that route all sensing data to the network endpoints
|
||||||
|
% (agents 1 and the largest odd index) with minimal peak node workload.
|
||||||
|
useRoutingTopology = false;
|
||||||
|
routingFlowThreshold = 0.02; % prune links carrying less flow than this (units of one node's production)
|
||||||
|
routingAdjacencyMatrix = false(0, 0); % directed adjacency chosen by the routing LP: (i, j) = link i -> j
|
||||||
|
routingFlows = zeros(0, 0); % data flow carried by each directed link (units/timestep)
|
||||||
artifactName = "";
|
artifactName = "";
|
||||||
f; % main plotting tiled layout figure
|
f; % main plotting tiled layout figure
|
||||||
fPerf; % performance plot figure
|
fPerf; % performance plot figure
|
||||||
@@ -79,12 +87,13 @@ classdef miSim
|
|||||||
obj.obstacles = {rectangularPrism};
|
obj.obstacles = {rectangularPrism};
|
||||||
obj.agents = {agent};
|
obj.agents = {agent};
|
||||||
end
|
end
|
||||||
[obj] = initialize(obj, domain, agents, barrierGain, barrierExponent, minAlt, timestep, maxIter, obstacles, makePlots, makeVideo, useDoubleIntegrator, dampingCoeff, useFixedTopology, optimizeSensorPointing, useSinrComms, sinrThreshold, pathLossExponent, ambientTemp, centerFreq, bandwidth);
|
[obj] = initialize(obj, domain, agents, barrierGain, barrierExponent, minAlt, timestep, maxIter, obstacles, makePlots, makeVideo, useDoubleIntegrator, dampingCoeff, useFixedTopology, optimizeSensorPointing, useSinrComms, sinrThreshold, pathLossExponent, ambientTemp, centerFreq, bandwidth, useRoutingTopology, routingFlowThreshold);
|
||||||
[obj] = initializeFromCsv(obj, csvPath);
|
[obj] = initializeFromCsv(obj, csvPath);
|
||||||
[obj] = initializeFromInits(obj, initsPath);
|
[obj] = initializeFromInits(obj, initsPath);
|
||||||
[obj] = plotFromSimHist(obj, initsPath, histPath);
|
[obj] = plotFromSimHist(obj, initsPath, histPath);
|
||||||
[obj] = run(obj);
|
[obj] = run(obj);
|
||||||
[obj] = lesserNeighbor(obj);
|
[obj] = lesserNeighbor(obj);
|
||||||
|
[obj] = routeTopology(obj);
|
||||||
[obj] = constrainMotion(obj);
|
[obj] = constrainMotion(obj);
|
||||||
[sinr, grad] = sinrLink(obj, positions, rxIdx, txIdx);
|
[sinr, grad] = sinrLink(obj, positions, rxIdx, txIdx);
|
||||||
[obj] = partition(obj);
|
[obj] = partition(obj);
|
||||||
|
|||||||
+58
-58
@@ -6,7 +6,12 @@ function obj = plotGraph(obj)
|
|||||||
obj (1, 1) {mustBeA(obj, "miSim")};
|
obj (1, 1) {mustBeA(obj, "miSim")};
|
||||||
end
|
end
|
||||||
|
|
||||||
nAgents = size(obj.agents, 1);
|
% The maintained (low-level connectivity) links are always drawn as a
|
||||||
|
% dotted undirected graph: basic traffic must be routable between any
|
||||||
|
% two drones at all times. In routing mode the bulk-data links selected
|
||||||
|
% by the LP are overlaid as SOLID DIRECTED edges (arrow = transmitter ->
|
||||||
|
% receiver) on the same node layout.
|
||||||
|
G = graph(obj.constraintAdjacencyMatrix, "omitselfloops");
|
||||||
|
|
||||||
% Resolve the target axes for the network graph tile
|
% Resolve the target axes for the network graph tile
|
||||||
if isnan(obj.networkGraphIndex)
|
if isnan(obj.networkGraphIndex)
|
||||||
@@ -15,72 +20,67 @@ function obj = plotGraph(obj)
|
|||||||
ax = obj.f.Children(1).Children(obj.networkGraphIndex(1));
|
ax = obj.f.Children(1).Children(obj.networkGraphIndex(1));
|
||||||
end
|
end
|
||||||
|
|
||||||
% Build the graph to plot. The fixed-radius model is undirected. In SINR
|
% Plot graph object(s)
|
||||||
% mode the link set (lesser-neighbor topology) is kept exactly as-is, but
|
hold(ax, "on");
|
||||||
% each connection is given an orientation: of the two directional SINRs on
|
o = plot(ax, G, "LineStyle", "--", "EdgeColor", "g", "NodeColor", "k", "LineWidth", 2);
|
||||||
% a link, the larger one wins, and the edge is directed from that
|
if obj.useRoutingTopology
|
||||||
% direction's transmitter to its receiver (arrow = transmitter -> receiver).
|
% Bulk overlay: reuse the base layout's node coordinates and hide
|
||||||
|
% the overlay's own nodes/labels so only its edges show. Sizeable
|
||||||
|
% arrowheads so the flow direction reads at tile size.
|
||||||
|
D = digraph(obj.routingAdjacencyMatrix);
|
||||||
|
o = [o; plot(ax, D, "XData", o(1).XData, "YData", o(1).YData, ...
|
||||||
|
"LineStyle", "-", "EdgeColor", "g", "LineWidth", 2, ...
|
||||||
|
"ArrowSize", 12, "Marker", "none", "NodeLabel", {})];
|
||||||
|
end
|
||||||
|
hold(ax, "off");
|
||||||
|
nBase = size(o, 1);
|
||||||
|
if ~isnan(obj.networkGraphIndex) && size(obj.networkGraphIndex, 2) > 1
|
||||||
|
for ii = 2:size(obj.networkGraphIndex, 2)
|
||||||
|
o = [o; copyobj(o(1:nBase), obj.f.Children(1).Children(obj.networkGraphIndex(ii)))];
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
% In SINR comms mode, label each edge with the connection's SINR and report
|
||||||
|
% the connectivity threshold in the tile title. The fixed-radius model is
|
||||||
|
% left unchanged (no edge labels, title stays as set in firstPlotSetup).
|
||||||
if obj.useSinrComms
|
if obj.useSinrComms
|
||||||
|
endNodes = G.Edges.EndNodes;
|
||||||
|
nAgents = size(obj.agents, 1);
|
||||||
positions = zeros(nAgents, 3);
|
positions = zeros(nAgents, 3);
|
||||||
for kk = 1:nAgents
|
for kk = 1:nAgents
|
||||||
positions(kk, :) = obj.agents{kk}.pos;
|
positions(kk, :) = obj.agents{kk}.pos;
|
||||||
end
|
end
|
||||||
D = false(nAgents); % directed adjacency: D(tx, rx) = true
|
% Labels are bare numbers (no units) so they stay compact; GraphPlot
|
||||||
for aa = 1:(nAgents - 1)
|
% renders them horizontally, left-to-right. Dotted (low-level) edges
|
||||||
for bb = (aa + 1):nAgents
|
% are labelled with the binding (worse) directional SINR — a link is
|
||||||
if obj.constraintAdjacencyMatrix(aa, bb)
|
% feasible only if BOTH directions clear the threshold. Links that
|
||||||
sinrARx = obj.sinrLink(positions, aa, bb); % aa receives bb
|
% also carry a bulk overlay leave the label to the solid edge, which
|
||||||
sinrBRx = obj.sinrLink(positions, bb, aa); % bb receives aa
|
% shows the SINR of the transmission direction.
|
||||||
if sinrARx >= sinrBRx
|
|
||||||
D(bb, aa) = true; % better as bb -> aa (tx bb, rx aa)
|
|
||||||
else
|
|
||||||
D(aa, bb) = true; % better as aa -> bb (tx aa, rx bb)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
G = digraph(D);
|
|
||||||
else
|
|
||||||
G = graph(obj.constraintAdjacencyMatrix, "omitselfloops");
|
|
||||||
end
|
|
||||||
|
|
||||||
% Plot graph object (digraph renders arrowheads automatically)
|
|
||||||
hold(ax, "on");
|
|
||||||
o = plot(ax, G, "LineStyle", "--", "EdgeColor", "g", "NodeColor", "k", "LineWidth", 2);
|
|
||||||
hold(ax, "off");
|
|
||||||
if ~isnan(obj.networkGraphIndex) && size(obj.networkGraphIndex, 2) > 1
|
|
||||||
for ii = 2:size(obj.networkGraphIndex, 2)
|
|
||||||
o = [o; copyobj(o(1), obj.f.Children(1).Children(obj.networkGraphIndex(ii)))];
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
% Mark ground-station-connected nodes with an "x" (others keep the default
|
|
||||||
% dot). Ground stations attach to the two "router" agents: agent 1 and the
|
|
||||||
% agent with the largest ODD index (that is nAgents when odd, else
|
|
||||||
% nAgents-1). With one or two agents only agent 1 qualifies.
|
|
||||||
routers = unique([1, 2 * ceil(nAgents / 2) - 1]);
|
|
||||||
for oo = 1:numel(o)
|
|
||||||
highlight(o(oo), routers, "Marker", "x", "MarkerSize", 8);
|
|
||||||
end
|
|
||||||
|
|
||||||
% In SINR comms mode, label each directed edge with the better SINR that set
|
|
||||||
% its orientation and report the threshold in the tile title. The
|
|
||||||
% fixed-radius model is left unchanged (no edge labels, title from setup).
|
|
||||||
if obj.useSinrComms
|
|
||||||
endNodes = G.Edges.EndNodes; % each row [tx rx] for a directed edge
|
|
||||||
% Label with the SINR of the chosen (better) direction: the receiver is
|
|
||||||
% the edge target, the transmitter the edge source. Bare numbers (no
|
|
||||||
% units) keep the small tile uncluttered; GraphPlot draws labels
|
|
||||||
% horizontally, left-to-right.
|
|
||||||
edgeLabels = strings(size(endNodes, 1), 1);
|
edgeLabels = strings(size(endNodes, 1), 1);
|
||||||
for ee = 1:size(endNodes, 1)
|
for ee = 1:size(endNodes, 1)
|
||||||
sinrLin = obj.sinrLink(positions, endNodes(ee, 2), endNodes(ee, 1)); % rx, tx
|
lo = endNodes(ee, 1);
|
||||||
edgeLabels(ee) = sprintf("%.1f", 10 * log10(sinrLin));
|
hi = endNodes(ee, 2);
|
||||||
|
if obj.useRoutingTopology && (obj.routingAdjacencyMatrix(lo, hi) || obj.routingAdjacencyMatrix(hi, lo))
|
||||||
|
edgeLabels(ee) = ""; % solid overlay carries this link's label
|
||||||
|
else
|
||||||
|
sinrLoRx = obj.sinrLink(positions, lo, hi); % receiver lo (lower index)
|
||||||
|
sinrHiRx = obj.sinrLink(positions, hi, lo); % receiver hi (higher index)
|
||||||
|
edgeLabels(ee) = sprintf("%.1f", 10 * log10(min(sinrLoRx, sinrHiRx)));
|
||||||
|
end
|
||||||
end
|
end
|
||||||
o(1).EdgeLabel = edgeLabels;
|
o(1).EdgeLabel = edgeLabels;
|
||||||
% Threshold in the existing single-line title (not a subtitle) so the
|
if obj.useRoutingTopology
|
||||||
% small tile isn't compressed, and disable the hover toolbar so the
|
endNodesD = D.Edges.EndNodes; % each row [tx rx]
|
||||||
% R2026 "..." menu stops covering the readout.
|
bulkLabels = strings(size(endNodesD, 1), 1);
|
||||||
|
for ee = 1:size(endNodesD, 1)
|
||||||
|
sinrTx = obj.sinrLink(positions, endNodesD(ee, 2), endNodesD(ee, 1)); % rx, tx
|
||||||
|
bulkLabels(ee) = sprintf("%.1f", 10 * log10(sinrTx));
|
||||||
|
end
|
||||||
|
o(2).EdgeLabel = bulkLabels;
|
||||||
|
end
|
||||||
|
% Threshold goes in the existing single-line title (not a subtitle) so
|
||||||
|
% the small tile is not compressed further, and the hover toolbar is
|
||||||
|
% disabled so the R2026 "..." menu stops covering the readout.
|
||||||
title(ax, sprintf("Network Graph (\\geq %.1f dB)", obj.sinrThreshold));
|
title(ax, sprintf("Network Graph (\\geq %.1f dB)", obj.sinrThreshold));
|
||||||
ax.Toolbar.Visible = "off";
|
ax.Toolbar.Visible = "off";
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -0,0 +1,146 @@
|
|||||||
|
function obj = routeTopology(obj)
|
||||||
|
% ROUTETOPOLOGY Select the network topology by solving a min-max-workload
|
||||||
|
% routing LP (alternative to the lesser-neighbor algorithm).
|
||||||
|
%
|
||||||
|
% Every agent produces one unit of sensing data per timestep, all of which
|
||||||
|
% must reach one of the network endpoints (agents 1 and the largest odd
|
||||||
|
% index), which output data off-network for free (bottomless sinks). Data
|
||||||
|
% may split across paths, but relayed data accumulates at intermediate
|
||||||
|
% nodes, so each node experiences a workload: the share of a timestep it
|
||||||
|
% spends transmitting and receiving, with per-link effort flow/capacity and
|
||||||
|
% Shannon capacity c = log2(1 + SINR). The LP minimizes the maximum
|
||||||
|
% workload over all nodes, since the busiest node bounds system capacity.
|
||||||
|
%
|
||||||
|
% Candidate links are the threshold-feasible pairs in obj.adjacency (both
|
||||||
|
% directional SINRs clear sinrThreshold, per updateAdjacency). After the
|
||||||
|
% first solve, links carrying negligible flow (< routingFlowThreshold) are
|
||||||
|
% disallowed and the LP is re-solved so they cannot reappear: feasible but
|
||||||
|
% impractical links add system complexity for very little gain.
|
||||||
|
%
|
||||||
|
% The routed links are a HIGH-QUALITY OVERLAY on top of the low-level
|
||||||
|
% connectivity topology, not a replacement for it: basic command/telemetry
|
||||||
|
% traffic must always be routable between any two drones, so the caller
|
||||||
|
% runs lesserNeighbor first and this method ORs the bulk links into the
|
||||||
|
% maintained set. Both tiers are held above threshold by the CBF.
|
||||||
|
%
|
||||||
|
% Outputs (set on obj):
|
||||||
|
% routingAdjacencyMatrix directed adjacency, (i, j) = bulk link i -> j
|
||||||
|
% routingFlows flow carried by each directed link
|
||||||
|
% constraintAdjacencyMatrix existing (lesser-neighbor) links OR the
|
||||||
|
% symmetrized routed links (the CBF maintains
|
||||||
|
% both directions of every link)
|
||||||
|
%
|
||||||
|
% Uses linprog (Optimization Toolbox): MATLAB simulation only, no codegen.
|
||||||
|
|
||||||
|
arguments (Input)
|
||||||
|
obj (1, 1) {mustBeA(obj, "miSim")};
|
||||||
|
end
|
||||||
|
arguments (Output)
|
||||||
|
obj (1, 1) {mustBeA(obj, "miSim")};
|
||||||
|
end
|
||||||
|
|
||||||
|
nAgents = size(obj.agents, 1);
|
||||||
|
|
||||||
|
% Network endpoints: agent 1 and the largest ODD index (nAgents when
|
||||||
|
% odd, nAgents-1 when even); with one or two agents only agent 1.
|
||||||
|
endpoints = unique([1, 2 * ceil(nAgents / 2) - 1]);
|
||||||
|
isEndpoint = false(1, nAgents);
|
||||||
|
isEndpoint(endpoints) = true;
|
||||||
|
|
||||||
|
% Trivial network: every node is an endpoint, no bulk routing needed
|
||||||
|
% (the lesser-neighbor constraint topology is left untouched)
|
||||||
|
if all(isEndpoint)
|
||||||
|
obj.routingAdjacencyMatrix = false(nAgents);
|
||||||
|
obj.routingFlows = zeros(nAgents);
|
||||||
|
return;
|
||||||
|
end
|
||||||
|
|
||||||
|
positions = zeros(nAgents, 3);
|
||||||
|
for kk = 1:nAgents
|
||||||
|
positions(kk, :) = obj.agents{kk}.pos;
|
||||||
|
end
|
||||||
|
|
||||||
|
% Candidate directed links: threshold-feasible pairs, excluding
|
||||||
|
% endpoint-sourced links (endpoints only sink; allowing them to
|
||||||
|
% transmit only enables useless cycles). Capacity is the Shannon
|
||||||
|
% spectral efficiency of the transmission direction.
|
||||||
|
src = zeros(nAgents^2, 1);
|
||||||
|
dst = zeros(nAgents^2, 1);
|
||||||
|
cap = zeros(nAgents^2, 1);
|
||||||
|
nE = 0;
|
||||||
|
for ii = 1:nAgents
|
||||||
|
if isEndpoint(ii)
|
||||||
|
continue;
|
||||||
|
end
|
||||||
|
for jj = 1:nAgents
|
||||||
|
if jj == ii || ~obj.adjacency(ii, jj)
|
||||||
|
continue;
|
||||||
|
end
|
||||||
|
nE = nE + 1;
|
||||||
|
src(nE) = ii;
|
||||||
|
dst(nE) = jj;
|
||||||
|
cap(nE) = log2(1 + obj.sinrLink(positions, jj, ii)); % tx ii -> rx jj
|
||||||
|
end
|
||||||
|
end
|
||||||
|
src = src(1:nE);
|
||||||
|
dst = dst(1:nE);
|
||||||
|
cap = cap(1:nE);
|
||||||
|
|
||||||
|
% --- LP over x = [f_1 .. f_nE, W] ------------------------------------
|
||||||
|
% min W + eps*sum(f) (eps breaks ties toward sparse, cycle-free flow)
|
||||||
|
% s.t. flow conservation (per non-endpoint i): out(i) - in(i) = 1
|
||||||
|
% workload (per node i): sum(f_out/c) + sum(f_in/c) - W <= 0
|
||||||
|
% f >= 0
|
||||||
|
relays = find(~isEndpoint);
|
||||||
|
nRelays = numel(relays);
|
||||||
|
|
||||||
|
fObj = [1e-6 * ones(nE, 1); 1];
|
||||||
|
|
||||||
|
Aeq = zeros(nRelays, nE + 1);
|
||||||
|
for rr = 1:nRelays
|
||||||
|
Aeq(rr, 1:nE) = double(src == relays(rr))' - double(dst == relays(rr))';
|
||||||
|
end
|
||||||
|
beq = ones(nRelays, 1);
|
||||||
|
|
||||||
|
Aineq = zeros(nAgents, nE + 1);
|
||||||
|
for ii = 1:nAgents
|
||||||
|
Aineq(ii, 1:nE) = (double(src == ii)' + double(dst == ii)') ./ cap';
|
||||||
|
Aineq(ii, nE + 1) = -1;
|
||||||
|
end
|
||||||
|
bineq = zeros(nAgents, 1);
|
||||||
|
|
||||||
|
lb = zeros(nE + 1, 1);
|
||||||
|
options = optimoptions("linprog", "Display", "off");
|
||||||
|
|
||||||
|
[x, ~, exitflag] = linprog(fObj, Aineq, bineq, Aeq, beq, lb, [], options);
|
||||||
|
if exitflag <= 0
|
||||||
|
error("Routing LP infeasible: feasible links cannot route all data to an endpoint");
|
||||||
|
end
|
||||||
|
|
||||||
|
% Prune trivial links and re-solve with them disallowed, so negligible
|
||||||
|
% flows cannot reappear in the final solution
|
||||||
|
ub = inf(nE + 1, 1);
|
||||||
|
ub(x(1:nE) < obj.routingFlowThreshold) = 0;
|
||||||
|
[x, ~, exitflag] = linprog(fObj, Aineq, bineq, Aeq, beq, lb, ub, options);
|
||||||
|
if exitflag <= 0
|
||||||
|
error("Routing LP infeasible after pruning: routingFlowThreshold eliminated links needed to route all data to an endpoint");
|
||||||
|
end
|
||||||
|
|
||||||
|
% Extract the directed topology from the pruned solution
|
||||||
|
flows = x(1:nE);
|
||||||
|
obj.routingFlows = zeros(nAgents);
|
||||||
|
obj.routingAdjacencyMatrix = false(nAgents);
|
||||||
|
for ee = 1:nE
|
||||||
|
if flows(ee) > 1e-9
|
||||||
|
obj.routingFlows(src(ee), dst(ee)) = flows(ee);
|
||||||
|
obj.routingAdjacencyMatrix(src(ee), dst(ee)) = true;
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
% OR the bulk links into the low-level (lesser-neighbor) constraint
|
||||||
|
% topology already on obj: the CBF maintains both tiers, keeping the
|
||||||
|
% network connected for basic traffic while holding the routed links.
|
||||||
|
% Both directions of every link are maintained (feasibility requires
|
||||||
|
% both to clear the threshold anyway).
|
||||||
|
obj.constraintAdjacencyMatrix = obj.constraintAdjacencyMatrix | obj.routingAdjacencyMatrix | obj.routingAdjacencyMatrix';
|
||||||
|
end
|
||||||
+5
-1
@@ -42,9 +42,13 @@ function [obj] = run(obj)
|
|||||||
% plotting purposes, the real partitioning is done by the agents)
|
% plotting purposes, the real partitioning is done by the agents)
|
||||||
[obj.partitioning, obj.agents] = obj.agents{1}.partition(obj.agents, obj.domain.objective);
|
[obj.partitioning, obj.agents] = obj.agents{1}.partition(obj.agents, obj.domain.objective);
|
||||||
|
|
||||||
% Determine desired communications links
|
% Determine desired communications links: lesser-neighbor gives the
|
||||||
|
% low-level connectivity topology; routing mode overlays bulk links
|
||||||
if ~obj.useFixedTopology
|
if ~obj.useFixedTopology
|
||||||
obj = obj.lesserNeighbor();
|
obj = obj.lesserNeighbor();
|
||||||
|
if coder.target('MATLAB') && obj.useRoutingTopology
|
||||||
|
obj = obj.routeTopology();
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
% Log constraint adjacency for this timestep
|
% Log constraint adjacency for this timestep
|
||||||
|
|||||||
@@ -5,6 +5,15 @@ function validate(obj)
|
|||||||
arguments (Output)
|
arguments (Output)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
%% Mode Validators
|
||||||
|
if obj.useRoutingTopology && ~obj.useSinrComms
|
||||||
|
error("Routing topology requires SINR comms mode (useSinrComms)");
|
||||||
|
end
|
||||||
|
|
||||||
|
if obj.useRoutingTopology && obj.useFixedTopology
|
||||||
|
error("useRoutingTopology and useFixedTopology are mutually exclusive topology selectors");
|
||||||
|
end
|
||||||
|
|
||||||
%% Communications Network Validators
|
%% Communications Network Validators
|
||||||
if max(conncomp(graph(obj.adjacency))) ~= 1
|
if max(conncomp(graph(obj.adjacency))) ~= 1
|
||||||
error("Network is not connected");
|
error("Network is not connected");
|
||||||
|
|||||||
@@ -62,6 +62,7 @@ function writeInits(obj)
|
|||||||
"useDoubleIntegrator", obj.useDoubleIntegrator, "dampingCoeff", obj.dampingCoeff, "useFixedTopology", obj.useFixedTopology, ...
|
"useDoubleIntegrator", obj.useDoubleIntegrator, "dampingCoeff", obj.dampingCoeff, "useFixedTopology", obj.useFixedTopology, ...
|
||||||
"useSinrComms", obj.useSinrComms, "sinrThreshold", obj.sinrThreshold, "pathLossExponent", obj.pathLossExponent, "ambientTemp", obj.ambientTemp, ...
|
"useSinrComms", obj.useSinrComms, "sinrThreshold", obj.sinrThreshold, "pathLossExponent", obj.pathLossExponent, "ambientTemp", obj.ambientTemp, ...
|
||||||
"centerFreq", obj.centerFreq, "bandwidth", obj.bandwidth, "txPower", txPower, ... % SINR comms model parameters
|
"centerFreq", obj.centerFreq, "bandwidth", obj.bandwidth, "txPower", txPower, ... % SINR comms model parameters
|
||||||
|
"useRoutingTopology", obj.useRoutingTopology, "routingFlowThreshold", obj.routingFlowThreshold, ... % routing topology parameters
|
||||||
"tilt", tilt, "azimuth", azimuth, ... % joint sensor parameters
|
"tilt", tilt, "azimuth", azimuth, ... % joint sensor parameters
|
||||||
"alphaDist", alphaDist, "betaDist", betaDist, "alphaTilt", alphaTilt, "betaTilt", betaTilt, ... % sigmoid sensor parameters
|
"alphaDist", alphaDist, "betaDist", betaDist, "alphaTilt", alphaTilt, "betaTilt", betaTilt, ... % sigmoid sensor parameters
|
||||||
"lossExponent", lossExponent, "P_TX", P_TX, "BW", BW, "f_c", f_c, "G_RX_dBi", G_RX_dBi, "beamwidthExponent", beamwidthExponent, ... % RF sensor parameters
|
"lossExponent", lossExponent, "P_TX", P_TX, "BW", BW, "f_c", f_c, "G_RX_dBi", G_RX_dBi, "beamwidthExponent", beamwidthExponent, ... % RF sensor parameters
|
||||||
|
|||||||
+198
-2
@@ -105,6 +105,13 @@ classdef test_miSim < matlab.unittest.TestCase
|
|||||||
methods (Test)
|
methods (Test)
|
||||||
% Test methods
|
% Test methods
|
||||||
function miSim_initialization(tc)
|
function miSim_initialization(tc)
|
||||||
|
% Test flag: set true to exercise the min-max-workload LP routing
|
||||||
|
% overlay on this random scenario. Routing requires SINR comms, so
|
||||||
|
% the flag switches the whole SINR stack on (agents keep their
|
||||||
|
% default 0.1 W txPower); the permissive -30 dB threshold keeps
|
||||||
|
% random geometries fully connected.
|
||||||
|
useRoutingTopology = true;
|
||||||
|
|
||||||
% randomly create obstacles
|
% randomly create obstacles
|
||||||
nGeom = tc.minNumObstacles + randi(tc.maxNumObstacles - tc.minNumObstacles);
|
nGeom = tc.minNumObstacles + randi(tc.maxNumObstacles - tc.minNumObstacles);
|
||||||
tc.obstacles = cell(nGeom, 1);
|
tc.obstacles = cell(nGeom, 1);
|
||||||
@@ -240,7 +247,11 @@ classdef test_miSim < matlab.unittest.TestCase
|
|||||||
end
|
end
|
||||||
|
|
||||||
% Initialize the simulation
|
% Initialize the simulation
|
||||||
tc.testClass = tc.testClass.initialize(tc.domain, tc.agents, tc.barrierGain, tc.barrierExponent, tc.minAlt, tc.timestep, tc.maxIter, tc.obstacles, tc.makePlots, tc.makeVideo, tc.useDoubleIntegrator, tc.dampingCoeff, tc.useFixedTopology, tc.optimizeSensorPointing);
|
if useRoutingTopology
|
||||||
|
tc.testClass = tc.testClass.initialize(tc.domain, tc.agents, tc.barrierGain, tc.barrierExponent, tc.minAlt, tc.timestep, tc.maxIter, tc.obstacles, tc.makePlots, tc.makeVideo, tc.useDoubleIntegrator, tc.dampingCoeff, tc.useFixedTopology, tc.optimizeSensorPointing, true, -30.0, 2.0, 290, 2.4e9, 20e6, true, 0.02);
|
||||||
|
else
|
||||||
|
tc.testClass = tc.testClass.initialize(tc.domain, tc.agents, tc.barrierGain, tc.barrierExponent, tc.minAlt, tc.timestep, tc.maxIter, tc.obstacles, tc.makePlots, tc.makeVideo, tc.useDoubleIntegrator, tc.dampingCoeff, tc.useFixedTopology, tc.optimizeSensorPointing);
|
||||||
|
end
|
||||||
end
|
end
|
||||||
function miSim_run_rf_sensor(tc)
|
function miSim_run_rf_sensor(tc)
|
||||||
% randomly create obstacles
|
% randomly create obstacles
|
||||||
@@ -391,6 +402,13 @@ classdef test_miSim < matlab.unittest.TestCase
|
|||||||
tc.testClass = tc.testClass.run();
|
tc.testClass = tc.testClass.run();
|
||||||
end
|
end
|
||||||
function miSim_run(tc)
|
function miSim_run(tc)
|
||||||
|
% Test flag: set true to exercise the min-max-workload LP routing
|
||||||
|
% overlay on this random scenario. Routing requires SINR comms, so
|
||||||
|
% the flag switches the whole SINR stack on (agents keep their
|
||||||
|
% default 0.1 W txPower); the permissive -30 dB threshold keeps
|
||||||
|
% random geometries fully connected.
|
||||||
|
useRoutingTopology = true;
|
||||||
|
|
||||||
% randomly create obstacles
|
% randomly create obstacles
|
||||||
nGeom = tc.minNumObstacles + randi(tc.maxNumObstacles - tc.minNumObstacles);
|
nGeom = tc.minNumObstacles + randi(tc.maxNumObstacles - tc.minNumObstacles);
|
||||||
tc.obstacles = cell(nGeom, 1);
|
tc.obstacles = cell(nGeom, 1);
|
||||||
@@ -527,7 +545,11 @@ classdef test_miSim < matlab.unittest.TestCase
|
|||||||
end
|
end
|
||||||
|
|
||||||
% Initialize the simulation
|
% Initialize the simulation
|
||||||
tc.testClass = tc.testClass.initialize(tc.domain, tc.agents, tc.barrierGain, tc.barrierExponent, tc.minAlt, tc.timestep, tc.maxIter, tc.obstacles, tc.makePlots, tc.makeVideo, tc.useDoubleIntegrator, tc.dampingCoeff, tc.useFixedTopology, tc.optimizeSensorPointing);
|
if useRoutingTopology
|
||||||
|
tc.testClass = tc.testClass.initialize(tc.domain, tc.agents, tc.barrierGain, tc.barrierExponent, tc.minAlt, tc.timestep, tc.maxIter, tc.obstacles, tc.makePlots, tc.makeVideo, tc.useDoubleIntegrator, tc.dampingCoeff, tc.useFixedTopology, tc.optimizeSensorPointing, true, -30.0, 2.0, 290, 2.4e9, 20e6, true, 0.02);
|
||||||
|
else
|
||||||
|
tc.testClass = tc.testClass.initialize(tc.domain, tc.agents, tc.barrierGain, tc.barrierExponent, tc.minAlt, tc.timestep, tc.maxIter, tc.obstacles, tc.makePlots, tc.makeVideo, tc.useDoubleIntegrator, tc.dampingCoeff, tc.useFixedTopology, tc.optimizeSensorPointing);
|
||||||
|
end
|
||||||
|
|
||||||
% Write out initialization state
|
% Write out initialization state
|
||||||
tc.testClass.writeInits();
|
tc.testClass.writeInits();
|
||||||
@@ -959,6 +981,180 @@ classdef test_miSim < matlab.unittest.TestCase
|
|||||||
% Run the simulation
|
% Run the simulation
|
||||||
tc.testClass = tc.testClass.run();
|
tc.testClass = tc.testClass.run();
|
||||||
end
|
end
|
||||||
|
function test_routing_topology_3_agents(tc)
|
||||||
|
% Three near-collinear agents with the routing-LP topology
|
||||||
|
% selector. Endpoints are agents 1 and 3 (largest odd index);
|
||||||
|
% agent 2 is the only relay, so only its unit of data transits
|
||||||
|
% the network. Agent 2 sits slightly closer to agent 1, giving
|
||||||
|
% the 2->1 link strictly higher capacity than 2->3. The min-max
|
||||||
|
% optimum is then unique: node 2's transmit workload
|
||||||
|
% f21/c21 + f23/c23 is always the system max (endpoint receive
|
||||||
|
% time is a subset of it), so ALL flow goes over the better
|
||||||
|
% link. No split occurs because the receive side never binds
|
||||||
|
% with a single relay.
|
||||||
|
tc.minDimension = 10; % domain size
|
||||||
|
tc.domain = tc.domain.initialize([zeros(1, 3);tc.minDimension* ones(1, 3)], REGION_TYPE.DOMAIN, "Domain");
|
||||||
|
|
||||||
|
% make basic sensing objective
|
||||||
|
tc.domain.objective = tc.domain.objective.initialize(objectiveFunctionWrapper([2, 8; 8, 8]), tc.domain, tc.discretizationStep, tc.protectedRange);
|
||||||
|
|
||||||
|
% Initialize agent collision geometry. Asymmetric spacing: agent 2
|
||||||
|
% is 1.8 m from agent 1 and 2.2 m from agent 3 (both above the 1 m
|
||||||
|
% path-loss floor). The 1-3 link (4 m, with agent 2 interfering
|
||||||
|
% close-in) stays below the threshold in both directions.
|
||||||
|
tc.agents = {agent; agent; agent;};
|
||||||
|
tc.collisionRanges = .25 * ones(size(tc.agents));
|
||||||
|
dLeft = [1.8, 0, 0];
|
||||||
|
dRight = [2.2, 0, 0];
|
||||||
|
geometry1 = spherical;
|
||||||
|
geometry2 = geometry1;
|
||||||
|
geometry3 = geometry1;
|
||||||
|
geometry1 = geometry1.initialize(tc.domain.center - dLeft, tc.collisionRanges(1), REGION_TYPE.COLLISION);
|
||||||
|
geometry2 = geometry2.initialize(tc.domain.center, tc.collisionRanges(2), REGION_TYPE.COLLISION);
|
||||||
|
geometry3 = geometry3.initialize(tc.domain.center + dRight, tc.collisionRanges(3), REGION_TYPE.COLLISION);
|
||||||
|
|
||||||
|
% Initialize agent sensor model
|
||||||
|
tc.sensor = sigmoidSensor;
|
||||||
|
tc.sensor = tc.sensor.initialize(tc.minDimension / 2, 3, 15, 3);
|
||||||
|
|
||||||
|
% Initialize obstacles
|
||||||
|
tc.obstacles = {};
|
||||||
|
|
||||||
|
% Initialize agents
|
||||||
|
tc.maxIter = 50;
|
||||||
|
tc.commsRanges = 4 * ones(size(tc.agents)); % unused in SINR mode
|
||||||
|
tc.agents{1} = tc.agents{1}.initialize(tc.domain.center - dLeft, geometry1, tc.sensor, tc.commsRanges(1), tc.maxIter, tc.initialStepSize, tc.initialMaxAngleStepSize);
|
||||||
|
tc.agents{2} = tc.agents{2}.initialize(tc.domain.center, geometry2, tc.sensor, tc.commsRanges(2), tc.maxIter, tc.initialStepSize, tc.initialMaxAngleStepSize);
|
||||||
|
tc.agents{3} = tc.agents{3}.initialize(tc.domain.center + dRight, geometry3, tc.sensor, tc.commsRanges(3), tc.maxIter, tc.initialStepSize, tc.initialMaxAngleStepSize);
|
||||||
|
|
||||||
|
% SINR communications model parameters (same threshold logic as
|
||||||
|
% the lesser-neighbor 3-agent test: a couple dB above the
|
||||||
|
% rejected end-to-end link, below the chain-link binding)
|
||||||
|
useSinrComms = true;
|
||||||
|
txPower = [0.1, 0.1, 0.1]; % homogeneous transmit power (W)
|
||||||
|
pathLossExponent = 2.0;
|
||||||
|
ambientTemp = 290; % K
|
||||||
|
centerFreq = 2.4e9; % Hz
|
||||||
|
bandwidth = 20e6; % Hz
|
||||||
|
endBindingDb = 10 * log10(0.5^pathLossExponent); % ~ -6.02 dB
|
||||||
|
sinrThreshold = endBindingDb + 2.0; % ~ -4.02 dB
|
||||||
|
useRoutingTopology = true;
|
||||||
|
routingFlowThreshold = 0.02;
|
||||||
|
|
||||||
|
tc.agents{1}.txPower = txPower(1);
|
||||||
|
tc.agents{2}.txPower = txPower(2);
|
||||||
|
tc.agents{3}.txPower = txPower(3);
|
||||||
|
|
||||||
|
% Initialize the simulation with the routing-LP topology selector
|
||||||
|
tc.testClass = tc.testClass.initialize(tc.domain, tc.agents, tc.barrierGain, tc.barrierExponent, tc.minAlt, tc.timestep, tc.maxIter, tc.obstacles, tc.makePlots, tc.makeVideo, tc.useDoubleIntegrator, tc.dampingCoeff, tc.useFixedTopology, tc.optimizeSensorPointing, useSinrComms, sinrThreshold, pathLossExponent, ambientTemp, centerFreq, bandwidth, useRoutingTopology, routingFlowThreshold);
|
||||||
|
|
||||||
|
% Feasible (threshold) links form the 1-2-3 line; the routing LP
|
||||||
|
% then puts all of relay 2's data on the higher-capacity 2->1
|
||||||
|
% link and drops 2->3 entirely
|
||||||
|
tc.assertEqual(tc.testClass.routingAdjacencyMatrix, logical( ...
|
||||||
|
[ 0, 0, 0; ...
|
||||||
|
1, 0, 0; ...
|
||||||
|
0, 0, 0;]));
|
||||||
|
tc.assertEqual(tc.testClass.routingFlows(2, 1), 1.0, "AbsTol", 1e-6);
|
||||||
|
|
||||||
|
% Endpoints never transmit; total flow equals the relay's unit
|
||||||
|
tc.assertEqual(sum(tc.testClass.routingFlows, "all"), 1.0, "AbsTol", 1e-6);
|
||||||
|
|
||||||
|
% The CBF maintains the low-level (lesser-neighbor) connectivity
|
||||||
|
% line PLUS the routed bulk link (2->1, already part of the line):
|
||||||
|
% the network must stay connected for basic traffic even though
|
||||||
|
% bulk data only flows over 2->1
|
||||||
|
tc.assertEqual(tc.testClass.constraintAdjacencyMatrix, logical( ...
|
||||||
|
[ 1, 1, 0; ...
|
||||||
|
1, 1, 1; ...
|
||||||
|
0, 1, 1;]));
|
||||||
|
|
||||||
|
% Run the simulation
|
||||||
|
tc.testClass = tc.testClass.run();
|
||||||
|
end
|
||||||
|
function test_routing_topology_4_agents(tc)
|
||||||
|
% Four collinear agents, 2 m spacing. Endpoints are agents 1 and
|
||||||
|
% 3; relays are agents 2 and 4. Threshold-feasible links form the
|
||||||
|
% chain 1-2-3-4 (longer links are interference-limited below the
|
||||||
|
% threshold). Relay 4's only outlet is endpoint 3; relay 2 can
|
||||||
|
% reach both endpoints but 2->1 has strictly higher capacity than
|
||||||
|
% 2->3 (endpoint 3 is also loaded by relay 4's traffic), so the
|
||||||
|
% unique optimum is 2->1 and 4->3.
|
||||||
|
tc.minDimension = 10; % domain size
|
||||||
|
tc.domain = tc.domain.initialize([zeros(1, 3);tc.minDimension* ones(1, 3)], REGION_TYPE.DOMAIN, "Domain");
|
||||||
|
|
||||||
|
% make basic sensing objective
|
||||||
|
tc.domain.objective = tc.domain.objective.initialize(objectiveFunctionWrapper([2, 8; 8, 8]), tc.domain, tc.discretizationStep, tc.protectedRange);
|
||||||
|
|
||||||
|
% Initialize agent collision geometry: collinear at x = 2, 4, 6, 8
|
||||||
|
tc.agents = {agent; agent; agent; agent;};
|
||||||
|
tc.collisionRanges = .25 * ones(size(tc.agents));
|
||||||
|
d = [2.0, 0, 0];
|
||||||
|
positions = [tc.domain.center - 1.5 * d; ...
|
||||||
|
tc.domain.center - 0.5 * d; ...
|
||||||
|
tc.domain.center + 0.5 * d; ...
|
||||||
|
tc.domain.center + 1.5 * d;];
|
||||||
|
|
||||||
|
% Initialize agent sensor model
|
||||||
|
tc.sensor = sigmoidSensor;
|
||||||
|
tc.sensor = tc.sensor.initialize(tc.minDimension / 2, 3, 15, 3);
|
||||||
|
|
||||||
|
% Initialize obstacles
|
||||||
|
tc.obstacles = {};
|
||||||
|
|
||||||
|
% Initialize agents
|
||||||
|
tc.maxIter = 50;
|
||||||
|
tc.commsRanges = 4 * ones(size(tc.agents)); % unused in SINR mode
|
||||||
|
for aa = 1:4
|
||||||
|
geometry = spherical;
|
||||||
|
geometry = geometry.initialize(positions(aa, :), tc.collisionRanges(aa), REGION_TYPE.COLLISION);
|
||||||
|
tc.agents{aa} = tc.agents{aa}.initialize(positions(aa, :), geometry, tc.sensor, tc.commsRanges(aa), tc.maxIter, tc.initialStepSize, tc.initialMaxAngleStepSize);
|
||||||
|
tc.agents{aa}.txPower = 0.1;
|
||||||
|
end
|
||||||
|
|
||||||
|
% SINR communications model parameters
|
||||||
|
useSinrComms = true;
|
||||||
|
pathLossExponent = 2.0;
|
||||||
|
ambientTemp = 290; % K
|
||||||
|
centerFreq = 2.4e9; % Hz
|
||||||
|
bandwidth = 20e6; % Hz
|
||||||
|
endBindingDb = 10 * log10(0.5^pathLossExponent); % ~ -6.02 dB
|
||||||
|
sinrThreshold = endBindingDb + 2.0; % ~ -4.02 dB
|
||||||
|
useRoutingTopology = true;
|
||||||
|
routingFlowThreshold = 0.02;
|
||||||
|
|
||||||
|
% Initialize the simulation with the routing-LP topology selector
|
||||||
|
tc.testClass = tc.testClass.initialize(tc.domain, tc.agents, tc.barrierGain, tc.barrierExponent, tc.minAlt, tc.timestep, tc.maxIter, tc.obstacles, tc.makePlots, tc.makeVideo, tc.useDoubleIntegrator, tc.dampingCoeff, tc.useFixedTopology, tc.optimizeSensorPointing, useSinrComms, sinrThreshold, pathLossExponent, ambientTemp, centerFreq, bandwidth, useRoutingTopology, routingFlowThreshold);
|
||||||
|
|
||||||
|
% Unique optimum: 2->1 and 4->3
|
||||||
|
expectedDirected = false(4);
|
||||||
|
expectedDirected(2, 1) = true;
|
||||||
|
expectedDirected(4, 3) = true;
|
||||||
|
tc.assertEqual(tc.testClass.routingAdjacencyMatrix, expectedDirected);
|
||||||
|
|
||||||
|
% The CBF maintains the full lesser-neighbor chain (low-level
|
||||||
|
% connectivity) with the bulk links overlaid on it
|
||||||
|
tc.assertEqual(tc.testClass.constraintAdjacencyMatrix, logical( ...
|
||||||
|
[ 1, 1, 0, 0; ...
|
||||||
|
1, 1, 1, 0; ...
|
||||||
|
0, 1, 1, 1; ...
|
||||||
|
0, 0, 1, 1;]));
|
||||||
|
|
||||||
|
% Flow conservation at every relay: out - in = 1 unit produced
|
||||||
|
F = tc.testClass.routingFlows;
|
||||||
|
endpoints = [1, 3];
|
||||||
|
for aa = 1:4
|
||||||
|
if any(aa == endpoints)
|
||||||
|
continue;
|
||||||
|
end
|
||||||
|
tc.assertEqual(sum(F(aa, :)) - sum(F(:, aa)), 1.0, "AbsTol", 1e-6);
|
||||||
|
end
|
||||||
|
|
||||||
|
% Every surviving link carries at least the pruning threshold,
|
||||||
|
% and total delivered data equals the number of relays
|
||||||
|
tc.assertTrue(all(F(F > 0) >= tc.testClass.routingFlowThreshold));
|
||||||
|
tc.assertEqual(sum(F(:, endpoints), "all") - sum(F(endpoints, :), "all"), 2.0, "AbsTol", 1e-6);
|
||||||
|
end
|
||||||
function test_communications_constraint_fixed_radius(tc)
|
function test_communications_constraint_fixed_radius(tc)
|
||||||
% No obstacles
|
% No obstacles
|
||||||
% Fixed two agents initial conditions
|
% Fixed two agents initial conditions
|
||||||
|
|||||||
Reference in new issue
Block a user