Source code for simulator.radios.DtnVariableRadio

"""
# ==================================================================================
# Author: Marc Sanchez Net
# Date:   03/24/2019
# Copyright (c) 2019, Jet Propulsion Laboratory.
# ==================================================================================
"""

import numpy as np
import pandas as pd

from simulator.core.DtnSemaphore import DtnSemaphore
from simulator.radios.DtnBasicRadio import DtnBasicRadio

[docs]class DtnVariableRadio(DtnBasicRadio): _data = {} def reset(self): # Reset static variable self.__class__._data = {}
[docs] def initialize(self, datarate_file=None, **kwargs): # Define the data rate as NaN kwargs['rate'] = np.nan # Initialize data rate profile file = self.config['globals'].indir/datarate_file # If this data is already loaded, use it try: df = self._data[self.parent.nid] except KeyError: df = self.load_data_rate(file) # Depack and transform all to numpy arrays self.dr = df.copy(deep=True).to_dict(orient='list') for k in self.dr.keys(): self.dr[k] = np.array(self.dr[k]) self.dr['t'] = df.index.values # Call parent initializer super(DtnVariableRadio, self).initialize(**kwargs) # Create a semaphore to signal when the radio has >0 data rate self.active = {n: DtnSemaphore(self.env, green=False) for n in self.dr if n != 't'} # Start radio data rate monitor for dest in self.active: self.env.process(self.datarate_monitor(dest))
def load_data_rate(self, file): # Load depending on file type if file.suffix == '.xlsx': df = pd.read_excel(file, header=[0, 1], index_col=0) elif file.suffix == '.h5': df = pd.read_hdf(file, key='data_rate') else: raise IOError('Only .xlsx and .h5 files can be loaded') # Get the timelines for this node df = df.xs(self.parent.nid, axis=1, level=0) # Store data self._data[self.parent.nid] = df return df def datarate_monitor(self, dest): # If no data for this destination, exit if dest not in self.dr: yield self.env.exit() # Initialize variables sm = self.active[dest] # Iterate over data rate profile for t, dr in zip(self.dr['t'], self.dr[dest]): # Wait until its time to update yield self.env.timeout(max(0, t-self.t)) # If no data rate and already red, continue if dr == 0 and sm.is_red: continue # If data rate == 0 and green, turn red if dr == 0 and sm.is_green: sm.turn_red() continue # If you reach this point, turn green if red if sm.is_red: sm.turn_green() def run(self): while self.is_alive: # Get the next segment to transmit item = yield from self.in_queue.get() # Depack item neighbor, message, peer, direction = item # Wait until this connection is active if self.active[neighbor].is_red: yield self.active[neighbor].green # Get the connection to send this message through conn = self.outcons[neighbor] # Get the data rate for this radio tx_time = self.get_tx_time(neighbor, message) # Apply delay for radio to transmit entire segment yield self.env.timeout(tx_time) # Count the energy consumed self.energy += message.num_bits * self.J_bit # Transmit the message through the connection. self.send_through_connection(message, conn, peer, direction) def get_tx_time(self, dest, message): # Initialize variables t, dr = self.dr['t'], self.dr[dest] # Find the first instant that exceeds current time idx1 = (t > self.t).argmax() # Figure out how much data you could send between now and this first instant dv = (t[idx1] - self.t) * dr[idx1 - 1] # If that is enough data rate, you are done if dv >= message.data_vol: return message.data_vol/dr[idx1-1] # Clip time series to only the relevant parts elapsed = t[idx1]-self.t t, dr = t[idx1:]-t[idx1], dr[idx1:-1] data_vol = np.ceil(message.data_vol - dv) # Otherwise, check how long it will take. First, compute the amount of data # sent over the connection cumulatively dv = np.cumsum(np.diff(t) * dr) # Find the instant in time where dv exceeds the message dv idx2 = (dv >= data_vol) # If it never sends this data volume, return infinite if not idx2.any(): print(self.parent.nid, 'radio is stuck forever') return float('inf') # Get the first index (i.e. the first instant in time) where you will have sent # the message idx2 = idx2.argmax() # Compute the amount of extra data that will have sent by the end of idx2+1. # You need to subtract it. extra_dv = dv[idx2]-data_vol # Compute the amount of time to send this ddv bits Ttx = elapsed + t[idx2+1] - extra_dv/dr[idx2] return Ttx