blob: 834b5ed96d8e3fb0133aca1badac2c9efbf4a7f9 [file] [log] [blame]
// Copyright 2022 The ChromiumOS Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
package cellular
import (
"context"
"math"
"time"
"github.com/golang/protobuf/ptypes/empty"
"go.chromium.org/tast-tests/cros/remote/cellular/callbox/manager"
"go.chromium.org/tast-tests/cros/services/cros/cellular"
"go.chromium.org/tast/core/errors"
"go.chromium.org/tast/core/testing"
"go.chromium.org/tast/core/testing/cellularconst"
"go.chromium.org/tast/core/testing/hwdep"
)
const (
// strengthMargin is the allowable margin of error for signal strength percent.
strengthMargin = 1.0
// rsrpMargin is the allowable margin of error to use while waiting for RSRP to update to requested value in dBm.
rsrpMargin = 3.0
)
// rxPower fetches the current signal power received at the dut in dBm.
type rxPower func(context.Context, cellular.RemoteCellularServiceClient) (float64, error)
// signalStrengthTest is a single test case in an attenuated signal strength test.
type signalStrengthTest struct {
maxPower float64
minPower float64
stepSize float64
stepCount int
fetchPower rxPower
callboxOpts *manager.ConfigureCallboxRequestBody
}
// passThreshold is the threshold at which it's ok if we can't detect the cellular network.
const passThreshold = -120
func init() {
testing.AddTest(&testing.Test{
Func: AttenuatedSignalStrength,
LacrosStatus: testing.LacrosVariantUnneeded,
Desc: "Progressively lowers the downlink power on the callbox and verifies that the signal strength calculated by shill decreases by a proportional amount",
Contacts: []string{"chromeos-cellular-team@google.com", "jstanko@google.com"},
BugComponent: "b:167157", // ChromeOS > Platform > Connectivity > Cellular
Attr: []string{"group:cellular", "cellular_callbox", "cellular_cmw_callbox"},
ServiceDeps: []string{"tast.cros.cellular.RemoteCellularService"},
SoftwareDeps: []string{"chrome"},
Fixture: "callboxManagedFixture",
// FM101 only reports RSSI to MM (b/274882743), this test relies on RSRP values.
HardwareDeps: hwdep.D(hwdep.SkipOnCellularModemType(cellularconst.ModemTypeFM101)),
Timeout: 15 * time.Minute,
Params: []testing.Param{
{
Name: "lte",
Val: signalStrengthTest{
maxPower: -88,
minPower: -128,
// decrease by ~20%, smaller step sizes may not be accurately resolvable if they're not at least 2*rsrpMargin
stepSize: -8,
stepCount: 4,
fetchPower: fetchLteRSRP,
callboxOpts: &manager.ConfigureCallboxRequestBody{
Hardware: manager.CallboxHardwareCMW,
CellularType: manager.CellularTechnologyLTE,
Parameters: []manager.CellConfiguration{
manager.NewLteCellConfiguration(
manager.RxPowerOption(manager.NewRxPower(-70)),
),
},
},
},
},
},
})
}
func AttenuatedSignalStrength(ctx context.Context, s *testing.State) {
tc := s.Param().(signalStrengthTest)
tf := s.FixtValue().(*manager.TestFixture)
dutConn := s.DUT().Conn()
if err := tf.ConnectToCallbox(ctx, dutConn, tc.callboxOpts); err != nil {
s.Fatal("Failed to initialize cellular connection: ", err)
}
req := &manager.ConfigureRxPowerRequestBody{Power: manager.NewRxPower(tc.maxPower)}
if err := tf.CallboxManagerClient.ConfigureRxPower(ctx, req); err != nil {
s.Fatal("Failed to change callbox uplink power: ", err)
}
pReq := tc.maxPower
// wait for received power at the DUT to update
pMeas, err := waitForPower(ctx, tc, tf.RemoteCellularClient, pReq)
if err != nil {
s.Fatal("Failed to wait for requested power: ", err)
}
serviceResp, err := tf.RemoteCellularClient.QueryService(ctx, &empty.Empty{})
if err != nil {
s.Fatal("Failed to get cellular service properties: ", err)
}
strength := serviceResp.Strength
calibrationOffset := pReq - pMeas
s.Logf("Starting power: %f dBm, strength: %d %%, incrementing by %f dBm", pMeas, strength, tc.stepSize)
for i := 0; i < tc.stepCount; i++ {
pReq += tc.stepSize
req := &manager.ConfigureRxPowerRequestBody{Power: manager.NewRxPower(pReq + calibrationOffset)}
if err := tf.CallboxManagerClient.ConfigureRxPower(ctx, req); err != nil {
s.Fatal("Failed to change callbox uplink power: ", err)
}
powerOld := pMeas
pMeas, err = waitForPower(ctx, tc, tf.RemoteCellularClient, pReq)
if err != nil {
if pReq <= passThreshold {
// Power is too low for detection, so just break here.
s.Log("Unable to detect cellular network signal: ", err)
break
}
s.Fatal("Failed to wait for requested power: ", err)
}
calibrationOffset = pReq + calibrationOffset - pMeas
// calculate expected decrease in signal strength
sDiffExpected := 100 * (powerOld - pMeas) / (tc.maxPower - tc.minPower)
serviceResp, err := tf.RemoteCellularClient.QueryService(ctx, &empty.Empty{})
if err != nil {
s.Fatal("Failed to get cellular service properties: ", err)
}
sDiff := float64(strength - serviceResp.Strength)
strength = serviceResp.Strength
s.Logf("Power: %f, strength: %d %%, offset: %f dBm", pMeas, strength, calibrationOffset)
// if we're > 90 or at 0 then we may have been clipped
if strength >= 90 {
continue
}
if strength == 0 {
break
}
if math.Abs(sDiffExpected-sDiff) > strengthMargin {
s.Fatalf("Failed to change signal strength, expected strength: %f+/-%f%%, got: %f%%", sDiffExpected, strengthMargin, sDiff)
}
}
}
// fetchLteRSRP fetches the RSRP for the current LTE signal.
func fetchLteRSRP(ctx context.Context, client cellular.RemoteCellularServiceClient) (float64, error) {
resp, err := client.QueryLTESignal(ctx, &empty.Empty{})
if err != nil {
return 0, err
}
return resp.Rsrp, nil
}
// waitForPower waits for the received power at the DUT to update within some margin of the requested value.
func waitForPower(ctx context.Context, test signalStrengthTest, client cellular.RemoteCellularServiceClient, pTarget float64) (float64, error) {
var pMeas float64
var err error
if err = testing.Poll(ctx, func(ctx context.Context) error {
if pMeas, err = test.fetchPower(ctx, client); err != nil {
return errors.Wrap(err, "failed to get DUT Rx signal properties")
}
if math.Abs(pTarget-pMeas) > rsrpMargin {
return errors.Errorf("waiting for DUT Rx power to reach %f, got %f", pTarget, pMeas)
}
return nil
}, &testing.PollOptions{Interval: time.Second}); err != nil {
return 0, errors.Wrap(err, "failed to wait for uplink power to reach the requested value")
}
return pMeas, nil
}