DESERT 3.5.1
Loading...
Searching...
No Matches
uwranging_tokenbus.cpp
Go to the documentation of this file.
1//
2// Copyright (c) 2022 Regents of the SIGNET lab, University of Padova.
3// All rights reserved.
4//
5// Redistribution and use in source and binary forms, with or without
6// modification, are permitted provided that the following conditions
7// are met:
8// 1. Redistributions of source code must retain the above copyright
9// notice, this list of conditions and the following disclaimer.
10// 2. Redistributions in binary form must reproduce the above copyright
11// notice, this list of conditions and the following disclaimer in the
12// documentation and/or other materials provided with the distribution.
13// 3. Neither the name of the University of Padova (SIGNET lab) nor the
14// names of its contributors may be used to endorse or promote products
15// derived from this software without specific prior written permission.
16//
17// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
18// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
19// TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
20// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
21// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
22// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
23// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
24// OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
25// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
26// OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
27// ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28//
29
39#include "uwranging_tokenbus.h"
41#include <mac.h>
42#include <iostream>
43#include <tclcl.h>
44#include "least_squares.h"
45
46extern packet_t PT_UWTOKENBUS;
47extern packet_t PT_UWRANGING_TOKENBUS;
51static class UwRangingTokenBusModuleClass : public TclClass
52{
53
54public:
59 : TclClass("Module/UW/RANGING_TOKENBUS")
60 {
61 }
66 TclObject *
67 create(int argc, const char *const *argv)
68 {
69 return (new UwRangingTokenBus());
70 }
71
73
74// define a macro to print debug messages
75#define DEBUG(level, text) \
76 { \
77 if (debug >= level) \
78 { \
79 std::cout << NOW << " UwRangingTokenBus(" << node_id << "): " << text << std::endl; \
80 } \
81 }
82
83// default constructor
85 : UwTokenBus(),
86 dist_num(n_nodes * (n_nodes - 1) / 2),
87 epsilon(1e-6),
88 max_tt(5),
89 dist_map(),
90 times_mat(),
91 times_age(),
92 x_mat(),
93 time_last_range(0),
94 id_last_range(-1)
95{
96 bind("epsilon", (double *)&epsilon);
97 bind("max_tt", (double *)&max_tt);
98
99 times_mat.resize(n_nodes, std::vector<double>(n_nodes - 1, -1.0)); // initialize the matrix
100 times_age.resize(n_nodes, std::vector<int>(n_nodes - 1, 0));
101 distances.resize(dist_num, -1.0);
102
103 // initialize the distance map
104 dist_map.resize(n_nodes, std::vector<int>(n_nodes, -1));
105 int temp = 0;
106 for (size_t ni = 0; ni < n_nodes; ni++)
107 {
108 for (size_t nj = ni + 1; nj < n_nodes; nj++)
109 {
110 dist_map[NMOD(ni)][NMOD(nj)] = temp;
111 dist_map[NMOD(nj)][NMOD(ni)] = temp++;
112 }
113 }
114
115 x_mat.resize(2 * dist_num, std::vector<double>(dist_num, 0.0)); // initialize null matrix 2d x dist_num
116 size_t eq = 0; // counter from 0 to 2d as index
117 for (size_t ni = 0; ni < n_nodes; ni++)
118 {
119 x_mat[eq++][dist_map[NMOD(ni)][NMOD(ni + 1)]] = 2.0; // TWTT range measure with next node t(ni+1,ni)
120 for (size_t n = ni + 2; n < ni + n_nodes; n++) //[t(n,n-1) + t(n,ni) - t(n-1,ni),...] for n = [ni+2…ni+n_nodes-1]
121 {
122 x_mat[eq][dist_map[NMOD(n)][NMOD(n - 1)]] = 1.0;
123 x_mat[eq][dist_map[NMOD(n)][NMOD(ni)]] = 1.0;
124 x_mat[eq++][dist_map[NMOD(n - 1)][NMOD(ni)]] = -1.0;
125 }
126 }
127
128 /*uncomment to print the coefficient matrix for each node*/
129 // std::cout << std::endl;
130 // for (size_t i = 0; i < x_mat.size(); i++)
131 // {
132 // for (size_t j = 0; j < x_mat[i].size(); j++)
133 // {
134 // cout << x_mat[i][j] << " ";
135 // }
136 // std::cout << std::endl;
137 // }
138}
139
140
144
146{
147 hdr_cmn *ch = HDR_CMN(p);
148 hdr_mac *mach = HDR_MAC(p);
149 hdr_tokenbus *tbh = HDR_TOKENBUS(p);
150 int dest_mac = mach->macDA();
151 int pkt_token_id = tbh->tokenId();
152 int pkt_node_id = NMOD(pkt_token_id); // find which node the token is meant to
153 DEBUG(10, " Phy2MacEndRx(); pkt token_id : " << pkt_token_id << " macSA: " << mach->macSA() << " macDA: " << mach->macDA())
155 {
157 if (ch->error())
158 {
159 /* in case of errors I assume the token_id in the header might be corrupted
160 * so I won't consider it for rescheduling the timers
161 */
162 DEBUG(3, "Phy2MacEndRx() dropping pkt with errors")
163 if (ch->ptype() == PT_UWRANGING_TOKENBUS)
164 {incrXCtrlPktsRx();}
165 else {incrErrorPktsRx();}
166 Packet::free(p);
167 }
168 else
169 {
170 if (validToken(p))
171 {
172 token_pass_timer.force_cancel();
173 bus_idle_timer.force_cancel();
174 last_token_id_heard = pkt_token_id;
175
176 if (ch->ptype() == PT_UWRANGING_TOKENBUS)
177 {
178 DEBUG(4, " received a ping with pkt_token_id: " << pkt_token_id << " from ID " << NMOD(pkt_token_id - 1))
180 incrCtrlPktsRx();
181 if (pkt_token_id >= id_last_range)
182 {
183 if ((normId(id_last_range + 1) == pkt_token_id) && (tbrh->token_hold() >= 0)) //if false packet RX time is not meaningful
184 {
185 times_mat[NMOD(node_id)][NMOD(pkt_node_id - node_id - 2)] = (NOW - time_last_range - tbrh->token_hold()); // update times_mat according to rx time
186 times_age[NMOD(node_id)][NMOD(pkt_node_id - node_id - 2)] = pkt_token_id;
187 } // true if the token timestamp is valid
188
189 for (size_t i = 0; i < (tbrh->times()).size(); i++)
190 {
191 if(tbrh->times()[i] >= 0) {
192 times_mat[NMOD(pkt_node_id - 1)][i] = tbrh->times()[i]; //copy times data from packet
193 times_age[NMOD(pkt_node_id - 1)][i] = pkt_token_id;
194 }
195 }
196
197 if (debug > 10)
198 {
199 DEBUG(0, " updated times_mat[" << node_id << "][" << NMOD(pkt_node_id - node_id - 2)
200 << "]" << " = " << NOW - time_last_range - tbrh->token_hold()
201 << " and times_mat[" << NMOD(pkt_node_id - 1) << "] = ")
202 for (int i=0;i<times_mat[NMOD(pkt_token_id-1)].size();i++)
203 {
204 std::cout << times_mat.at(NMOD(pkt_token_id-1)).at(i) << " ";
205 }
206 std::cout << std::endl;
207 }
208 }
209
210 id_last_range = pkt_token_id;
211 time_last_range = NOW;
212 }
213
214 if (pkt_node_id == node_id)
215 {
216 got_token = true;
217 last_token_id_owned = pkt_token_id;
218 token_rx_time = NOW;
220 DEBUG(4, "Received the token")
221 txData();
222 }
223 else if (pkt_node_id < node_id)
224 { // pkt from previous node in the ring
225 bus_idle_timer.resched((3 * (node_id - pkt_node_id + 1) * bus_idle_timeout));
226 }
227 else // consider ring turnaround
228 bus_idle_timer.resched((3 * (n_nodes - pkt_node_id + node_id + 1) * bus_idle_timeout));
229 }
230 else
231 {
232 DEBUG(0, " received a pkt with invalid token id")
233 }
234
235 if (ch->ptype() == PT_UWTOKENBUS || ch->ptype() == PT_UWRANGING_TOKENBUS)
236 {
237 Packet::free(p);
238 }
239 else if ((dest_mac != addr) && (dest_mac != BCAST_ADDR))
240 {
241 Packet::free(p);
242 incrXDataPktsRx();
243 }
244 else
245 {
246 sendUp(p);
247 incrDataPktsRx();
248 }
249 }
250 }
251 else
252 {
253 if (ch->ptype() == PT_UWRANGING_TOKENBUS)
254 {incrXCtrlPktsRx();}
255 else {incrErrorPktsRx();}
256 DEBUG(0, " discard packet rx while tx; pkt token_id : " << pkt_token_id << " macSA: " << mach->macSA() << " macDA: " << mach->macDA())
257 Packet::free(p); // assume the packet got corrupted in collision with tx packet
258 }
259}
260
262{
263 if (!got_token)
264 {
265 DEBUG(0, " attempt to send token without owning it ")
266 return;
267 }
268 // build the token packet
269 Packet *p = Packet::alloc();
270 hdr_cmn *ch = HDR_CMN(p);
271 hdr_mac *mach = HDR_MAC(p);
272 hdr_tokenbus *tbh = HDR_TOKENBUS(p);
274 ch->ptype() = PT_UWRANGING_TOKENBUS;
275 mach->set(MF_CONTROL, addr, BCAST_ADDR);
276 mach->macDA() = BCAST_ADDR; // hdr_mac->set() doesn't set correctly broadcast address as -1
277 tbh->tokenId() = token_id;
278 id_last_range = token_id;
279 tbrh->token_resend() = false;
280 for (size_t i = 0; i < times_mat[NMOD(node_id)].size(); i++)
281 {
282 if (normId(times_age[NMOD(node_id)][i] + 1*n_nodes) >= token_id) { //send only time measures not older than 1*n_nodes
283 //(tbrh->times()).push_back(half_float::half_cast<half>(times_mat[NMOD(node_id)][i]));
284 (tbrh->times()).push_back((times_mat[NMOD(node_id)][i]));
285 }
286 else {
287 //(tbrh->times()).push_back(half_float::half_cast<half>(-1.)); //mark as invalid if older than 1 round
288 (tbrh->times()).push_back((-1.)); //mark as invalid if older than 1 round
289 }
290 }
291
292 (ch->size()) += tbrh->getSize() + tbh->getSize();
293 tbrh->token_hold() = NOW + Mac2PhyTxDuration(p) - token_rx_time;
294
295 if (last_token_id_heard == token_id) // true when I'm sending the token for the second time
296 {
297 tbh->tokenId() = normId(token_id + n_nodes); //jump n_nodes in tokenId to make distinguishable from the first token sent
298 id_last_range = normId(token_id + n_nodes);
299 tbrh->token_resend() = true;
300 }
301 else if (normId(last_token_id_heard + 1) != token_id) // true when bus_idle_timer has expired and i regen the token
302 {
303 tbrh->token_resend() = false;
304 tbrh->token_hold() = -1.0; // flag an invalid hold time
305 }
306
307 time_last_range = NOW + Mac2PhyTxDuration(p);
308 got_token = false;
309 DEBUG(4, " sending TOKEN to node: " << NMOD(token_id) << " tokenid: " << token_id << " token hold: " << tbrh->token_hold() << " from mac " << mach->macSA() << " to mach " << mach->macDA())
310 DEBUG(10,"pkt size: "<<ch->size() << " tx duration: "<< Mac2PhyTxDuration(p))
311 incrCtrlPktsTx();
313}
314
316{
317 std::vector<double> y; // vector with known terms for linear regression
318 auto x_full = std::vector<std::vector<double>>(dist_num); // outer vec contains the coeff for a single unknown distance
319 auto dist_mask = std::vector<bool>(dist_num, false); // indicates if a distance should be included in the LR (there is at least one equation involving it)
320 size_t i_eq = 0; // index of eq in X_mat matrix
321 size_t i_valid_eq = 0; // index of eq in the final x matrix
322 for (size_t n = 0; n < n_nodes; n++)
323 {
324 for (size_t t = 0; t < n_nodes - 1; t++)
325 {
326 if ((normId(times_age[NMOD(n)][NMOD(t)] + 2*n_nodes) >= id_last_range) && (times_mat[NMOD(n)][NMOD(t)] >= -epsilon))
327 {
328 y.push_back(max(times_mat[NMOD(n)][NMOD(t)], 0.0)); // populate the y vector
329 for (size_t coeff = 0; coeff < dist_num; coeff++) // coeff is the index of the distance in X_mat
330 {
331 (x_full[coeff]).push_back(x_mat[i_eq][coeff]);
332 if (x_mat[i_eq][coeff])
333 { // if coeff is not zero, the relative unknown will be included in LR
334 dist_mask[coeff] = true;
335 }
336 }
337 ++i_valid_eq;
338 }
339 ++i_eq;
340 }
341 }
342
343 // count the number of distances involved in the equations
344 size_t count_valid_dist = 0;
345 for (size_t i = 0; i < dist_num; i++)
346 {
347 if (dist_mask[i])
348 {
349 ++count_valid_dist;
350 }
351 }
352
353 std::vector<std::vector<double>> a; // a is the flattened version of the "A" coefficient matrix (x_full) for the least squares solver
354 for (size_t i = 0; i < dist_num; i++) // copy all the meaningful columns (which have at least one non zero coeff)
355 {
356 if (dist_mask[i])
357 {
358 a.push_back(x_full[i]);
359 }
360 }
361
362 if (count_valid_dist <= y.size())
363 {
364 auto solution = std::vector<double>(count_valid_dist, -1.0); // allocate solution vector
365 LSSQ::LeastSqResult nnls_status = LSSQ::nnLeastSquares(a, y, solution);
366 if (nnls_status == LSSQ::LeastSqResult::OK) // if I have a valid solution
367 {
368 // distances = std::vector<double> (dist_num,-1.0); //uncomment to forget previous distance values
369
370 for (size_t i = 0, j = 0; i < dist_num; i++)
371 {
372 if (dist_mask[i] && solution[j] >= 0.0 && solution[j] <= max_tt)
373 {
374 distances[i] = solution[j++];
375 }
376 }
377 }
378 else
379 {
380 if (nnls_status == LSSQ::LeastSqResult::TIMEOUT)
381 {
382 DEBUG(0, " nnleast_squares() TIMEOUT! keeping old distances vector")
383 }
384 else
385 {
386 DEBUG(0, " nnleast_squares() ERROR! keeping old distances vector")
387 }
388 }
389 }
390}
391
392int UwRangingTokenBus::command(int argc, const char *const *argv)
393{
394 Tcl &tcl = Tcl::instance();
395 if (argc == 2)
396 {
397 if (strcasecmp(argv[1], "calc_distances") == 0)
398 {
399 computeDist();
400 return TCL_OK;
401 }
402 }
403 if (argc == 4)
404 {
405 if (strcasecmp(argv[1], "get_distance") == 0)
406 {
407 int n1 = atoi(argv[2]);
408 int n2 = atoi(argv[3]);
409 if (n1 >= 0 && n1 < n_nodes && n2 >= 0 && n2 < n_nodes)
410 {
411 if (n1==n2) {
412 tcl.resultf("%.17f",0.);
413 }
414 else {
415 tcl.resultf("%.17f", distances[dist_map[NMOD(n1)][NMOD(n2)]]);
416 }
417 return TCL_OK;
418 }
419 return TCL_ERROR;
420 }
421 }
422 return UwTokenBus::command(argc, argv);
423}
424
425bool UwRangingTokenBus::validToken(Packet *p) const
426{
427 if (HDR_UWRANGING_TOKENBUS(p)->token_resend()) // if the token have been resent, make sure I haven't already received the original
428 {
429 int token_id = HDR_TOKENBUS(p)->tokenId();
430 return (token_id > normId(last_token_id_owned + n_nodes));
431 }
432 return UwTokenBus::validToken(p);
433}
Class that represent the binding of the protocol with tcl.
TclObject * create(int argc, const char *const *argv)
Creates the TCL object needed for the tcl language interpretation.
UwRangingTokenBusModuleClass()
Constructor of the UwRangingTokenBusModule class.
Class that represents a TokenBus Node.
const int dist_num
num of distances: will be initialized to n_nodes*(n_nodes-1)/2
std::vector< std::vector< double > > times_mat
vector of shape [n_nodes][n_nodes-1] holds the travel times: the first index is the node_id which has...
int id_last_range
node id from which I received the last ranging pkt
virtual void computeDist()
compute the linear regression and updates the distances vector
double max_tt
max travel time between nodes in seconds, used to discard bad nnleast_squares() results
virtual bool validToken(Packet *p) const override
Assert if the received token id is valid, i.e it follows the monotonic progression taking in account ...
virtual ~UwRangingTokenBus()
Destructor of the TokenBus class.
std::vector< double > distances
vector of shape [D], contains the one way travel times between nodes to be transformed to distances b...
std::vector< std::vector< int > > dist_map
of size [n_nodes][n_nodes] maps(nodeX,nodeY) -> distance
double time_last_range
time of last ping reception (or transmission)
std::vector< std::vector< int > > times_age
vector of shape [n_nodes][n_nodes-1] holds the age of a time (slot number in which the time was calcu...
virtual void sendToken(int next_id) override
Passes the token to the next node.
std::vector< std::vector< double > > x_mat
of size [2D][D] it's the sparse matrix with the equations coefficients (-1,0,1)
virtual int command(int argc, const char *const *argv)
TCL command interpreter.
virtual void Phy2MacEndRx(Packet *p) override
Method called when the Phy Layer finish to receive a Packet.
UwRangingTokenBus()
Default constructor of the TokenBus class.
double epsilon
difference between virtually equal distances can result in small negative numbers due to floating poi...
Class that represents a TokenBus Node.
Definition uwtokenbus.h:52
UWTokenBus_STATUS rtx_status
Definition uwtokenbus.h:239
double min_token_hold_time
if the node has en empty queue when it receive the token, it waits this time before passing the token
Definition uwtokenbus.h:235
int debug
Debug variable: 0 for no info.
Definition uwtokenbus.h:256
TimerBusIdle bus_idle_timer
token_pass_timer is scheduled when a node pass the token, it's cancelled when activity from the follo...
Definition uwtokenbus.h:247
int n_nodes
number of nodes in the ring
Definition uwtokenbus.h:230
virtual int normId(int id) const
virtual void Mac2PhyStartTx(Packet *p)
Method called when the Mac Layer start to transmit a Packet.
virtual int command(int argc, const char *const *argv) override
TCL command interpreter.
constexpr int NMOD(int n)
given any int returns the corresponding node id via modulo operations
Definition uwtokenbus.h:265
double bus_idle_timeout
base timeout for the namesake timer should be (slot_time+max_token_hold_time)
Definition uwtokenbus.h:243
double token_rx_time
time of token reception
Definition uwtokenbus.h:236
TimerTokenPass token_pass_timer
Definition uwtokenbus.h:244
int last_token_id_heard
last token id heard on the bus
Definition uwtokenbus.h:231
bool got_token
set if node is currently holding the token
Definition uwtokenbus.h:240
int last_token_id_owned
last token id owned
Definition uwtokenbus.h:232
int node_id
id of the node (0 to n_nodes-1)
Definition uwtokenbus.h:229
virtual void txData()
Starts transmitting the packets from the queue.
virtual bool validToken(Packet *p) const
Assert if the received token id is valid, i.e it follows the monotonic progression taking in account ...
LeastSqResult nnLeastSquares(std::vector< std::vector< double > > a, std::vector< double > b, std::vector< double > &x, double *resid=nullptr)
Least Squares Linear Regressor solves the least squares problem A * X = B, X>=0.
Header of the token bus protocol.
size_t getSize() const
Returns the size of this header.
tokenid_t & tokenId()
Returns a reference to the token_id variable.
Header of the token bus protocol.
std::vector< uwrange_time_t > & times()
Returns a reference to the travel times array.
size_t getSize() const
Returns the size of this header.
bool & token_resend()
Returns a reference to token_resend_.
uwrange_time_t & token_hold()
Returns a reference to token_hold_ value.
UwRangingTokenBusModuleClass class_module_uwranging_tokenbus
Provides the definition of the class UwRangingTokenBus.
Common structures and variables in the protocol.
#define HDR_UWRANGING_TOKENBUS(p)
alias defined to access the TOKEN BUS HEADER
#define DEBUG(level, text)
#define HDR_TOKENBUS(p)
alias defined to access the TOKEN BUS HEADER