• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1#!/usr/bin/env python3
2#
3#   Copyright 2021 - The Android Open Source Project
4#
5#   Licensed under the Apache License, Version 2.0 (the 'License');
6#   you may not use this file except in compliance with the License.
7#   You may obtain a copy of the License at
8#
9#       http://www.apache.org/licenses/LICENSE-2.0
10#
11#   Unless required by applicable law or agreed to in writing, software
12#   distributed under the License is distributed on an 'AS IS' BASIS,
13#   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14#   See the License for the specific language governing permissions and
15#   limitations under the License.
16
17import os
18from glob import glob
19from time import sleep
20from collections import namedtuple
21from numpy import arange
22from pandas import DataFrame
23from acts.signals import TestError
24from acts.signals import TestFailure
25from acts.logger import epoch_to_log_line_timestamp
26from acts.context import get_current_context
27from acts_contrib.test_utils.gnss import LabTtffTestBase as lttb
28from acts_contrib.test_utils.gnss.gnss_test_utils import launch_eecoexer
29from acts_contrib.test_utils.gnss.gnss_test_utils import excute_eecoexer_function
30from acts_contrib.test_utils.gnss.gnss_test_utils import start_gnss_by_gtw_gpstool
31from acts_contrib.test_utils.gnss.gnss_test_utils import get_current_epoch_time
32from acts_contrib.test_utils.gnss.gnss_test_utils import check_current_focus_app
33from acts_contrib.test_utils.gnss.gnss_test_utils import process_ttff_by_gtw_gpstool
34from acts_contrib.test_utils.gnss.gnss_test_utils import check_ttff_data
35from acts_contrib.test_utils.gnss.gnss_test_utils import process_gnss_by_gtw_gpstool
36from acts_contrib.test_utils.gnss.gnss_test_utils import start_pixel_logger
37from acts_contrib.test_utils.gnss.gnss_test_utils import stop_pixel_logger
38from acts_contrib.test_utils.gnss.dut_log_test_utils import start_diagmdlog_background
39from acts_contrib.test_utils.gnss.dut_log_test_utils import get_gpstool_logs
40from acts_contrib.test_utils.gnss.dut_log_test_utils import stop_background_diagmdlog
41from acts_contrib.test_utils.gnss.dut_log_test_utils import get_pixellogger_bcm_log
42from acts_contrib.test_utils.gnss.gnss_testlog_utils import parse_gpstool_ttfflog_to_df
43
44
45def range_wi_end(ad, start, stop, step):
46    """
47    Generate a list of data from start to stop with the step. The list includes start and stop value
48    and also supports floating point.
49    Args:
50        start: start value.
51            Type, int or float.
52        stop: stop value.
53            Type, int or float.
54        step: step value.
55            Type, int or float.
56    Returns:
57        range_ls: the list of data.
58    """
59    if step == 0:
60        ad.log.warn('Step is 0. Return empty list')
61        range_ls = []
62    else:
63        if start == stop:
64            range_ls = [stop]
65        else:
66            range_ls = list(arange(start, stop, step))
67            if len(range_ls) > 0:
68                if (step < 0 and range_ls[-1] > stop) or (step > 0 and
69                                                          range_ls[-1] < stop):
70                    range_ls.append(stop)
71    return range_ls
72
73
74def check_ttff_pe(ad, ttff_data, ttff_mode, pe_criteria):
75    """Verify all TTFF results from ttff_data.
76
77    Args:
78        ad: An AndroidDevice object.
79        ttff_data: TTFF data of secs, position error and signal strength.
80        ttff_mode: TTFF Test mode for current test item.
81        pe_criteria: Criteria for current test item.
82
83    """
84    ret = True
85    ad.log.info("%d iterations of TTFF %s tests finished." %
86                (len(ttff_data.keys()), ttff_mode))
87    ad.log.info("%s PASS criteria is %f meters" % (ttff_mode, pe_criteria))
88    ad.log.debug("%s TTFF data: %s" % (ttff_mode, ttff_data))
89
90    if len(ttff_data.keys()) == 0:
91        ad.log.error("GTW_GPSTool didn't process TTFF properly.")
92        raise TestFailure("GTW_GPSTool didn't process TTFF properly.")
93
94    if any(
95            float(ttff_data[key].ttff_pe) >= pe_criteria
96            for key in ttff_data.keys()):
97        ad.log.error("One or more TTFF %s are over test criteria %f meters" %
98                     (ttff_mode, pe_criteria))
99        ret = False
100    else:
101        ad.log.info("All TTFF %s are within test criteria %f meters." %
102                    (ttff_mode, pe_criteria))
103        ret = True
104    return ret
105
106
107class GnssBlankingBase(lttb.LabTtffTestBase):
108    """ LAB GNSS Cellular Coex Tx Power Sweep TTFF/FFPE Tests"""
109
110    def __init__(self, controllers):
111        """ Initializes class attributes. """
112        super().__init__(controllers)
113        self.eecoex_func = ''
114        self.start_pwr = 10
115        self.stop_pwr = 24
116        self.offset = 1
117        self.result_cell_pwr = 10
118        self.gsm_sweep_params = None
119        self.lte_tdd_pc3_sweep_params = None
120        self.lte_tdd_pc2_sweep_params = None
121        self.sa_sensitivity = -150
122        self.gnss_pwr_lvl_offset = -5
123        self.maskfile = None
124
125    def setup_class(self):
126        super().setup_class()
127        req_params = ['sa_sensitivity', 'gnss_pwr_lvl_offset']
128        self.unpack_userparams(req_param_names=req_params)
129        cell_sweep_params = self.user_params.get('cell_pwr_sweep', [])
130        self.gsm_sweep_params = cell_sweep_params.get("GSM", [10, 33, 1])
131        self.lte_tdd_pc3_sweep_params = cell_sweep_params.get(
132            "LTE_TDD_PC3", [10, 24, 1])
133        self.lte_tdd_pc2_sweep_params = cell_sweep_params.get(
134            "LTE_TDD_PC2", [10, 26, 1])
135        self.sa_sensitivity = self.user_params.get('sa_sensitivity', -150)
136        self.gnss_pwr_lvl_offset = self.user_params.get('gnss_pwr_lvl_offset', -5)
137
138    def setup_test(self):
139        super().setup_test()
140        launch_eecoexer(self.dut)
141
142        # Set DUT temperature the limit to 60 degree
143        self.dut.adb.shell(
144            'setprop persist.com.google.eecoexer.cellular.temperature_limit 60')
145
146        # Get current context full path to create the log folder.
147        cur_test_item_dir = get_current_context().get_full_output_path()
148        self.gnss_log_path = os.path.join(self.log_path, cur_test_item_dir)
149        os.makedirs(self.gnss_log_path, exist_ok=True)
150
151        # Start GNSS chip log
152        if self.diag_option == "QCOM":
153            start_diagmdlog_background(self.dut, maskfile=self.maskfile)
154        else:
155            start_pixel_logger(self.dut)
156
157    def teardown_test(self):
158        super().teardown_test()
159        # Set gnss_vendor_log_path based on GNSS solution vendor.
160        gnss_vendor_log_path = os.path.join(self.gnss_log_path,
161                                            self.diag_option)
162        os.makedirs(gnss_vendor_log_path, exist_ok=True)
163
164        # Stop GNSS chip log and pull the logs to local file system.
165        if self.diag_option == "QCOM":
166            stop_background_diagmdlog(self.dut,
167                                      gnss_vendor_log_path,
168                                      keep_logs=False)
169        else:
170            stop_pixel_logger(self.dut)
171            self.log.info('Getting Pixel BCM Log!')
172            get_pixellogger_bcm_log(self.dut,
173                                    gnss_vendor_log_path,
174                                    keep_logs=False)
175
176        # Stop cellular Tx and close GPStool and EEcoexer APPs.
177        self.stop_cell_tx()
178        self.log.debug('Close GPStool APP')
179        self.dut.force_stop_apk("com.android.gpstool")
180        self.log.debug('Close EEcoexer APP')
181        self.dut.force_stop_apk("com.google.eecoexer")
182
183    def stop_cell_tx(self):
184        """
185        Stop EEcoexer Tx power.
186        """
187        # EEcoexer cellular stop Tx command.
188        stop_cell_tx_cmd = 'CELLR,19'
189
190        # Stop cellular Tx by EEcoexer.
191        self.log.info('Stop EEcoexer Test Command: {}'.format(stop_cell_tx_cmd))
192        excute_eecoexer_function(self.dut, stop_cell_tx_cmd)
193
194    def analysis_ttff_ffpe(self, ttff_data, json_tag=''):
195        """
196        Pull logs and parsing logs into json file.
197        Args:
198            ttff_data: ttff_data from test results.
199                Type, list.
200            json_tag: tag for parsed json file name.
201                Type, str.
202        """
203        # Create log directory.
204        gps_log_path = os.path.join(self.gnss_log_path,
205                                    'Cell_Pwr_Sweep_Results')
206
207        # Pull logs of GTW GPStool.
208        get_gpstool_logs(self.dut, gps_log_path, False)
209
210        # Parsing the log of GTW GPStool into pandas dataframe.
211        target_log_name_regx = os.path.join(gps_log_path, 'GPSLogs', 'files',
212                                            'GNSS_*')
213        self.log.info('Get GPStool logs from: {}'.format(target_log_name_regx))
214        gps_api_log_ls = glob(target_log_name_regx)
215        latest_gps_api_log = max(gps_api_log_ls, key=os.path.getctime)
216        self.log.info(
217            'Get latest GPStool log is: {}'.format(latest_gps_api_log))
218        try:
219            df_ttff_ffpe = DataFrame(
220                parse_gpstool_ttfflog_to_df(latest_gps_api_log))
221
222            # Add test case, TTFF and FFPE data into the dataframe.
223            ttff_dict = {}
224            for i in ttff_data:
225                data = ttff_data[i]._asdict()
226                ttff_dict[i] = dict(data)
227            ttff_time = []
228            ttff_pe = []
229            test_case = []
230            for value in ttff_dict.values():
231                ttff_time.append(value['ttff_sec'])
232                ttff_pe.append(value['ttff_pe'])
233                test_case.append(json_tag)
234            self.log.info('test_case length {}'.format(str(len(test_case))))
235
236            df_ttff_ffpe['test_case'] = test_case
237            df_ttff_ffpe['ttff_sec'] = ttff_time
238            df_ttff_ffpe['ttff_pe'] = ttff_pe
239            json_file = 'gps_log_{}.json'.format(json_tag)
240            json_path = os.path.join(gps_log_path, json_file)
241            # Save dataframe into json file.
242            df_ttff_ffpe.to_json(json_path, orient='table', index=False)
243        except ValueError:
244            self.log.warning('Can\'t create the parsed the log data in file.')
245
246    def gnss_hot_start_ttff_ffpe_test(self,
247                                      iteration,
248                                      sweep_enable=False,
249                                      json_tag=''):
250        """
251        GNSS hot start ttff ffpe tset
252
253        Args:
254            iteration: hot start TTFF test iteration.
255                    Type, int.
256                    Default, 1.
257            sweep_enable: Indicator for the function to check if it is run by cell_power_sweep()
258                    Type, bool.
259                    Default, False.
260            json_tag: if the function is run by cell_power_sweep(), the function would use
261                    this as a part of file name to save TTFF and FFPE results into json file.
262                    Type, str.
263                    Default, ''.
264        Raise:
265            TestError: fail to send TTFF start_test_action.
266        """
267        # Start GTW GPStool.
268        test_type = namedtuple('Type', ['command', 'criteria'])
269        test_type_ttff = test_type('Hot Start', self.hs_ttff_criteria)
270        test_type_pe = test_type('Hot Start', self.hs_ttff_pecriteria)
271        self.dut.log.info("Restart GTW GPSTool")
272        start_gnss_by_gtw_gpstool(self.dut, state=True)
273
274        # Get current time and convert to human readable format
275        begin_time = get_current_epoch_time()
276        log_begin_time = epoch_to_log_line_timestamp(begin_time)
277        self.dut.log.debug('Start time is {}'.format(log_begin_time))
278
279        # Run hot start TTFF
280        for i in range(3):
281            self.log.info('Start hot start attempt %d' % (i + 1))
282            self.dut.adb.shell(
283                "am broadcast -a com.android.gpstool.ttff_action "
284                "--es ttff hs --es cycle {} --ez raninterval False".format(
285                    iteration))
286            sleep(1)
287            if self.dut.search_logcat(
288                    "act=com.android.gpstool.start_test_action", begin_time):
289                self.dut.log.info("Send TTFF start_test_action successfully.")
290                break
291        else:
292            check_current_focus_app(self.dut)
293            raise TestError("Fail to send TTFF start_test_action.")
294
295        # Verify hot start TTFF results
296        ttff_data = process_ttff_by_gtw_gpstool(self.dut, begin_time,
297                                                self.simulator_location)
298
299        # Stop GTW GPSTool
300        self.dut.log.info("Stop GTW GPSTool")
301        start_gnss_by_gtw_gpstool(self.dut, state=False)
302
303        if sweep_enable:
304            self.analysis_ttff_ffpe(ttff_data, json_tag)
305
306        result_ttff = check_ttff_data(self.dut,
307                                      ttff_data,
308                                      ttff_mode=test_type_ttff.command,
309                                      criteria=test_type_ttff.criteria)
310        result_pe = check_ttff_pe(self.dut,
311                                  ttff_data,
312                                  ttff_mode=test_type_pe.command,
313                                  pe_criteria=test_type_pe.criteria)
314        if not result_ttff or not result_pe:
315            self.dut.log.warning('%s TTFF fails to reach '
316                                 'designated criteria' % test_type_ttff.command)
317            self.dut.log.info("Stop GTW GPSTool")
318            return False
319
320        return True
321
322    def hot_start_gnss_power_sweep(self,
323                                   start_pwr,
324                                   stop_pwr,
325                                   offset,
326                                   wait,
327                                   iteration=1,
328                                   sweep_enable=False,
329                                   title=''):
330        """
331        GNSS simulator power sweep of hot start test.
332
333        Args:
334            start_pwr: GNSS simulator power sweep start power level.
335                    Type, int.
336            stop_pwr: GNSS simulator power sweep stop power level.
337                    Type, int.
338            offset: GNSS simulator power sweep offset
339                    Type, int.
340            wait: Wait time before the power sweep.
341                    Type, int.
342            iteration: The iteration times of hot start test.
343                    Type, int.
344                    Default, 1.
345            sweep_enable: Indicator for power sweep.
346                          It will be True only in GNSS sensitivity search case.
347                    Type, bool.
348                    Defaule, False.
349            title: the target log folder title for GNSS sensitivity search test items.
350                    Type, str.
351                    Default, ''.
352        """
353
354        # Calculate loop range list from gnss_simulator_power_level and sa_sensitivity
355        range_ls = range_wi_end(self.dut, start_pwr, stop_pwr, offset)
356        sweep_range = ','.join([str(x) for x in range_ls])
357
358        self.log.debug(
359            'Start the GNSS simulator power sweep. The sweep range is [{}]'.
360            format(sweep_range))
361
362        if sweep_enable:
363            self.start_gnss_and_wait(wait)
364        else:
365            self.dut.log.info('Wait %d seconds to start TTFF HS' % wait)
366            sleep(wait)
367
368        # Sweep GNSS simulator power level in range_ls.
369        # Do hot start for every power level.
370        # Check the TTFF result if it can pass the criteria.
371        gnss_pwr_lvl = -130
372        for gnss_pwr_lvl in range_ls:
373
374            # Set GNSS Simulator power level
375            self.log.info('Set GNSS simulator power level to %.1f' %
376                          gnss_pwr_lvl)
377            self.gnss_simulator.set_power(gnss_pwr_lvl)
378            json_tag = title + '_gnss_pwr_' + str(gnss_pwr_lvl)
379
380            # GNSS hot start test
381            if not self.gnss_hot_start_ttff_ffpe_test(iteration, sweep_enable,
382                                                      json_tag):
383                sensitivity = gnss_pwr_lvl - offset
384                return False, sensitivity
385        return True, gnss_pwr_lvl
386
387    def gnss_init_power_setting(self, first_wait=180):
388        """
389        GNSS initial power level setting.
390        Args:
391            first_wait: wait time after the cold start.
392                        Type, int.
393                        Default, 180.
394        Returns:
395            True if the process is done successully and hot start results pass criteria.
396        Raise:
397            TestFailure: fail TTFF test criteria.
398        """
399
400        # Start and set GNSS simulator
401        self.start_and_set_gnss_simulator_power()
402
403        # Start 1st time cold start to obtain ephemeris
404        process_gnss_by_gtw_gpstool(self.dut, self.test_types['cs'].criteria)
405
406        self.hot_start_gnss_power_sweep(self.gnss_simulator_power_level,
407                                        self.sa_sensitivity,
408                                        self.gnss_pwr_lvl_offset, first_wait)
409
410        return True
411
412    def start_gnss_and_wait(self, wait=60):
413        """
414        The process of enable gnss and spend the wait time for GNSS to
415        gather enoung information that make sure the stability of testing.
416
417        Args:
418            wait: wait time between power sweep.
419                Type, int.
420                Default, 60.
421        """
422        # Create log path for waiting section logs of GPStool.
423        gnss_wait_log_dir = os.path.join(self.gnss_log_path, 'GNSS_wait')
424
425        # Enable GNSS to receive satellites' signals for "wait_between_pwr" seconds.
426        self.log.info('Enable GNSS for searching satellites')
427        start_gnss_by_gtw_gpstool(self.dut, state=True)
428        self.log.info('Wait for {} seconds'.format(str(wait)))
429        sleep(wait)
430
431        # Stop GNSS and pull the logs.
432        start_gnss_by_gtw_gpstool(self.dut, state=False)
433        get_gpstool_logs(self.dut, gnss_wait_log_dir, False)
434
435    def cell_power_sweep(self):
436        """
437        Linear search cellular power level. Doing GNSS hot start with cellular coexistence
438        and checking if hot start can pass hot start criteria or not.
439
440        Returns: final power level of cellular power
441        """
442        # Get parameters from user params.
443        ttft_iteration = self.user_params.get('ttff_iteration', 25)
444        wait_before_test = self.user_params.get('wait_before_test', 60)
445        wait_between_pwr = self.user_params.get('wait_between_pwr', 60)
446        power_th = self.start_pwr
447
448        # Generate the power sweep list.
449        power_search_ls = range_wi_end(self.dut, self.start_pwr, self.stop_pwr,
450                                       self.offset)
451
452        # Set GNSS simulator power level.
453        self.gnss_simulator.set_power(self.sa_sensitivity)
454
455        # Create gnss log folders for init and cellular sweep
456        gnss_init_log_dir = os.path.join(self.gnss_log_path, 'GNSS_init')
457
458        # Pull all exist GPStool logs into GNSS_init folder
459        get_gpstool_logs(self.dut, gnss_init_log_dir, False)
460
461        if power_search_ls:
462            # Run the cellular and GNSS coexistence test item.
463            for i, pwr_lvl in enumerate(power_search_ls):
464                self.log.info('Cellular power sweep loop: {}'.format(int(i)))
465                self.log.info('Cellular target power: {}'.format(int(pwr_lvl)))
466
467                # Enable GNSS to receive satellites' signals for "wait_between_pwr" seconds.
468                # Wait more time before 1st power level
469                if i == 0:
470                    wait = wait_before_test
471                else:
472                    wait = wait_between_pwr
473                self.start_gnss_and_wait(wait)
474
475                # Set cellular Tx power level.
476                eecoex_cmd = self.eecoex_func.format(str(pwr_lvl))
477                eecoex_cmd_file_str = eecoex_cmd.replace(',', '_')
478                excute_eecoexer_function(self.dut, eecoex_cmd)
479
480                # Get the last power level that can pass hots start ttff/ffpe spec.
481                if self.gnss_hot_start_ttff_ffpe_test(ttft_iteration, True,
482                                                      eecoex_cmd_file_str):
483                    if i + 1 == len(power_search_ls):
484                        power_th = pwr_lvl
485                else:
486                    if i == 0:
487                        power_th = self.start_pwr
488                    else:
489                        power_th = power_search_ls[i - 1]
490
491                # Stop cellular Tx after a test cycle.
492                self.stop_cell_tx()
493
494        else:
495            # Run the stand alone test item.
496            self.start_gnss_and_wait(wait_between_pwr)
497
498            eecoex_cmd_file_str = 'no_cellular_coex'
499            self.gnss_hot_start_ttff_ffpe_test(ttft_iteration, True,
500                                               eecoex_cmd_file_str)
501
502        self.log.info('The GNSS WWAN coex celluar Tx power is {}'.format(
503            str(power_th)))
504
505        return power_th
506