callbox: Add ability to configure tx/rx power
This adds APIs to the CallboxManager to configure uplink/downlink power
independently i.e. without reconfiguring the entire callbox. It also
adds support for setting the callbox downlink power using a float as
opposed to only using the predefined values.
The predefined Rx power levels are updated to use the default ChromeOS
RSRP values defined in cellular_capability_3gpp.cc rather than the
Android values that it was previously using.
BUG=b:247788033
TEST=build_dockerimage $chroot_path $sysroot_path --service cros-callbox --build_type local
Change-Id: I061a6301edeadf752e6bb69aeec558575c0f42b8
Reviewed-on: https://chromium-review.googlesource.com/c/chromiumos/platform/dev-util/+/3924035
Reviewed-by: Nagi Marupaka <nmarupaka@google.com>
Tested-by: Jason Stanko <jstanko@google.com>
Commit-Queue: Jason Stanko <jstanko@google.com>
Reviewed-by: Madhav <madhavadas@google.com>
diff --git a/src/chromiumos/test/callbox/docker/cellular/callbox_utils/cmw500_cellular_simulator.py b/src/chromiumos/test/callbox/docker/cellular/callbox_utils/cmw500_cellular_simulator.py
index c71927e..69fd2ec 100644
--- a/src/chromiumos/test/callbox/docker/cellular/callbox_utils/cmw500_cellular_simulator.py
+++ b/src/chromiumos/test/callbox/docker/cellular/callbox_utils/cmw500_cellular_simulator.py
@@ -2,13 +2,17 @@
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
-import time
-import logging
+# TODO(b/254347891): unify formatting and ignore specific lints in callbox libraries
+# pylint: skip-file
-from .. import cellular_simulator as cc
+import logging
+import time
+
from . import cmw500
+from .. import cellular_simulator as cc
from ..simulation_utils import LteSimulation
+
CMW_TM_MAPPING = {
LteSimulation.TransmissionMode.TM1: cmw500.TransmissionModes.TM1,
LteSimulation.TransmissionMode.TM2: cmw500.TransmissionModes.TM2,
@@ -256,7 +260,8 @@
self.log.warning('Open loop supports-50dBm to 23 dBm. '
'Setting it to max power 23 dBm')
input_power = 23
- bts.uplink_power_control = input_power
+ # open loop power only supports integers
+ bts.uplink_power_control = round(input_power)
bts.tpc_power_control = cmw500.TpcPowerControl.CLOSED_LOOP
bts.tpc_closed_loop_target_power = input_power
@@ -599,4 +604,3 @@
self.cmw.wait_for_attached_state()
self.cmw.set_sms(sms_message)
self.cmw.send_sms()
-
diff --git a/src/chromiumos/test/callbox/docker/cellular/cellular_simulator.py b/src/chromiumos/test/callbox/docker/cellular/cellular_simulator.py
index f6cdc5d..e0aefe1 100644
--- a/src/chromiumos/test/callbox/docker/cellular/cellular_simulator.py
+++ b/src/chromiumos/test/callbox/docker/cellular/cellular_simulator.py
@@ -2,6 +2,9 @@
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
+# TODO(b/254347891): unify formatting and ignore specific lints in callbox libraries
+# pylint: skip-file
+
from . import simulation_utils as sims
@@ -71,10 +74,10 @@
bts_index: the base station number.
"""
- if config.output_power:
+ if config.output_power is not None:
self.set_output_power(bts_index, config.output_power)
- if config.input_power:
+ if config.input_power is not None:
self.set_input_power(bts_index, config.input_power)
if isinstance(config, sims.LteSimulation.LteSimulation.BtsConfig):
diff --git a/src/chromiumos/test/callbox/docker/cellular/proxyserver/flask_app.py b/src/chromiumos/test/callbox/docker/cellular/proxyserver/flask_app.py
index 1bbd042..68f9ce1 100644
--- a/src/chromiumos/test/callbox/docker/cellular/proxyserver/flask_app.py
+++ b/src/chromiumos/test/callbox/docker/cellular/proxyserver/flask_app.py
@@ -6,7 +6,7 @@
# disable some lints to stay consistent with ACTS formatting
# pylint: disable=bad-indentation, banned-string-format-function
-# pylint: disable=docstring-trailing-quotes, docstring-section-indent, bad-continuation
+# pylint: disable=docstring-trailing-quotes, docstring-section-indent
import traceback
import urllib
@@ -60,15 +60,41 @@
}, None)
config.parameter_list = data['parameter_list']
+ config.simulation.parse_parameters(config.parameter_list)
return 'OK'
def begin_simulation(self, data):
self._require_dict_keys(data, 'callbox')
config = self._get_callbox_config(data['callbox'])
- config.simulation.parse_parameters(config.parameter_list)
config.simulation.start()
return 'OK'
+ def set_uplink_tx_power(self, data):
+ self._require_dict_keys(data, 'callbox', LteSimulation.LteSimulation.PARAM_UL_PW)
+ config = self._get_callbox_config(data['callbox'])
+ parameters = [LteSimulation.LteSimulation.PARAM_UL_PW, data[LteSimulation.LteSimulation.PARAM_UL_PW]]
+ power = config.simulation.get_uplink_power_from_parameters(parameters)
+ config.simulation.set_uplink_tx_power(power)
+ return 'OK'
+
+ def set_downlink_rx_power(self, data):
+ self._require_dict_keys(data, 'callbox', LteSimulation.LteSimulation.PARAM_DL_PW)
+ config = self._get_callbox_config(data['callbox'])
+ parameters = [LteSimulation.LteSimulation.PARAM_DL_PW, data[LteSimulation.LteSimulation.PARAM_DL_PW]]
+ power = config.simulation.get_downlink_power_from_parameters(parameters)
+ config.simulation.set_downlink_rx_power(power)
+ return 'OK'
+
+ def query_uplink_tx_power(self, data):
+ self._require_dict_keys(data, 'callbox')
+ config = self._get_callbox_config(data['callbox'])
+ return { LteSimulation.LteSimulation.PARAM_UL_PW : config.simulation.get_uplink_tx_power()}
+
+ def query_downlink_rx_power(self, data):
+ self._require_dict_keys(data, 'callbox')
+ config = self._get_callbox_config(data['callbox'])
+ return { LteSimulation.LteSimulation.PARAM_DL_PW : config.simulation.get_downlink_rx_power()}
+
def query_throughput(self, data):
self._require_dict_keys(data, 'callbox')
config = self._get_callbox_config(data['callbox'])
@@ -144,6 +170,10 @@
path_lookup = {
'config': callbox_manager.configure_callbox,
+ 'config/power/downlink' : callbox_manager.set_downlink_rx_power,
+ 'config/power/uplink' : callbox_manager.set_uplink_tx_power,
+ 'config/fetch/power/downlink' : callbox_manager.query_downlink_rx_power,
+ 'config/fetch/power/uplink' : callbox_manager.query_uplink_tx_power,
'config/fetch/maxthroughput' : callbox_manager.query_throughput,
'start': callbox_manager.begin_simulation,
'sms': callbox_manager.send_sms,
diff --git a/src/chromiumos/test/callbox/docker/cellular/simulation_utils/BaseSimulation.py b/src/chromiumos/test/callbox/docker/cellular/simulation_utils/BaseSimulation.py
index 4fd41f6..0044c7f 100644
--- a/src/chromiumos/test/callbox/docker/cellular/simulation_utils/BaseSimulation.py
+++ b/src/chromiumos/test/callbox/docker/cellular/simulation_utils/BaseSimulation.py
@@ -2,10 +2,15 @@
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
-import numpy as np
-import time
-from .. import cellular_simulator
+# TODO(b/254347891): unify formatting and ignore specific lints in callbox libraries
+# pylint: skip-file
+
from enum import Enum
+import time
+
+import numpy as np
+
+from .. import cellular_simulator
class BaseSimulation(object):
@@ -78,7 +83,7 @@
values with the new ones for all the parameters different to None.
"""
for attr, value in vars(new_config).items():
- if value:
+ if value is not None:
setattr(self, attr, value)
def __init__(self, simulator, log, dut, test_config, calibration_table):
@@ -387,6 +392,22 @@
self.simulator.configure_bts(new_config)
self.primary_config.incorporate(new_config)
+ def get_uplink_tx_power(self):
+ """ Returns the uplink tx power level
+
+ Returns:
+ calibrated tx power in dBm
+ """
+ return self.primary_config.input_power
+
+ def get_downlink_rx_power(self):
+ """ Returns the downlink tx power level
+
+ Returns:
+ calibrated rx power in dBm
+ """
+ return self.primary_config.output_power
+
def get_uplink_power_from_parameters(self, parameters):
""" Reads uplink power from a list of parameters. """
@@ -396,14 +417,11 @@
if values[1] in self.UPLINK_SIGNAL_LEVEL_DICTIONARY:
return self.UPLINK_SIGNAL_LEVEL_DICTIONARY[values[1]]
else:
- try:
- if values[1][0] == 'n':
- # Treat the 'n' character as a negative sign
- return -int(values[1][1:])
- else:
- return int(values[1])
- except ValueError:
- pass
+ if values[1][0] == 'n':
+ # Treat the 'n' character as a negative sign
+ return -float(values[1][1:])
+ else:
+ return float(values[1])
# If the method got to this point it is because PARAM_UL_PW was not
# included in the test parameters or the provided value was invalid.
@@ -421,11 +439,15 @@
values = self.consume_parameter(parameters, self.PARAM_DL_PW, 1)
if values:
- if values[1] not in self.DOWNLINK_SIGNAL_LEVEL_DICTIONARY:
- raise ValueError("Invalid signal level value {}.".format(
- values[1]))
- else:
+ if values[1] in self.DOWNLINK_SIGNAL_LEVEL_DICTIONARY:
return self.DOWNLINK_SIGNAL_LEVEL_DICTIONARY[values[1]]
+ else:
+ if values[1][0] == 'n':
+ # Treat the 'n' character as a negative sign
+ return -float(values[1][1:])
+ else:
+ return float(values[1])
+
else:
# Use default value
power = self.DOWNLINK_SIGNAL_LEVEL_DICTIONARY['excellent']
@@ -458,7 +480,7 @@
# Try to use measured path loss value. If this was not set, it will
# throw an TypeError exception
try:
- calibrated_power = round(power + self.dl_path_loss)
+ calibrated_power = round(power + self.dl_path_loss, 1)
if calibrated_power > self.simulator.MAX_DL_POWER:
self.log.warning(
"Cannot achieve phone DL Rx power of {} dBm. Requested TX "
@@ -481,7 +503,7 @@
except TypeError:
self.log.info("Phone downlink received power set to {} (link is "
"uncalibrated).".format(round(power)))
- return round(power)
+ return round(power, 1)
def calibrated_uplink_tx_power(self, bts_config, signal_level):
""" Calculates the power level at the instrument's input in order to
@@ -507,7 +529,7 @@
# Try to use measured path loss value. If this was not set, it will
# throw an TypeError exception
try:
- calibrated_power = round(power - self.ul_path_loss)
+ calibrated_power = round(power - self.ul_path_loss, 1)
if calibrated_power < self.UL_MIN_POWER:
self.log.warning(
"Cannot achieve phone UL Tx power of {} dBm. Requested UL "
@@ -530,7 +552,7 @@
except TypeError:
self.log.info("Phone uplink transmitted power set to {} (link is "
"uncalibrated).".format(round(power)))
- return round(power)
+ return round(power, 1)
def calibrate(self, band):
""" Calculates UL and DL path loss if it wasn't done before.
diff --git a/src/chromiumos/test/callbox/docker/cellular/simulation_utils/LteSimulation.py b/src/chromiumos/test/callbox/docker/cellular/simulation_utils/LteSimulation.py
index 36df63b..d877237 100644
--- a/src/chromiumos/test/callbox/docker/cellular/simulation_utils/LteSimulation.py
+++ b/src/chromiumos/test/callbox/docker/cellular/simulation_utils/LteSimulation.py
@@ -2,12 +2,15 @@
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
+# TODO(b/254347891): unify formatting and ignore specific lints in callbox libraries
+# pylint: skip-file
+
+from enum import Enum
import math
import time
-from enum import Enum
-from .BaseSimulation import BaseSimulation
from . import BaseCellularDut
+from .BaseSimulation import BaseSimulation
class TransmissionMode(Enum):
@@ -80,13 +83,12 @@
# Units in which signal level is defined in DOWNLINK_SIGNAL_LEVEL_DICTIONARY
DOWNLINK_SIGNAL_LEVEL_UNITS = "RSRP"
- # RSRP signal levels thresholds (as reported by Android) in dBm/15KHz.
- # Excellent is set to -75 since callbox B Tx power is limited to -30 dBm
+ # RSRP signal levels thresholds taken from cellular_capability_3gpp.cc
DOWNLINK_SIGNAL_LEVEL_DICTIONARY = {
- 'excellent': -75,
- 'high': -110,
- 'medium': -115,
- 'weak': -120,
+ 'excellent': -88,
+ 'high': -98,
+ 'medium': -108,
+ 'weak': -118,
'disconnected': -170
}
@@ -829,25 +831,6 @@
# Now that the band is set, calibrate the link if necessary
self.load_pathloss_if_required()
- def calibrated_downlink_rx_power(self, bts_config, rsrp):
- """ LTE simulation overrides this method so that it can convert from
- RSRP to total signal power transmitted from the basestation.
-
- Args:
- bts_config: the current configuration at the base station
- rsrp: desired rsrp, contained in a key value pair
- """
-
- power = self.rsrp_to_signal_power(rsrp, bts_config)
-
- self.log.info(
- "Setting downlink signal level to {} RSRP ({} dBm)".format(
- rsrp, power))
-
- # Use parent method to calculate signal level
- return super(LteSimulation,
- self).calibrated_downlink_rx_power(bts_config, power)
-
def downlink_calibration(self, rat=None, power_units_conversion_func=None):
""" Computes downlink path loss and returns the calibration value.
@@ -864,8 +847,7 @@
"""
return super().downlink_calibration(
- rat='lteDbm',
- power_units_conversion_func=self.rsrp_to_signal_power)
+ rat='lteDbm')
def rsrp_to_signal_power(self, rsrp, bts_config):
""" Converts rsrp to total band signal power