class RemoteDeviceUnderTest(DeviceUnderTest): def add_options(self, parser): add_communication_options(parser) parser.add_option("-n", "--node", dest="node", metavar="ADDR", type="int", help="Connect to node with ZigBit address ADDR") def setup(self, options): if not options.verbosity: options.verbosity = "warning" coor = get_coordinator(options) coor.post("prog/firstcall", "1") self.node = ALHProxy(coor, options.node) self.node.post("prog/firstcall", "1") self.spectrumsensor = SpectrumSensor(self.node) self.config_list = self.spectrumsensor.get_config_list() if not self.config_list.configs: raise Exception("Device returned no configurations. " "It is still scanning or not responding.") self.config = self.config_list.get_config(self.device_id, self.config_id) def get_fw_version(self): return self.node.get("hello").strip() def get_status(self): resp = self.node.get("sensing/deviceStatus").strip() return [v.strip() for v in resp.split("\n")] def measure_ch_impl(self, ch, n): sweep_config = SweepConfig(self.config, ch, ch + 1, 1) duration = int(math.ceil(self.config.time * n * 1e-3 + 1.0)) now = time.time() sensor_program = SpectrumSensorProgram(sweep_config, now + 1, duration, 2) self.spectrumsensor.program(sensor_program) while not self.spectrumsensor.is_complete(sensor_program): time.sleep(1) result = self.spectrumsensor.retrieve(sensor_program) measurements = [sweep.data[0] for sweep in result.sweeps] measurements = measurements[:n] assert len(measurements) == n return measurements
class RemoteDeviceUnderTest(DeviceUnderTest): def add_options(self, parser): add_communication_options(parser) parser.add_option( "-n", "--node", dest="node", metavar="ADDR", type="int", help="Connect to node with ZigBit address ADDR" ) def setup(self, options): if not options.verbosity: options.verbosity = "warning" coor = get_coordinator(options) coor.post("prog/firstcall", "1") self.node = ALHProxy(coor, options.node) self.node.post("prog/firstcall", "1") self.spectrumsensor = SpectrumSensor(self.node) self.config_list = self.spectrumsensor.get_config_list() if not self.config_list.configs: raise Exception("Device returned no configurations. " "It is still scanning or not responding.") self.config = self.config_list.get_config(self.device_id, self.config_id) def get_fw_version(self): return self.node.get("hello").strip() def get_status(self): resp = self.node.get("sensing/deviceStatus").strip() return [v.strip() for v in resp.split("\n")] def measure_ch_impl(self, ch, n): sweep_config = SweepConfig(self.config, ch, ch + 1, 1) duration = int(math.ceil(self.config.time * n * 1e-3 + 1.0)) now = time.time() sensor_program = SpectrumSensorProgram(sweep_config, now + 1, duration, 2) self.spectrumsensor.program(sensor_program) while not self.spectrumsensor.is_complete(sensor_program): time.sleep(1) result = self.spectrumsensor.retrieve(sensor_program) measurements = [sweep.data[0] for sweep in result.sweeps] measurements = measurements[:n] assert len(measurements) == n return measurements
def test_retrieve(self): class MockALH(ALHProtocol): def _get(self, resource, *args): if b"Info" in resource: return b"status=COMPLETE,size=14" else: return b"\x00\x00\x00\x00\x00\x00\x01\x00\x02\x00\x91m\x00i" alh = MockALH() ss = SpectrumSensor(alh) sc = self._get_sc() p = SpectrumSensorProgram(sc, 0, 10, 1) r = ss.retrieve(p) self.assertEqual(len(r.sweeps), 1) self.assertEqual(r.sweeps[0].data, [0., .01, .02]) self.assertEqual(r.sweeps[0].timestamp, 0)
def senseStart(self, time_start, time_duration , slot_id): #start spectrum sensing on node with the given configuration (self.sweep_config it is what matter) #may perform a few frequency sweeps, depends on the time duration #this can generate exception if sweep_config is None if self.sweep_config is None: print "Cannot start sensing, configuration is missing" return None #get a program object. program = SpectrumSensorProgram(self.sweep_config, time_start, time_duration , slot_id ) #get sensor object, with this we'll program the node sensor = SpectrumSensor(self.node) #program the sensor sensor.program(program) #waiting for the sensor to finish the job while not sensor.is_complete(program): print "waiting..." time.sleep(1) if time.time() > (program.time_start + program.time_duration + 60): raise Exception("Something went wrong with the sensing") print "Experiment is finished. retrieving data." result = sensor.retrieve(program) #try to make a folder try: os.mkdir("./data") except OSError: pass try: os.mkdir("./data/coor_%d" %self.coordinator_id) except OSError: pass #write results in a file result.write("./data/coor_%d/node_%s.dat" %(self.coordinator_id ,self.node_id))
class NodePair(object): # Object constructor takes two nodes: Node doing the signal # transmission and node doing power measurements. def __init__(self, txnode, rxnode): # We set up the SignalGenerator and SpectrumSensor objects here # for later use. We also query both nodes for their # corresponding lists of available hardware configurations. # # Since hardware will not change during the experiment, we only # do the query once in object constructor. This makes repeated # measurements faster. self.generator = SignalGenerator(txnode) self.generator_cl = self.generator.get_config_list() self.sensor = SpectrumSensor(rxnode) self.sensor_cl = self.sensor.get_config_list() # This method performs the power measurement. f_hz parameter is the # central frequency in hertz on which the channel measurement will be done. # ptx_dbm is the transmission power in dbm. If ptx_dbm is None, then # the transmitting node will remain silent. def measure(self, f_hz, ptx_dbm): now = time.time() if ptx_dbm is not None: # If transmission was requested, setup the transmitter. tx_config = self.generator_cl.get_tx_config(f_hz, ptx_dbm) if tx_config is None: raise Exception("Node can not scan specified frequency range.") generator_p = SignalGeneratorProgram(tx_config, now + 1, 14) self.generator.program(generator_p) # Setup the receiver for power measurement at the same # frequency as the transmitter. We will only sense a single # frequency, hence both start and stop parameters of the sweep # are set to f_hz. sweep_config = self.sensor_cl.get_sweep_config(f_hz, f_hz, 400e3) if sweep_config is None: raise Exception("Node can not scan specified frequency range.") sensor_p = SpectrumSensorProgram(sweep_config, now + 3, 10, 1) self.sensor.program(sensor_p) # Note that the transmit interval is longer than the receive # interval: # # now + 1 + 3 / + 13 + 15 # | tx start rx start \ rx stop tx stop # | | | / | | # | | |===========\===========| | # | | receive / | | # | | \ | # | |======================/======================| # transmit # # This is to make sure that signal transmission is happening # during the whole time the receiver is measuring signal power. # Start times may differ +/- 1 second due to unsynchronized # clocks and management network latency. while not self.sensor.is_complete(sensor_p): print "waiting..." time.sleep(2) # Retrieve the data and return a single vector with power # measurements (in dBm) from the sensing node. result = self.sensor.retrieve(sensor_p) return np.array(result.get_data())[:, 0] # This method calculates the channel gain between the nodes. def get_channel_gain(self, f_hz, ptx_dbm): def db_to_mw(db): return 10.**(db / 10.) def mw_to_db(mw): return 10. * np.log10(mw) # First, measure just the noise level on the receiving node. # Transmitter is turned off. pnoise_dbm = self.measure(f_hz, None) # Second, measure the received signal power level with the # transmitter turned on. prx_dbm = self.measure(f_hz, ptx_dbm) # Convert all power values to linear scale. ptx_mw = db_to_mw(ptx_dbm) pnoise_mw = db_to_mw(pnoise_dbm) prx_mw = db_to_mw(prx_dbm) # Take the mean of both noise and received signal power # measurements. pnoise_mw_mean = np.mean(pnoise_mw) prx_mw_mean = np.mean(prx_mw) print "p_noise = %.1f dBm (mean=%e mW std=%e mW)" % ( mw_to_db(pnoise_mw_mean), pnoise_mw_mean, np.std(pnoise_mw)) print "p_rx = %.1f dBm (mean=%e mW std=%e mW)" % ( mw_to_db(prx_mw_mean), prx_mw_mean, np.std(prx_mw)) # Use the mean values to estimate the channel gain. h_mean = (prx_mw_mean - pnoise_mw_mean) / ptx_mw # Convert back to logarithmic scale. h_mean_db = mw_to_db(h_mean) print "h = %.1f dB" % (h_mean_db, ) return h_mean_db
def main(): # Turn on logging so that we can see ALH requests happening in the # background. logging.basicConfig(level=logging.INFO) coor = alh.ALHWeb("https://crn.log-a-tec.eu/communicator", 10001) # Nodes 16 and 17 are equipped with an 2.4 GHz tranceiver (CC2500 on # SNE-ISMTV-24) that is capable of transmitting and receiving on the # 2.4 GHz ISM band. node16 = alh.ALHProxy(coor, 16) node17 = alh.ALHProxy(coor, 17) # We will use node 16 as a signal generator. We wrap its ALHProxy # object with a SignalGenerator object for convenience. generator = SignalGenerator(node16) # Set up a transmission configuration for 2.425 GHz and 0 dBm generator_config_list = generator.get_config_list() tx_config = generator_config_list.get_tx_config(2425e6, 0) if tx_config is None: raise Exception("Node can not scan specified frequency range.") # We will use node 17 as a spectrum sensor. Again, we wrap it with a # SpectrumSensor object for convenience. sensor = SpectrumSensor(node17) # We set up a frequency sweep configuration covering 2.40 GHz to 2.45 # GHz band with 400 kHz steps. sensor_config_list = sensor.get_config_list() sweep_config = sensor_config_list.get_sweep_config(2400e6, 2450e6, 400e3) if sweep_config is None: raise Exception("Node can not scan specified frequency range.") # Take note of current time. now = time.time() # SignalGeneratorProgram and SpectrumSensorProgram objects allow us to # program signal generation and spectrum sensing tasks in advance. # # In this case, we setup a signal generation task using the # configuration we prepared above starting 10 seconds from now and # lasting for 20 seconds. # # Similarly for spectrum sensing, we setup a task using frequency sweep # we prepared above starting 5 seconds from now and lasting for 30 # seconds. Results of the measurement will be stored in slot 4. generator_program = SignalGeneratorProgram(tx_config, now + 10, 20) sensor_program = SpectrumSensorProgram(sweep_config, now + 5, 30, 4) # Now actually send instructions over the management network to nodes # in the testbed. generator.program(generator_program) sensor.program(sensor_program) # Query the spectrum sensing node and wait until the task has been # completed. while not sensor.is_complete(sensor_program): print "waiting..." time.sleep(2) # Retrieve spectrum sensing results. This might take a while since the # management mesh network is slow. result = sensor.retrieve(sensor_program) # Write results into a CSV file. result.write("06-programmed-tasks.dat")
def main(): # Turn on logging so that we can see requests and responses in the # terminal. This is useful to keep track of what is going on during the # experiment. logging.basicConfig(level=logging.INFO) # We must first create an object representing the coordinator node. # Each cluster in the LOG-a-TEC has its own coordinator and all # communication with the nodes must go through it. # # The parameters used here correspond to the LOG-a-TEC City Center # out-door cluster (see LOG-a-TEC documentation for other valid # options). # # Note that you must have a valid user name and password saved in an # "alhrc" file in order for this to work. See vesna-alh-tools README # for instructions on how to do that. # # Also make sure you reserved the cluster in the calendar before # running the experiment! coor = alh.ALHWeb("https://crn.log-a-tec.eu/communicator", 10002) # We will be using node 45 in this example. First we create a generic # node object for it. node = alh.ALHProxy(coor, 45) # We will use our node as a spectrum sensor. Here, we wrap the generic # node object with the SpectrumSensor class that provides a convenient # interface to the spectrum sensing capabilities of the node's radio # hardware. sensor = SpectrumSensor(node) # Request the list of spectrum sensing configurations that the node # supports. # # Node 45 is equipped with a SNE-ISMTV-2400 radio board that contains a # CC2500 reconfigurable transceiver. This tranceiver supports a number # of configurations that cover the 2.4 GHz ISM band. sensor_config_list = sensor.get_config_list() # Let's print the list out in a nice format for future reference. print print "Spectrum sensing devices and configurations available" print "=====================================================" print sensor_config_list print # For a demonstration, we'll choose to sense the whole ISM band between # 2.4 GHz and 2.5 GHz with 400 kHz steps. We create a SweepConfig object # that describes this frequency range. sweep_config = sensor_config_list.get_sweep_config(2400e6, 2500e6, 400e3) if sweep_config is None: raise Exception("Node can not scan specified frequency range.") # Take note of current time. now = time.time() # SpectrumSensorProgram object allows us to program a node to perform a # spectrum sensing task in advance. We'll setup a task for sensing the # frequency range we selected above, starting 5 seconds from now and # lasting for 30 seconds. # # Each node has local slots for temporary storage of spectrum sensing # results. Since we are only performing one scan of the spectrum, it # doesn't matter which slot we use. Let's pick slot 1. sensor_program = SpectrumSensorProgram(sweep_config, now + 5, 30, 1) # Now we actually send the programming instructions over the testbed's # management network to the node. sensor.program(sensor_program) # Wait until the programmed task has been completed. while not sensor.is_complete(sensor_program): print "patience..." time.sleep(2) # The task is complete. We must now retrieve spectrum sensing results # back from the node to our computer. This might take a while since the # management mesh network is slow. result = sensor.retrieve(sensor_program) # Finally, we write results into a CSV file for further analysis. # # Alternatively, we could do something else with the results directly # from this program. The SpectrumSensorResult object provides some # convenient methods for accessing the result of the scan. result.write("logatec.csv")
class GameNode: # transmitting and receiving configurations sweepConfig = None txConfig = None # reference to sensor and generator objects sensorNode = None generatorNode = None def __init__(self, coordinatorId, nodeId, showLog=True): """node constructor Keywords arguments: coordinatorId -- Numerical cluster id. nodeId -- id number of node. """ # get coordinator object self.coordinator = alh.ALHWeb("https://crn.log-a-tec.eu/communicator", coordinatorId) # get node object self.node = alh.ALHProxy(self.coordinator, nodeId) # save Ids self.coordinatorId = coordinatorId self.nodeId = nodeId if showLog: logging.basicConfig(level=logging.INFO) def nodeTest(self): """Print a string if node is active""" print self.node.get("sensor/mcuTemp") def coordinatorTest(self): """Print a string if coordinator is active""" print self.coordinator.get("hello") def getNodeID(self): return self.nodeId def getSenseConfig(self): """just get a configuration list so I can see it""" SpectrumSensor(self).get_config_list() def getGeneratorConfig(self): """just get a configuration list so I can see it""" SignalGenerator(self).get_config_list() def setSenseConfiguration(self, startFreqHz, endFreqHz, stepFerqHz): """Set sensing configuration. Measure the power in frequency band [startFrewHz, endFreqHz] with a predefined step. startFreqHz -- Lower bound of the frequency band to check (inclusive) endFreqHz -- Upper bound of the frequency band to check (inclusive) stepFerqHz -- Frequency step """ self.sensorNode = SpectrumSensor(self.node) sensorConfigList = self.sensorNode.get_config_list() self.sweepConfig = sensorConfigList.get_sweep_config(startFreqHz, endFreqHz, stepFerqHz) if self.sweepConfig is None: raise Exception("Something went wrong with the sweepConfig. sweepConfig is None") def setSenseConfigurationChannel(self, startCh, endCh, stepCh): """Set sensing configuration. Measure the power from start_ch to stop_ch with predefined step startCh -- Lowest frequency channel to sweep endCh -- One past the highest frequency channel to sweep stepCh -- How many channels in a step """ self.sensorNode = SpectrumSensor(self.node) sensorConfigList = self.sensorNode.get_config_list() self.sweepConfig = SweepConfig(sensorConfigList.get_config(0, 0), startCh, endCh, stepCh) if self.sweepConfig is None: raise Exception("Something went wrong with the sweepConfig. sweepConfig is None") def setSenseConfigurationFuulSweep(self): """Set sensing configuration. This method will sense the entire band that device supports. """ self.sensorNode = SpectrumSensor(self.node) sensorConfigList = self.sensorNode.get_config_list() # self.sweepConfig = SpectrumSensor(self.node).get_config_list().get_config(0,0).get_full_sweep_config() self.sweepConfig = sensorConfigList.get_config(0, 0).get_full_sweep_config() if self.sweepConfig is None: raise Exception("Something went wrong with the sweepConfig. sweepConfig is None") def senseStart(self, timeStart, timeDuration, slotID): """Start spectrum sensing on node with predefined configuration (self.sweep_config). May perform a few frequency sweeps, depends on the time duration. If sweepConfig is None raise exception. timeStart -- Time to start the task (UNIX timestamp). timeDuration -- Duration of the task in seconds. slotID -- Numerical slot id used for storing measurements. """ if self.sweepConfig is None: print "Cannot start Sensing. Configuration is missing!!" return None # get program object program = SpectrumSensorProgram(self.sweepConfig, timeStart, timeDuration, slotID) # get sensor object and program the node self.sensorNode.program(program) # wait for sensor to finish the job while not self.sensorNode.is_complete(program): print "waiting..." time.sleep(1) if time.time() > (program.time_start + program.time_duration + 60): raise Exception("Something went wrong when sensing") print "Sensing finished, retrieving data." result = self.sensorNode.retrieve(program) # for simplicity save data in a folder try: os.mkdir("./data") except OSError: pass try: os.mkdir("./data/coor_%d" % (self.coordinatorId)) except OSError: pass # write data, overwrite existing file result.write("./data/coor_%d/node_%d.dat" % (self.coordinatorId, self.nodeId)) def senseStartQuick(self): """returns a list : [[frequencies] , [power_dbm]] Starts spectrum sensing on node with predefined configuration (self.sweepConfig). This method will do a quick step. Use this if time is critical! Bandwidth you can measure in this case is limited. This will perform a single frequency step with the sweep method defined in the SpectrumSensor class. If sweepConfig is None raise exception. """ if self.sweepConfig is None: print "Cannot start Sensing. Configuration is missing!!" return None sweep = SpectrumSensor(self.node).sweep(self.sweepConfig) # get frequency list fHz = self.sweepConfig.get_hz_list() dataReceived = [] dataReceived.append(fHz) dataReceived.append(sweep.data) return dataReceived def setGeneratorConfiguration(self, fHz, powerDBm): """Set configuration for signal generation. fHz -- Frequency to generate. powerDBm -- Transmission power. """ self.generatorNode = SignalGenerator(self.node) generatorConfigList = self.generatorNode.get_config_list() self.txConfig = generatorConfigList.get_tx_config(fHz, powerDBm) if self.txConfig is None: raise Exception("Something went wrong with configuration, txConfig is None.") def setGeneratorConfigurationChannel(self, fCh, powerDBm): """Set configuration for signal generation. fHz -- Frequency to generate. powerDBm -- Transmission power. """ self.generatorNode = SignalGenerator(self.node) generatorConfigList = self.generatorNode.get_config_list() self.txConfig = TxConfig(generatorConfigList.get_config(0, 0), fCh, powerDBm) if self.txConfig is None: raise Exception("Something went wrong with configuration, txConfig is None.") def generatorStart(self, timeStart, timeDuration): """Start signal generation with predefined configuration (txConfig). If txConfig is None raise exception. timeStart -- Time when node starts transmitting. timeDuration -- Time for which node is transmitting. """ if self.txConfig is None: print "Cannot start signal generating, configuration is missing" return None # get a program object program = SignalGeneratorProgram(self.txConfig, timeStart, timeDuration) self.generatorNode.program(program)