DESERT 3.5.1
Loading...
Searching...
No Matches
uwmodamodem.cpp
Go to the documentation of this file.
1//
2// Copyright (c) 2019 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#include "phymac-clmsg.h"
30
31#include <uwmodamodem.h>
32#include <uwsocket.h>
33
34#include <string>
35#include <functional>
36#include <algorithm>
37
38#include <iostream>
39
40const int UwMODAModem::SIGNALING_ADDRESS = 55006;
41
42const int UwMODAModem::DATA_ADDRESS = 55555;
43
44const std::string sep = {"::"};
45
46const std::string end_delim = {";"};
47
48const std::chrono::milliseconds UwMODAModem::MODEM_TIMEOUT =
49 std::chrono::milliseconds(3000);
50
51std::vector<std::pair<std::string, UwMODAModem::ModemResponse> >
53 std::make_pair("RX_STARTED", ModemResponse::RX_BEG),
54 std::make_pair("TX_ENDED", ModemResponse::TX_END),
55 std::make_pair("CFG_ENDED", ModemResponse::CFG_END)
56};
57
58std::map<UwMODAModem::ModemState, std::string>
60 std::make_pair(ModemState::AVAILABLE, "AVAILABLE"),
61 std::make_pair(ModemState::TRANSMITTING, "TRANSMITTING"),
62 std::make_pair(ModemState::RECEIVING, "RECEIVING"),
63 std::make_pair(ModemState::CONFIGURING, "CONFIGURING")
64};
65
70static class UwMODAModem_TclClass : public TclClass
71{
72
73public:
75 : TclClass("Module/UW/UwModem/MODA")
76 {
77 }
78
79 TclObject *
80 create(int args, const char *const *argv)
81 {
82 return (new UwMODAModem());
83 }
84
86
88 : UwModem()
89 , status(ModemState::AVAILABLE)
90 , status_m()
91 , tx_queue_m()
92 , tx_queue_cv()
93 , receiving(false)
94 , transmitting(false)
95 , rx_payload("")
96 , sig_thread()
97 , rx_thread()
98 , tx_thread()
99 , signal_conn(new UwSocket())
100 , data_conn(new UwSocket())
101 , signal_buffer()
102 , signal_tag("DRIVER")
103 , signal_address("")
104 , premodulation(0)
105{
106 bind("premodulation", (int *) &premodulation);
107
108 signal_conn->setTCP();
109 data_conn->setTCP();
110 data_buffer.clear();
111}
112
117
118void
120{
121 hdr_cmn *ch = HDR_CMN(p);
122 hdr_MPhy *ph = HDR_MPHY(p);
123
124 if (ch->direction() == hdr_cmn::UP) {
125 if (isOn) {
126 startRx(p);
127 endRx(p);
128 } else {
129 Packet::free(p);
130 }
131 } else { // hdr_cmn::DOWN
132 if (!isOn) {
133 return;
134 }
135 ph->Pr = 0;
136 ph->Pn = 0;
137 ph->Pi = 0;
138 ph->txtime = NOW;
139 ph->rxtime = ph->txtime;
140
141 ph->worth_tracing = false;
142
143 ph->srcSpectralMask = getTxSpectralMask(p);
144 ph->srcAntenna = getTxAntenna(p);
145 ph->srcPosition = getPosition();
146 ph->dstSpectralMask = 0;
147 ph->dstPosition = 0;
148 ph->dstAntenna = 0;
149 ph->modulationType = getModulationType(p);
150 ph->duration = getTxDuration(p);
151
152 std::unique_lock<std::mutex> tx_lock(tx_queue_m);
153 tx_queue.push(p);
154 tx_lock.unlock();
155 printOnLog(LogLevel::DEBUG, "MODAMODEM", "recv::PUSHING_IN_TX_QUEUE");
156 tx_queue_cv.notify_one();
157 }
158 return;
159}
160
161int
162UwMODAModem::command(int argc, const char *const *argv)
163{
164 // Tcl &tcl = Tcl::instance();
165
166 if (argc == 3) {
167 if (!strcmp(argv[1], "setSignalAddress")) {
168 signal_address = argv[2];
169 return TCL_OK;
170 }
171 }
172 return UwModem::command(argc, argv);
173}
174
175int
177{
178 return 0;
179}
180
181double
183{
184 hdr_uwal *uwalh = HDR_UWAL(p);
185 double tx_duration = -1;
186
187 // With Flexframe modulation, the TX duration is obtained
188 // dividing the number of samples written by the sampling frequency
189 // (192 kHz by default).
190 // It can be written as a linear function of the packet size:
191 // tx_duration = a + b * pkt_size
192 // The two constants a = 0.560833333 [s] and b = 0.013333333 [s]
193 // are obtained by interpolating the TX duration values obtained
194 // with the smallest and biggest packet size possible.
195 tx_duration = 0.560833333 + 0.013333333 * (uwalh->binPktLength());
196
197 if (premodulation)
198 tx_duration += 0.1;
199
200 return tx_duration;
201}
202
203int
205{
206 if (m->type() == CLMSG_MAC2PHY_GETTXDURATION) {
207 Packet *p = ((ClMsgMac2PhyGetTxDuration *) m)->pkt;
208 hdr_cmn *ch = HDR_CMN(p);
209
210 if (ch->direction() == hdr_cmn::DOWN) {
211 double duration = getTxDuration(p);
212
213 if (duration > 0)
215 "MODAMODEM", "recvSyncClMsg::GET_TXDURATION "
216 + std::to_string(duration));
217
218 ((ClMsgMac2PhyGetTxDuration *) m)->setDuration(duration);
219
220 return 0;
221 }
222
223 // Don't set TX duration on RX.
224 ((ClMsgMac2PhyGetTxDuration *) m)->setDuration(-1);
225
226 return 1;
227 }
228
229 return MPhy::recvSyncClMsg(m);
230}
231
232void
234{
235 printOnLog(LogLevel::INFO, "MODAMODEM",
236 "startRx::CALL_PHY2MACSTARTRX");
237 Phy2MacStartRx(p);
238}
239
240void
242{
243 printOnLog(LogLevel::INFO, "MODAMODEM", "endRx::CALL_SENDUP");
244 sendUp(p, 0.01);
245}
246
247void
249{
250 printOnLog(LogLevel::DEBUG, "MODAMODEM", "STARTING_DRIVER");
251
252 if (!signal_conn->openConnection(signal_address)) {
253 std::string err_msg = "SIGNALING_CHANNEL_FAILED_TO_OPEN_AT_PORT:"
255 printOnLog(LogLevel::ERROR,"MODAMODEM", err_msg);
256 return;
257 }
258
259 if (!data_conn->openConnection(modem_address)) {
260 std::string err_msg = "DATA_CHANNEL_FAILED_TO_OPEN_AT_PORT:"
262 printOnLog(LogLevel::ERROR,"MODAMODEM", err_msg);
263 return;
264 }
265
266 // set flags to true so loops can start
267 receiving.store(true);
268 transmitting.store(true);
269
270 // Dispatch threads
271 sig_thread = std::thread(&UwMODAModem::receivingSignaling, this);
272 rx_thread = std::thread(&UwMODAModem::receivingData, this);
273 tx_thread = std::thread(&UwMODAModem::transmittingData, this);
274
275 checkTimer = new CheckTimer(this);
276 checkTimer->resched(period);
277}
278
279void
281{
282 receiving.store(false);
283 transmitting.store(false);
284
285 status_cv.notify_all();
286 tx_queue_cv.notify_all();
287
288 if (tx_thread.joinable())
289 tx_thread.join();
290
291 if (signal_conn->isConnected() && !signal_conn->closeConnection())
293 "MODAMODEM", "SIGNAL_CONNECTION_UNABLE_TO_CLOSE");
294 if (data_conn->isConnected() && !data_conn->closeConnection())
296 "MODAMODEM","DATA_CONNECTION_UNABLE_TO_CLOSE");
297
298 if (sig_thread.joinable())
299 sig_thread.join();
300 if (rx_thread.joinable())
301 rx_thread.join();
302
303 checkTimer->force_cancel();
304}
305
306void
308{
310 std::fill(data_buffer.begin(), data_buffer.end(), '\0');
311
312 while (receiving.load())
313 {
314 std::unique_lock<std::mutex> state_lock(status_m);
315 if (status_cv.wait_for(state_lock, MODEM_TIMEOUT, [&] {
316 return status == ModemState::RECEIVING;
317 })) {
318
319 state_lock.unlock();
320
321 auto beg_it = data_buffer.begin();
322 int r_bytes = data_conn->readFromDevice(&(*beg_it), MAX_READ_BYTES);
323
324 if (r_bytes > 0) {
325
326 while (r_bytes < rx_size) {
327 r_bytes += data_conn->readFromDevice(&(*beg_it) + r_bytes,
328 MAX_READ_BYTES - r_bytes);
329 }
330
331 rx_payload = std::string(beg_it, beg_it + r_bytes);
332 printOnLog(LogLevel::DEBUG, "MODAMODEM", "receivingData::LEN::" +
333 std::to_string(rx_payload.size()) + "::DATA::" + rx_payload);
334
335 Packet *p = Packet::alloc();
337 std::function<void(UwModem &, Packet * p)> callback = &UwModem::recv;
338 ModemEvent e = {callback, p};
339 event_q.push(e);
340
341 data_buffer.clear();
342
343 }
344
345 state_lock.lock();
347
348 }
349
350 }
351}
352
353void
355{
356 hdr_uwal *uwalh = HDR_UWAL(p);
357 uwalh->binPktLength() = rx_payload.size();
358 std::memset(uwalh->binPkt(), 0, uwalh->binPktLength());
359 std::copy(rx_payload.begin(), rx_payload.end(), uwalh->binPkt());
360 HDR_CMN(p)->direction() = hdr_cmn::UP;
361}
362
363void
365{
367 std::fill(signal_buffer.begin(), signal_buffer.end(), '\0');
368
370
371 auto beg_it = signal_buffer.begin();
372 auto end_it = beg_it;
373
374 while (receiving.load())
375 {
376 int r_bytes = signal_conn->readFromDevice(&(*end_it),
377 MAX_READ_BYTES - (end_it - beg_it));
378 end_it = end_it + r_bytes;
379
380 rsp = parseSignaling(end_it);
381
382 while (receiving.load() && rsp == ModemResponse::UNDEFINED) {
383 r_bytes += signal_conn->readFromDevice(&(*end_it), MAX_READ_BYTES-r_bytes);
384 end_it += r_bytes;
385 rsp = parseSignaling(end_it);
386 }
387
388 updateStatus(rsp);
389
390 // signal_buffer.clear();
391 }
392}
393
394void
396{
397 while (transmitting.load()) {
398
399 std::unique_lock<std::mutex> tx_lock(tx_queue_m);
400
401 tx_queue_cv.wait(
402 tx_lock, [&] { return !tx_queue.empty() || !transmitting; });
403
404 if (!transmitting.load())
405 break;
406
407 Packet *pck = tx_queue.front();
408 tx_queue.pop();
409 tx_lock.unlock();
410 if (pck) {
411 startTx(pck);
412 }
413
415 "MODAMODEM",
416 "transmittingData::BLOCKING_ON_NEXT_PACKET");
417 }
418}
419
421UwMODAModem::parseSignaling(std::vector<char>::iterator& end_it)
422{
423 auto beg_it = signal_buffer.begin();
424 int offset = 0;
426
427 // Identify of the signaling is for driver (me)
428 auto tag_it = std::search(beg_it, end_it, signal_tag.begin(), signal_tag.end());
429
430 if (tag_it != end_it) {
431
432 // Identify which info the signalinf brings
433 for (uint i = 0; i < signaling_dict.size(); i++) {
434 auto comm_it = std::search(beg_it,
435 end_it,
436 signaling_dict[i].first.begin(),
437 signaling_dict[i].first.end());
438 if (comm_it != end_it) {
439
440 rsp = signaling_dict[i].second;
441
442 // if the info is related to reception started, retrieve size of the data
443 if (rsp == ModemResponse::RX_BEG) {
444 auto size_beg = std::search(comm_it, end_it, sep.begin(), sep.end());
445 auto size_end = std::search(comm_it, end_it,
446 end_delim.begin(), end_delim.end());
447 std::string size_s = std::string(size_beg+sep.size(), size_end);
448 rx_size = std::stoi(size_s);
449
450 offset = end_it - (size_end + end_delim.size());
451 std::copy(size_end + end_delim.size(),
452 end_it,
453 signal_buffer.begin());
454 end_it = beg_it + offset;
455 }
456
457 }
458
459 }
460
461 }
462
463 return rsp;
464}
465
466void
468{
469 std::lock_guard<std::mutex> status_lock(status_m);
470
471 switch (response) {
474 status_cv.notify_all();
475 break;
478 status_cv.notify_all();
479 break;
482 status_cv.notify_all();
483 break;
484 default:
485 printOnLog(LogLevel::ERROR, "MODAMODEM", "updateStatus::UNKNOWN_RESPONSE");
486 return;
487 }
488
489 printOnLog(LogLevel::DEBUG, "MODAMODEM", "updateStatus::TO::" +
491}
492
493
494void
496{
497 hdr_uwal *uwalh = HDR_UWAL(p);
498 std::string payload;
499 payload.assign(uwalh->binPkt(), uwalh->binPktLength());
500
501 std::unique_lock<std::mutex> state_lock(status_m);
502 if (status_cv.wait_for(state_lock, MODEM_TIMEOUT, [&] {
503 return status == ModemState::AVAILABLE;
504 })) {
505
507 state_lock.unlock();
508
509 if ((data_conn->writeToDevice(payload)) < 0) {
511 "MODAMODEM",
512 "startTx::FAIL_TO_WRITE_DATA_TO_DEVICE");
513 return;
514 }
515
516 std::function<void(UwModem &, Packet * p)> callback =
518 ModemEvent e = {callback, p};
519 event_q.push(e);
520
521 printOnLog(LogLevel::INFO, "MODAMODEM", "startTx::PACKET_TRANSMITTED");
522
523 }
524}
525
Class to create the Otcl shadow object for an object of the class UwMODAModem.
TclObject * create(int args, const char *const *argv)
std::thread rx_thread
Thread managing the data reception process.
virtual int getModulationType(Packet *p)
Method that returns the modulation type used for the packet being transmitted.
int premodulation
True if premodulation is on, false otherwise.
void createRxPacket(Packet *p)
Method that creates a packet from the received stream of bytes.
const std::string sep
String separator used in reception signaling.
virtual void endRx(Packet *p)
Method that ends a packet reception.
ModemState status
Variable holding the current status of the modem.
void receivingSignaling()
Method that dispacthes a thread dedicated to receiving signaling from the signaling connector Allows ...
ModemResponse parseSignaling(std::vector< char >::iterator &end_it)
Method that parses the content of the signaling data buffer to retrieve signaling messages.
void receivingData()
Method that dispatch a thread dedicated to receiving data from the data connector.
std::mutex status_m
Mutex associated with the state machine of the modem.
static const int SIGNALING_ADDRESS
Port of the signaling channel.
virtual void recv(Packet *p)
Method that handles the reception of packets arriving from upper layers of the network simulator.
std::thread tx_thread
Thread managing the data transmission process.
virtual double getTxDuration(Packet *p)
Method that returns the duration of a given transmitted packet.
void updateStatus(ModemResponse response)
Method that, based on the received signaling, updates the state machine of the driver.
std::string rx_payload
String that is updated with each new received message.
void start()
Method that starts the driver operations.
std::thread sig_thread
Thread managing the signaling reception process.
std::atomic< bool > receiving
Atomic boolean variable that controls the receiving looping thread.
virtual void startTx(Packet *p)
Method that triggers the transmission of a packet through a specified modem.
std::condition_variable status_cv
Condition variable that is linked with the status variable.
std::mutex tx_queue_m
Mutex associated with the transmission queue.
ModemResponse
Enum type representing modem responses.
Definition uwmodamodem.h:71
static const std::chrono::milliseconds MODEM_TIMEOUT
Maximum time to wait for modem to become ModemState::AVAILABLE.
std::condition_variable tx_queue_cv
Condition variable that is linked with the transmitting queue.
static std::vector< std::pair< std::string, ModemResponse > > signaling_dict
Dictionary of accepted signaling states.
Definition uwmodamodem.h:52
void stop()
Method that stops the driver operations.
void transmittingData()
Method that dispatches a thread dedicated to transmitting data through the data connector.
virtual int command(int argc, const char *const *argv)
Tcl command interpreter: Method that maps Tcl commands into C++ methods.
static std::map< ModemState, std::string > stateToString
Dictionary for converting a state to a printable string.
Definition uwmodamodem.h:59
std::string signal_tag
Signaling tag to recognize signaling from the modem.
UwMODAModem()
Constructor of the UwMODAModem class.
const std::string end_delim
String end delimiter used in reception signaling.
std::vector< char > signal_buffer
Bytes buffer for the signaling channel (unparsed data)
ModemState
Enum type for the modem general state.
Definition uwmodamodem.h:62
virtual void startRx(Packet *p)
Method that starts a packet reception.
virtual ~UwMODAModem()
Destructor of the UwMODAModem class.
int rx_size
Size of each new received message, coming from signaling.
std::unique_ptr< UwConnector > signal_conn
Signaling connector: used ot retrieve signaling coming from the modem signaling's channel.
virtual int recvSyncClMsg(ClMessage *m)
Cross-Layer messages synchronous interpreter.
static const int DATA_ADDRESS
Port of the data channel.
std::atomic< bool > transmitting
Atomic boolean variable that controls the transmitting looping thread.
std::string signal_address
std::unique_ptr< UwConnector > data_conn
Data connector: used ot retrieve data coming from the modem data socket.
Class that implements the interface to DESERT, as used through Tcl scripts.
Definition uwmodem.h:62
void realTxEnded(Packet *p)
Method to call endTx from end of real packet transmission.
Definition uwmodem.h:219
friend class CheckTimer
Definition uwmodem.h:63
std::string modem_address
String containing the address needed to connect to the device In case of socket, it may be expressed ...
Definition uwmodem.h:252
virtual void recv(Packet *p)=0
Method that handles the reception of packets arriving from the upper layers of the network simulator.
std::queue< ModemEvent > event_q
Queue of events that are scheduled for NS2 to execute (callbacks)
Definition uwmodem.h:269
int MAX_READ_BYTES
Maximum number of bytes to be read by a single dump of data.
Definition uwmodem.h:245
CheckTimer * checkTimer
Pointer to an object to schedule the "check-modem" events.
Definition uwmodem.h:265
std::queue< Packet * > tx_queue
Modem's transmission queue: holds packets that are to be transmitted.
Definition uwmodem.h:234
void printOnLog(LogLevel log_level, string module, string message)
Function that, given the appropriate level of log, prints to the set log file the provided log messag...
Definition uwmodem.cpp:102
unsigned int DATA_BUFFER_LEN
Size of the buffer that holds data.
Definition uwmodem.h:242
virtual int command(int argc, const char *const *argv)
Tcl command interpreter: Method that maps Tcl commands into C++ methods.
Definition uwmodem.cpp:127
std::vector< char > data_buffer
Char buffer (vector) that holds data read from the modem (unparsed data) Main container for data rece...
Definition uwmodem.h:231
double period
Checking period of the modem's buffer.
Definition uwmodem.h:267
Class that implements a TCP or UDP socket.
Definition uwsocket.h:59
#define HDR_UWAL(p)
Definition hdr-uwal.h:43
hdr_uwal describes the packet header used by Uwal objects.
Definition hdr-uwal.h:52
char * binPkt()
Return to the binPkt_ array pointer.
Definition hdr-uwal.h:141
uint32_t & binPktLength()
Reference to the binPktLength_ variable.
Definition hdr-uwal.h:150
const std::string sep
const std::string end_delim
UwMODAModem_TclClass class_modamodem
Driver for the MODA SDM acoustic UW modem.
Class that implements a connector and, specifically, the socket connector. BSD sockets are used,...