|
Synchronising data from multiple Android sensor files into one file |
Tags | other☁android☁opensignals mobile☁file handling ☁file synchronisation |
The
OpenSignals mobile application
(
Google Play link
) allows to acquire data from the sensors that are built into the hardware of an Android smartphone. When acquiring data from multiple Android sensors, the data for each is saved into an individual ".txt" file. In order to properly put the data of the sensors into context the files need to be synchronised. In this
Jupyter Notebook
we will have a look at how to synchronise the sensors and how to write them all into a single ".txt" file.
For this
Jupyter Notebook
we will synchronise the data from five sensors: The accelerometer, GPS, light, proximity, and significant motion. This is done because these sensors represent a wide range of different acquisition types. However, the procedures shown here can be also applied to as many Android sensors as you record using the
OpenSignals mobile application
.
As part of this Jupyter Notebook we will guide you through all essential steps for synchronising the Android sensors that will allow you to have full control over the entire synchronisation process. In the last section we will present a function integrated into our biosignalsnotebooks package that conveniently handles all these steps for you.
In case this is your first time working with Android sensors, we highly recommend reading the
Introduction to Android sensors
notebook with general information on Android sensors.
If you want to have a look into the implementation of all this functions, you are welcome to visit our
GitHub biosignalsnotebooks
repository
. We will provide direct links to each presented function throughout this
notebook
.
1 - Package imports
First, lets import the biosignalsnotebooks package. All functions we will need for the synchronisation steps are part of this package. Furthermore, we are going to import the os package.# biosignalsnotebooks package
import biosignalsnotebooks as bsnb
# package for using operating system dependent functionality
import os
2 - Loading the data and gathering information on the signals
Before starting with the actual synchronisation, we will load the sensor data and gather some useful information about the signals. We will do this with the load_android_data(...)This function takes two inputs:
The function returns the
sensor data
in a list and a
dictionary
with the following information about the signals:
In order to use this function we will first have to make a list containing all the file paths pointing to the files we want to synchronise. For simplicity we added all our files into one folder, thus making the process of creating a file list much more straight forward.
When we run the function with the parameter print_report=True , a report is printed. The printed report shows that the accelerometer samples at the highest rate, while the GPS, light, and the proximity sensor sample at lower rates. Furthermore, we see that the significant motion sensor detected only one motion that it labelled as significant. Thus, its sampling rate is set to zero. The report also shows that the proximity sensor was the first to start recording, while the significant motion sensor was the last to start recording. The accelerometer was the last to stop recording and the significant motion sensor the first to stop recording.
(*) in case you are wondering why an average sampling rate is displayed, then have a look at this
notebook
about resampling signals recorded with Android sensors.
# set file path
path = '../../images/other/android_file_sync/'
# get a list with all the files within that folder
file_list = os.listdir(path)
# make full path for each file
file_list = [path + file for file in file_list]
# load the files
sensor_data, report = bsnb.load_android_data(file_list)
3 - Padding all signals to the same length
Since the signals are going to be synchronised and written into the same file, all signals have to be of the same length. However, depending on what parts of the signals we want to include into our synchronised file, the length to which all signals should be padded to varies. In order to make this a little bit more clear, we will have a look at two "toy" signals.For these two signals, there are four possible ways on how to decide which parts of these signals to include into the synchronisation. The graphs are shown below.
In order to have the freedom to explore all possible options, the biosignalsnotebooks package provides a function that allows setting when to start and when to end the synchronisation. This, of course, means that all signals will either be padded or cropped to the defined start and end points. To provide an intuitive usage, the start and end points are defined by the sensor names, thus, making it possible to easily choose from our set of recorded signals.
Additionally, it gives the possibility to set the type of padding to be used. There are only some exceptions because an arbitrary padding doesn"t make sense for all sensor types. The GPS always uses a padding of type "same", thus mimicking that the phone is at a fixed location. The significant motion sensor is always padded with zeros.
The name of function is
pad_android_data(...)
and it takes the following inputs:
The function returns the padded sensor data within a list.
For the purpose of this Jupyter Notebook we will be using the entire recording time (start when the proximity sensors starts recording and end when the accelerometer sensor stops recording) and we will pad using the padding type "same" . This means that the values at the start and end of the recording are repeated.
padded_sensor_data = bsnb.pad_android_data(sensor_data, report, start_with='Proximity', end_with='Acc', padding_type='same')
4 - Resampling all signals to the same sampling rate
In this next step, we will resample all our signals to the same sampling rate. This has to be done in order to ensure that all signal columns are of the same length. We will be using the function we developed in the notebook focused on resampling signals recorded with Android sensorsFor each sensor we are going to resample the data to a sampling rate of 100 Hz , which would be the approximate sampling rate of the accelerometer according to the report we generated above. In addition to that we will shift the time axis to start at zero and display it in seconds and use the interpolation type "previous" . The results of the resampling are then saved into two lists. One for holding the resampled signal data and the other for holding the resampled time axes.
# list for holding the resampled data
re_sampled_data = []
# list for holding the time axes of each sensor
re_sampled_time = []
# cycle over the sig
for data in padded_sensor_data:
# resample the data ('_' suppresses the output for the sampling rate)
re_time, re_data, sampling_rate = bsnb.re_sample_data(data[:,0], data[:,1:], shift_time_axis=True, sampling_rate=100, kind_interp='previous')
# add the the time and data to the lists
re_sampled_time.append(re_time)
re_sampled_data.append(re_data)
Since we resampled all of our signals to the same sampling rate, all time axes should be equal and the data of each sensor should be of the same length. We can easily check this by doing the following:
print('Checking for number of samples in each sensor')
# cycle through the data list
for i,data in enumerate(re_sampled_data):
# get the sensor name
name = report['names'][i]
# print the first axis of the data
print('{}: {}'.format(name,data.shape[0]))
# get the number of unique time axes
unique_axes = np.unique(re_sampled_time)
print('\nNumber of unique time axes: {}'.format(unique_axes.ndim))
5 - Creating a new header
Next, we are going to create a new header that will be written to the file in which we are going to store all our data. This can be done using the create_android_sync_header(...)The function takes a single input:
It returns the header as a string.
# create header
header = bsnb.create_android_sync_header(file_list, sampling_rate)
6 - Writing the synchronised data to a new file
The last step that we need to conclude the synchronisation is to write our data to a new file. For this, you can use the save_synchronised_android_data(...)
The function takes the following inputs:
The function returns the path, where the file was saved. We will save the file with the synchronised data to our current working directory. You can of course choose any other valid directory.
# get the current path
save_path = os.path.abspath(os.getcwd())
# save the synchronised data
bsnb.save_synchronised_android_data(re_sampled_time[0], re_sampled_data, header, save_path)
Thus, our step by step synchronisation process is concluded. Following these steps gives you full control over the entire synchronisation process and all data that is returned during that process.
7 - Synchronisation with a single function call
In case you want to use a function that conveniently handles everything for you, you can use the sync_android_files(...)The function takes the following inputs:
As the parameter automatic_sync indicates, the function can be run in two different modes:
If automatic_sync=True , the function will do a full automatic synchronisation of the files. The synchronisation will only take place in the time window in which all sensors are running simultaneously. The rest of the data is cropped accordingly. Furthermore, the sampling rate for resampling the signals is set to the highest sampling rate present. This sampling rate is rounded to the next tens digit (i.e 43 Hz -> 40 Hz | 98 Hz -> 100 Hz). Sampling rates below 5 Hz are set to 1 Hz. The interpolation type "previous" is always used. In this mode, the function will give feedback on what it is doing and how it is setting the values.
If automatic_sync=False , the function will run in an interactive mode. It will guide you through the entire synchronisation process step by step and prompt you for specific inputs that are needed to set certain parameters.
Below, we run the function in the automatic synchronisation mode. Feel free to change the boolean to "False" and try out the function in the interactive mode.
bsnb.sync_android_files(file_list, save_path, sync_file_name='automatic_android_sync', automatic_sync=True)
In this Jupyter notebook we learned how to synchronise multiple Android sensors and write them to a single file. Additionally we saw how we can do the entire synchronisation process with a single function call.
We hope that you have enjoyed this guide
.
biosiganlsnotebooks
is an environment in continuous expansion, so don"t stop your journey and learn more with the remaining
Notebooks
.