コード例 #1
0
def get_pos_vel_data(db_name):
    docs = DBUtils.get_docs(db_name, 'ros_amcl_pose')
    num_of_docs = len(docs)
    data = np.zeros((num_of_docs, 5))
    # data (nx6) is arranged as follows
    for i, doc in enumerate(docs):
        # get position information from amcl_pose localisation
        data[i][0] = DataUtils.get_var_value(doc, 'pose/pose/position/x')
        data[i][1] = DataUtils.get_var_value(doc, 'pose/pose/position/y')
        quat_x = DataUtils.get_var_value(doc, 'pose/pose/orientation/x')
        quat_y = DataUtils.get_var_value(doc, 'pose/pose/orientation/y')
        quat_z = DataUtils.get_var_value(doc, 'pose/pose/orientation/z')
        quat_w = DataUtils.get_var_value(doc, 'pose/pose/orientation/w')
        theta = tf.euler_from_quaternion((quat_w, quat_x, quat_y, quat_z))[2]
        data[i][2] = theta
        timestamp = DataUtils.get_var_value(doc, 'timestamp')

        # get velocity information from odom at the same timestamp as position
        odom_doc = DBUtils.get_last_doc_before(db_name, 'ros_ropod_odom',
                                               timestamp)
        vel_x = DataUtils.get_var_value(odom_doc, 'twist/twist/linear/x')
        vel_y = DataUtils.get_var_value(odom_doc, 'twist/twist/linear/y')
        data[i][3] = vel_x
        data[i][4] = vel_y

    return data
コード例 #2
0
    def test_get_windowed_correlation(self):
        var_pairs, correlation = DataUtils.get_windowed_correlations(
            variable_names=["var1", "var2", "var3"],
            data=np.array([[1, 2, 3], [4, 6, 8], [3, 6, 6]]),
            window_size=2)
        self.assertListEqual([("var1", "var2"), ("var1", "var3"), ("var2", "var3")], var_pairs)
        self.assertTrue(np.array_equal(np.array([[1.0, 1.0], [1.0, 0.0], [1.0, 0.0]]), correlation))

        var_pairs, correlation = DataUtils.get_windowed_correlations(
            variable_names=["var1", "var2", "var3"],
            data=np.array([[1, 2, 3], [4, 8, 8], [3, 6, 6]]),
            window_size=2)
        self.assertListEqual([("var1", "var2"), ("var1", "var3"), ("var2", "var3")], var_pairs)
        self.assertTrue(np.array_equal(np.array([[1.0, 0.0], [1.0, 0.0], [1.0, 1.0]]), correlation))
コード例 #3
0
    def get_latest_data(self, collection_name, variable_names):
        '''Returns a dictionary in which each key is a full variable name
        (namely a variable name of the format "collection_name/variable_name",
        where "variable_name" is a flattened version of a variable stored in the collection)
        and the value is a list string "[timestamp, value]", namely the latest value
        of the variable together with its timestamp (the list is a string to allow "value"
        to be of different types). If the given collection does not exist or
        there are no documents in it, returns an empty dictionary.

        Keyword arguments:
        @param collection_name -- name corresponding to a collection from the log database
        @param variable_names -- list of variable names that should be retrieved from the collection

        '''
        client = pm.MongoClient(port=self.db_port)
        database = client[self.db_name]
        collection = database[collection_name]
        doc = collection.find_one(sort=[('timestamp', pm.DESCENDING)])

        var_data = {}
        var_full_names = {}
        for var_name in variable_names:
            full_var_name = '{0}/{1}'.format(collection_name, var_name)
            var_data[full_var_name] = None
            var_full_names[var_name] = full_var_name

        if doc:
            for var_name in variable_names:
                var_value = DataUtils.get_var_value(doc, var_name)
                var_data[var_full_names[var_name]] = '[{0}, {1}]'.format(
                    doc['timestamp'], var_value)
        return var_data
コード例 #4
0
 def test_parse_bb_data_msg(self):
     response_msg = {
         'payload': {
             'dataList': {
                 'ros_ropod_cmd_vel/linear/x':
                 ['[1544441409.0861013, -0.0]', '[1544441409.191301, -0.0]',
                  '[1544441409.3861327, -0.0]', '[1544441409.586039, 0.030635764822363853]',
                  '[1544441409.695998, 0.04727638512849808]',
                  '[1544441409.8860662, 0.18040134012699127]']
                 },
             'receiverId': '347b6674-c915-4940-831e-9e86a0c194f7'
             },
         'header': {
             'metamodel': 'ropod-msg-schema.json',
             'type': 'DATA-QUERY',
             'timestamp': 1561631692.9753046,
             'msgId': 'b62c41fa-f98f-4fb9-9957-51f59819f900'
             }
         }
     variables, data = DataUtils.parse_bb_data_msg(response_msg)
     self.assertIsInstance(variables, list)
     self.assertIsInstance(data, list)
     self.assertEqual(len(variables), len(data))
     for variable_data in data:
         for entry in variable_data:
             self.assertEqual(len(entry), 2)
コード例 #5
0
    def get_e_stop_info(self):
        """Sends a query to bb query interface. Parses the data to
        get the required info.
        :returns: (bool, bool)  == (status, e_stop_pressed)

        """
        variables = [self.variable_name_pattern.replace('*', str(i)) for i\
                     in range(self.num_of_wheels)]
        dict_msg = self.black_box_comm.send_latest_data_query(variables)

        if dict_msg is None:
            return (False, True)

        _, data = DataUtils.parse_bb_latest_data_msg(dict_msg)
        if not data or all(not x for x in data):
            return (False, True)

        for i in data:
            if i is None:
                return (False, True)
        status_list = [i[1] for i in data]

        # if different wheel have different status
        if status_list.count(status_list[0]) != len(status_list):
            return (False, True)

        e_stop_pressed_list = []
        for wheel_number in range(self.num_of_wheels):
            status1 = status_list[wheel_number]
            # list of flags as described in https://git.ropod.org/ropod/smartwheel/blob/master/README.md
            flag_list = [
                i == '1' for i in list(bin(int(status1))[2:].zfill(5))[::-1]
            ]
            e_stop_pressed_list.append(flag_list[2])
        return (True, all(e_stop_pressed_list))
コード例 #6
0
    def get_experiment_feedback(session_id, robot_id):
        global data_thread
        experiment_ongoing = True
        feedback_received = False
        black_box_id = BBUtils.get_bb_id(robot_id)
        robot_smart_wheel_count = config.get_robot_smart_wheel_count(robot_id)
        diagnostic_vars = DataUtils.expand_var_names(
            experiment_diagnostic_vars, robot_smart_wheel_count)

        zyre_communicator.reset_experiment_feedback(robot_id)
        while experiment_ongoing:
            feedback_msg = zyre_communicator.get_experiment_feedback(robot_id)
            if feedback_msg and feedback_msg['robot_id'] == robot_id:
                feedback_received = True
            experiment_ongoing = send_experiment_feedback(
                robot_id, feedback_msg, feedback_received)

            if experiment_ongoing:
                with data_thread_lock:
                    if not data_thread:
                        data_thread = threading.Thread(
                            target=send_diagnostic_data,
                            kwargs={
                                'session_id': session_id,
                                'black_box_id': black_box_id,
                                'diagnostic_vars': diagnostic_vars
                            })
                        data_thread.start()

        global feedback_thread
        feedback_thread = None
コード例 #7
0
    def get_download_query():
        '''Responds to a data download query by sending a query to the appropriate
        black box and then saving the data to a temporary file for download.
        '''
        robot_id = request.args.get('robot_id', '', type=str)
        black_box_id = BBUtils.get_bb_id(robot_id)
        variable_list = request.args.get('variables').split(',')
        start_query_time = request.args.get('start_timestamp')
        end_query_time = request.args.get('end_timestamp')

        query_msg = DataUtils.get_bb_query_msg(session['uid'].hex,
                                               black_box_id,
                                               variable_list,
                                               start_query_time,
                                               end_query_time)
        query_result = zyre_communicator.get_query_data(query_msg)

        message = ''
        try:
            with open(query_result_file_path, 'w') as download_file:
                json.dump(query_result, download_file)
            return jsonify(success=True)
        except Exception as exc:
            print('[get_download_query_robot_data] %s' % str(exc))
            message = 'Data could not be retrieved'
            return jsonify(message=message)
コード例 #8
0
    def get_variables(self, data_source):
        '''Returns a list of all stored variables corresponding to the input data source

        Keyword arguments:
        @param data_source -- a string denoting the name of a data source; collection names
                              corresponding to data from this data source are expected to
                              start with the data source name

        '''
        (host, port) = DBUtils.get_db_host_and_port()
        if port != self.db_port:
            port = self.db_port

        client = pm.MongoClient(host=host, port=port)
        database = client[self.db_name]
        collection_names = database.list_collection_names()

        variable_list = []
        for collection_name in collection_names:
            if collection_name == 'system.indexes' or \
               not collection_name.startswith(data_source):
                continue

            collection = database[collection_name]
            variable_names = DataUtils.get_variable_list(
                collection_name, collection)
            variable_list.extend(variable_names)
        return variable_list
コード例 #9
0
 def test_get_all_measurement(self):
     database = self.client[self.test_db_name]
     collection = database[self.collection_name]
     doc_cursor = collection.find({})
     docs = [doc for doc in doc_cursor]
     measurements = DataUtils.get_all_measurements(docs, 'linear/x')
     self.assertEqual(measurements.shape, (149,))
コード例 #10
0
 def test_get_bb_query_msg_template(self):
     query_msg = DataUtils.get_bb_query_msg_template()
     self.assertIsInstance(query_msg, dict)
     self.assertIn('header', query_msg)
     self.assertIsInstance(query_msg['header'], dict)
     self.assertIn('metamodel', query_msg['header'])
     self.assertEqual('ropod-msg-schema.json', query_msg['header']['metamodel'])
     self.assertIn('payload', query_msg)
     self.assertIsInstance(query_msg['payload'], dict)
コード例 #11
0
    def get_data(self,
                 collection_name,
                 variable_names,
                 start_time=-1,
                 end_time=-1):
        '''Returns a dictionary in which each key is a full variable name
        (namely a variable name of the format "collection_name/variable_name",
        where "variable_name" is a flattened version of a variable stored in the collection)
        and each value is a list of "[timestamp, value]" strings, namely
        each entry in the list corresponds to a value of the variable at
        a particular timestamp (the entries are in string format to allow "value"
        to be of different types)

        Keyword arguments:
        @param collection_name -- name corresponding to a collection from the log database
        @param variable_names -- list of variable names that should be retrieved from the collection
        @param start_time -- a UNIX timestamp in seconds representing the start time
                             for the queried data (default -1, in which case
                             there is no lower bound for the timestamp
                             of the retrieved data)
        @param end_time -- a UNIX timestamp in seconds representing the end time
                           for the queried data (default -1, in which case there is
                           no upper bound for the timestamp of the retrieved data)

        '''
        client = pm.MongoClient(port=self.db_port)
        database = client[self.db_name]
        collection = database[collection_name]

        docs = {}
        if start_time == -1 and end_time == -1:
            docs = collection.find({})
        elif start_time == -1:
            docs = collection.find({'timestamp': {'$lte': end_time}})
        elif end_time == -1:
            docs = collection.find({'timestamp': {'$gte': start_time}})
        else:
            docs = collection.find(
                {'timestamp': {
                    '$gte': start_time,
                    '$lte': end_time
                }})

        var_data = {}
        var_full_names = {}
        for var_name in variable_names:
            full_var_name = '{0}/{1}'.format(collection_name, var_name)
            var_data[full_var_name] = []
            var_full_names[var_name] = full_var_name

        for doc in docs:
            for var_name in variable_names:
                var_value = DataUtils.get_var_value(doc, var_name)
                var_data[var_full_names[var_name]].append('[{0}, {1}]'.format(
                    doc['timestamp'], var_value))
        return var_data
コード例 #12
0
    def send_diagnostic_data(session_id, black_box_id, diagnostic_vars):
        end_query_time = int(time.time())
        start_query_time = end_query_time - 10

        query_msg = DataUtils.get_bb_query_msg(session_id, black_box_id,
                                               diagnostic_vars,
                                               start_query_time,
                                               end_query_time)
        query_result = zyre_communicator.get_query_data(query_msg)

        try:
            variables, data = DataUtils.parse_bb_data_msg(query_result)
            vel_vars, vel_data = get_variable_data('velocity', variables, data)
            socketio.emit('vel_data',
                          json.dumps({
                              'variables': vel_vars,
                              'data': vel_data
                          }),
                          namespace='/experiments')

            accel_vars, accel_data = get_variable_data('accel', variables,
                                                       data)
            socketio.emit('accel_data',
                          json.dumps({
                              'variables': accel_vars,
                              'data': accel_data
                          }),
                          namespace='/experiments')

            gyro_vars, gyro_data = get_variable_data('gyro', variables, data)
            socketio.emit('gyro_data',
                          json.dumps({
                              'variables': gyro_vars,
                              'data': gyro_data
                          }),
                          namespace='/experiments')
        except Exception as exc:
            print('[send_diagnostic_data] {0} does not seem to be responding'.
                  format(black_box_id))
            print(str(exc))

        global data_thread
        data_thread = None
コード例 #13
0
 def get_diff_drive_statuses(self):
     current_time = time.time()
     variables = []
     for inp in self.variable_names:
         v = [inp.replace('*', str(i)) for i\
                      in range(self.num_wheels)]
         variables.extend(v)
     dict_msg = self.black_box_comm.send_query(current_time - 1.0,
                                               current_time, variables)
     var_names, data = DataUtils.parse_bb_data_msg(dict_msg)
     result, overall_result = self.__process_data(var_names, data)
     return result, overall_result
コード例 #14
0
    def send_latest_data_query(self, variables):
        """
        create and send a query message to black box query interface through
        pyre shout.

        :variables: list of strings
        :returns: None

        """
        msg_sender_id = str(uuid.uuid4())
        data_query_msg = DataUtils.get_bb_latest_data_query_msg(
            msg_sender_id, self.black_box_id, variables)
        return self.get_black_box_data(data_query_msg)
コード例 #15
0
    def setup_robot_query_params(robots):
        for robot in robots:
            # for each robot, we expand the smart wheel variable names
            # depending on the robot's smart wheel count, combine the
            # query variables into a single list, and generate a robot-specific
            # data query message
            black_box_id = BBUtils.get_bb_id(robot)
            robot_smart_wheel_count = config.get_robot_smart_wheel_count(robot)
            expanded_wheel_vars = DataUtils.expand_var_names(wheel_vars, robot_smart_wheel_count)
            query_vars = expanded_wheel_vars + cmd_vel_vars
            query_msg = DataUtils.get_bb_latest_data_query_msg(session['uid'].hex,
                                                               black_box_id,
                                                               query_vars)
            robot_data_query_msgs[robot] = query_msg

            # for each robot, we get a short variable name mapping for the smart wheel variables
            robot_wheel_var_name_mapping[robot] = []
            for i in range(robot_smart_wheel_count):
                robot_wheel_var_name_mapping[robot].append({})
                for var in wheel_var_name_mapping:
                    var_name = var.replace('*', str(i+1))
                    robot_wheel_var_name_mapping[robot][i][var_name] = wheel_var_name_mapping[var]
コード例 #16
0
    def get_robot_data():
        robot_id = request.args.get('robot_id', '', type=str)
        black_box_id = BBUtils.get_bb_id(robot_id)
        variable_list = request.args.get('variables').split(',')
        start_query_time = request.args.get('start_timestamp')
        end_query_time = request.args.get('end_timestamp')

        query_msg = DataUtils.get_bb_query_msg(session['uid'].hex,
                                               black_box_id,
                                               variable_list,
                                               start_query_time,
                                               end_query_time)
        query_result = zyre_communicator.get_query_data(query_msg)

        variables = list()
        data = list()
        message = ''
        try:
            variables, data = DataUtils.parse_bb_data_msg(query_result)
        except Exception as exc:
            print('[get_robot_data] %s' % str(exc))
            message = 'Data could not be retrieved'
        return jsonify(variables=variables, data=data, message=message)
コード例 #17
0
    def send_query(self, variables):
        """create and send a query message to black box query interface through
        pyre shout.

        :variables: list of strings
        :returns: None

        """
        if len(variables) == 0:
            return None
        msg_sender_id = str(uuid.uuid4())
        data_query_msg = DataUtils.get_bb_latest_data_query_msg(
            msg_sender_id, self.black_box_id, variables)
        self.sender_ids.append(msg_sender_id)
        self.data['bb_variables'] = {}
        self.shout(data_query_msg)
コード例 #18
0
    def test_get_bb_latest_data_query_msg(self):
        query_msg = DataUtils.get_bb_latest_data_query_msg(
            sender_id='some_unique_id',
            bb_id='black_box_101',
            variable_list=['variable_name'])

        self.assertIsInstance(query_msg, dict)
        self.assertIn('header', query_msg)
        self.assertIsInstance(query_msg['header'], dict)
        self.assertIn('type', query_msg['header'])
        self.assertEqual('LATEST-DATA-QUERY', query_msg['header']['type'])
        self.assertIn('timestamp', query_msg['header'])
        self.assertIn('payload', query_msg)
        self.assertIsInstance(query_msg['payload'], dict)
        self.assertEqual('some_unique_id', query_msg['payload'].get('senderId', None))
        self.assertEqual('black_box_101', query_msg['payload'].get('blackBoxId', None))
        self.assertListEqual(['variable_name'], query_msg['payload'].get('variables', None))
コード例 #19
0
    def get_robot_variables():
        robot_id = request.args.get('robot_id', '', type=str)
        black_box_id = BBUtils.get_bb_id(robot_id)

        query_msg = dict(msg_data)
        query_msg['header']['type'] = 'VARIABLE-QUERY'
        query_msg['payload']['senderId'] = session['uid'].hex
        query_msg['payload']['blackBoxId'] = black_box_id
        query_result = zyre_communicator.get_query_data(query_msg)

        variables = dict()
        message = ''
        try:
            variables = DataUtils.parse_bb_variable_msg(query_result)
        except Exception as exc:
            print('[get_robot_variables] %s' % str(exc))
            message = 'Variable list could not be retrieved'
        return jsonify(robot_variables=variables, message=message)
コード例 #20
0
    def test_parse_bb_variable_msg(self):
        response_msg = {
            'payload':
                {'variableList':
                    {'ros':
                        ['ros_cmd_vel/linear/x', 'ros_cmd_vel/linear/y']
                        }
                    }
                }

        response = DataUtils.parse_bb_variable_msg(response_msg)
        self.assertIsInstance(response, dict)
        self.assertIn('ros_cmd_vel', response)
        self.assertIsInstance(response['ros_cmd_vel'], dict)
        self.assertIn('linear', response['ros_cmd_vel'])
        self.assertIsInstance(response['ros_cmd_vel']['linear'], dict)
        self.assertIn('x', response['ros_cmd_vel']['linear'])
        self.assertIn('y', response['ros_cmd_vel']['linear'])
コード例 #21
0
 def test_parse_bb_latest_data_msg(self):
     response_msg = {
         'payload': {
             'receiverId': '7276ff82-b265-4176-a177-b7936c3c1341',
             'dataList': {'ros_ropod_cmd_vel/linear/x': '[1544441432.830365, 0.0]'}
             },
         'header': {
             'type': 'LATEST-DATA-QUERY',
             'metamodel': 'ropod-msg-schema.json',
             'msgId': 'e46325a0-a57d-4ec9-919b-a32c91f342af',
             'timestamp': 1561636142.9828317}
         }
     variables, data = DataUtils.parse_bb_latest_data_msg(response_msg)
     self.assertIsInstance(variables, list)
     self.assertIsInstance(data, list)
     self.assertEqual(len(variables), len(data))
     for entry in data:
         self.assertEqual(len(entry), 2)
    def _get_localisation_avg_variance(self):
        """Provide a localisation average variance along with status after querying
        black box.
        :returns: (bool, float, float, float, bool)

        """
        dict_msg = self.black_box_comm.send_latest_data_query(
            [self.bb_variable_name])
        if not dict_msg:
            return (False, 0.0, 0.0, 0.0, False)

        _, data = DataUtils.parse_bb_latest_data_msg(dict_msg)
        if not data or not data[0]:
            return (False, 0.0, 0.0, 0.0, False)
        covariance_matrix = data[0][1]
        avg_variance = (covariance_matrix[0] + covariance_matrix[7] +
                        covariance_matrix[35]) / 3.0
        return (True, covariance_matrix[0], covariance_matrix[7],
                covariance_matrix[35],
                avg_variance < self.avg_variance_threshold)
コード例 #23
0
    def get_robot_data(robot_id):
        robot_smart_wheel_count = config.get_robot_smart_wheel_count(robot_id)

        while True:
            data_msg = zyre_communicator.get_query_data(robot_data_query_msgs[robot_id])
            variables, data = DataUtils.parse_bb_latest_data_msg(data_msg)

            short_vel_vars = [cmd_vel_var_name_mapping[x] for x in cmd_vel_vars]
            wheel_data = []
            for i in range(robot_smart_wheel_count):
                wheel_data.append({})
                for var in wheel_vars:
                    expanded_var_name = var.replace('*', str(i))
                    wheel_data[i][expanded_var_name] = None

            vel_data = list()
            if variables:
                try:
                    for i in range(robot_smart_wheel_count):
                        for var in wheel_vars:
                            expanded_var_name = var.replace('*', str(i))
                            wheel_data[i][expanded_var_name] = data[variables.index(expanded_var_name)]
                    socketio.emit('wheel_data',
                                  json.dumps({'variables': robot_wheel_var_name_mapping[robot_id],
                                              'data': wheel_data}),
                                  namespace='/real_time_monitoring')
                except ValueError as exc:
                    print('[real_time_monitoring] Smart wheel data error')
                    print(str(exc))

                try:
                    vel_data = [data[variables.index(x)] for x in cmd_vel_vars]
                    socketio.emit('cmd_vel_data',
                                  json.dumps({'variables': short_vel_vars,
                                              'data': vel_data}),
                                  namespace='/real_time_monitoring')
                except ValueError as exc:
                    print('[real_time_monitoring] Velocity data error')
                    print(str(exc))

            socketio.sleep(1)
コード例 #24
0
    def get_avg_battery(self):
        """Send a query message to bb query interface for voltage values for
        each wheel. Take average voltage and find percentage.
        :returns: (bool, float) == (status, percentage)

        """
        variables = [self.variable_name_pattern.replace('*', str(i)) for i\
                     in range(self.num_of_wheels)]
        dict_msg = self.black_box_comm.send_latest_data_query(variables)
        if dict_msg is None:
            return (False, 0.0)

        _, data = DataUtils.parse_bb_latest_data_msg(dict_msg)
        if not data or all(not x for x in data):
            return (False, 0.0)

        for i in data:
            if i is None:
                return (False, 0.0)
        battery_voltage = sum([i[1] for i in data]) / self.num_of_wheels
        battery_percentage = ((battery_voltage - self.lowest_voltage) / \
                (self.highest_voltage - self.lowest_voltage)) * 100.0
        return (True, battery_percentage)
コード例 #25
0
 def test_get_variable_list(self):
     database = self.client[self.test_db_name]
     collection = database[self.collection_name]
     variable_list = DataUtils.get_variable_list(self.collection_name, collection)
     self.assertCountEqual(variable_list, ['ros_ropod_cmd_vel/linear/x', 'ros_ropod_cmd_vel/linear/y', 'ros_ropod_cmd_vel/linear/z', 'ros_ropod_cmd_vel/angular/x', 'ros_ropod_cmd_vel/angular/y', 'ros_ropod_cmd_vel/angular/z'])
コード例 #26
0

def get_label(event_start, event_end):
    if event_start and event_end:
        if event_start == 'success':
            return 1
        else:
            return 0
    return 0


if __name__ == '__main__':
    log_db_name = 'pull_logs'

    event_docs = DBUtils.get_all_docs(log_db_name, 'ros_event')
    event_timestamps = DataUtils.get_all_measurements(event_docs, 'timestamp')
    event_descriptions = DataUtils.get_all_measurements(
        event_docs, 'description')

    data_point_count = len(event_timestamps) // 2
    object_positions = np.zeros((data_point_count, 3))
    goal_positions = np.zeros((data_point_count, 3))
    distances_from_edge = np.zeros((data_point_count, 1))
    motion_durations = np.zeros((data_point_count, 1))

    labels = np.zeros(data_point_count, dtype=int)
    data_idx = 0
    for i in range(0, data_point_count * 2, 2):
        object_positions[data_idx], _ = get_object_pose(
            log_db_name, event_timestamps[i])
        goal_positions[data_idx], _ = get_goal_pose(log_db_name,
コード例 #27
0
 def test_expand_var_names(self):
     var_names = DataUtils.expand_var_names(['var1/*'], 2)
     self.assertListEqual(var_names, ['var1/0', 'var1/1'])
コード例 #28
0
 def test_find_correlated_variables(self):
     var_pairs = DataUtils.find_correlated_variables(
         variable_names=["var1", "var2", "var3"],
         measurement_matrix=np.array([[1, 2, 3], [4, 6, 8], [3, 6, 7]]))
     self.assertListEqual([("var1", "var2"), ("var1", "var3"), ("var2", "var3")], var_pairs)