Data output
The data schema can be obtained with the sifibridge schema subcommand.
Data packet format
SiFi Bridge outputs sensor data as JSON packets (use -p flag for pretty-printed output). Each packet follows a standard structure defined by the DataPacket schema.
DataPacket structure
Each packet contains data from a single sensor at most.
Each data channel has the same number of samples, and the timestamps always has the same number of values too. Data values and timestamps are matched 1-to-1: (timestamps[0], data['ecg'][0]) form a pair.
{
"device": "BioPoint",
"id": "BP_7A1F",
"name": "my_bp",
"mac": "C2:EC:EF:30:0D:10",
"packet_type": "ecg",
"status": "ok",
"received_at": 1782573963.169,
"timestamps": [
17.826,
17.828,
17.83,
],
"data": {
"ecg": [
null,
0.0,
4.378609035326087e-7,
]
},
"sample_rate": 500.28058548806575,
"samples_lost": 1,
"download_progress": 90
}
Field descriptions
| Field | Type | Required | Description |
|---|---|---|---|
device | string | Yes | Device type, i.e. BioPoint or SiFiBand |
id | string | Yes | Device unique identifier |
name | string | Yes | Device name |
mac | string | Yes | Device MAC address |
packet_type | string | Yes | See Packet types |
status | string | Yes | See Packet Status |
received_at | double | Yes | Packet time of arrival (seconds since Unix epoch) |
data | HashMap[string, list[float]] | null | No | See Data channels |
timestamps | list[float] | null | No | Data timestamps (seconds since the start of the acquisition) |
sample_rate | double | null | No | Sampling rate (Hz). See Sampling rate |
samples_lost | uint | null | No | See Lost samples |
download_progress | uint8 | null | No | Memory download progress (percent) |
Packet types
The packet_type field indicates what kind of data is in the packet:
| Type | Description | Data channels |
|---|---|---|
ecg | Electrocardiography | ecg |
emg | Electromyography | emg |
eda | Electrodermal Activity | eda |
imu | Inertial Measurement Unit | ax, ay, az, qw, qx, qy, qz |
ppg | Photoplethysmography | ir, r, g, b |
emg_armband | EMG Armband (8 channels) | emg0-emg7 |
temperature | Temperature sensor | temperature |
status | Device status update | battery_% |
start_time | Acquisition start time | year, month, day, hour, minute, second |
memory | Memory event | - |
Packet Status
The status field indicates the state of the packet:
| Status | Description |
|---|---|
ok | Data received successfully |
recording | Device is recording to memory |
memory_download_completed | Memory download finished |
memory_erased | Device memory was erased |
invalid_datetime | Invalid timestamp |
invalid | Invalid/corrupted packet |
Channel names
Available channel names that can appear in the data object:
Biosignal channels
ecg- Electrocardiography (volts)emg(BioPoint) /emg0-emg7(SiFiBand) - Electromyography (volts)eda- Electrodermal activity (siemens)temperature- Skin temperature (°C)
IMU channels
ax,ay,az- Accelerometer (m/s²)qw,qx,qy,qz- Normalized quaternions
PPG channels
PPG does not have a standardized unit since it mesures the intensity of the light reflected in the skin. Typical values are in the ~1000-10000 absolute units (AU) range.
ir- Infrared LEDr- Red LEDg- Green LEDb- Blue LED
Status channels
temperature- Temperature (°C). Redundant with dedicatedtemperaturepackets.battery_%- Battery level (percentage)
Device types
The device field can be:
BioPoint- BioPoint deviceSiFiBand- SiFiBand deviceInvalid- Unknown/invalid device
Sampling rate
The sample_rate field reports the effective sampling rate in Hz of the sensor as measured by SiFi Bridge. SiFi Bridge continually measures this value as each packet is received. Normally, it converges toward and perhaps slightly oscillates around the configured rate due to the jitter in Bluetooth communications.
It is null for packet types that do not carry periodically sampled data, such as status, start_time, and memory. It is also null during a memory download, since packets are streamed as fast as possible, making the measured sampling rate meaningless.
Lost samples
The samples_lost field reports how many samples were lost since the last packet. This can happen if the BLE link is unstable, causing the effective bandwidth of the BLE link to be lower than what the device requires to deliver all data in real-time.
To preserve the 1-to-1 alignment between timestamps and data values, each lost sample is kept as a null entry in the affected data channel(s), with its corresponding timestamp still present. In the DataPacket example above, samples_lost is 1 and the first ecg value is null. Its associated timestamp is 17.826 s.
If the key is missing, it means no samples were lost.
Lost samples only affect streamed data. The on-device data is not lost!
For instance, if an acquisition is started with memory mode of both, and some samples are lost, you can still obtain the real data by downloading the data from the device's onboard memory afterward.
Example packets
ECG packet
{
"device": "SiFiBand",
"id": "SB_EFFD",
"name": "my_sb",
"mac": "CD:92:AD:DA:B7:71",
"packet_type": "ecg",
"status": "ok",
"received_at": 1782598065.104,
"sample_rate": 996.8285003209548,
"timestamps": [
8.184
],
"data": {
"ecg": [
0.0
]
}
}
EMG packet
This only applies to BioPoint.
{
"device": "BioPoint",
"id": "BP_7A1F",
"name": "my_bp",
"mac": "C2:EC:EF:30:0D:10",
"packet_type": "emg",
"status": "ok",
"received_at": 1783259483.825,
"sample_rate": 1998.5506970149786,
"timestamps": [
3.448,
3.499
],
"data": {
"emg": [
-0.000020798392917798913,
-0.000021455184273097826
]
}
}
8-channel EMG packet
This only applies to SiFiBand.
{
"device": "SiFiBand",
"id": "SB_EFFD",
"name": "my_sb",
"mac": "CD:92:AD:DA:B7:71",
"packet_type": "emg_armband",
"status": "ok",
"received_at": 1783476224.947,
"sample_rate": 1597.1503517986785,
"timestamps": [
1.541875,
],
"data": {
"emg0": [
6.56791355298913e-7,
],
"emg1": [
2.1893045176630434e-7,
],
"emg2": [
-8.757218070652174e-7,
],
"emg3": [
-4.378609035326087e-6,
],
"emg4": [
2.1893045176630434e-7,
],
"emg5": [
5.254330842391304e-6,
],
"emg6": [
1.970374065896739e-6,
],
"emg7": [
1.0946522588315218e-6,
]
}
}
EDA packet
{
"device": "BioPoint",
"id": "BP_7A1F",
"name": "my_bp",
"mac": "C2:EC:EF:30:0D:10",
"packet_type": "eda",
"status": "ok",
"received_at": 1730370226.150,
"timestamps": [1730370225.123, 1730370225.163, 1730370225.203],
"data": {
"eda": [1.6323e-6, 1.64873e-6 , 1.72176e-6]
},
"sample_rate": 50.0
}
IMU packet
{
"device": "SiFiBand",
"id": "SB_EFFD",
"name": "my_sb",
"mac": "CD:92:AD:DA:B7:71",
"packet_type": "imu",
"status": "ok",
"received_at": 1782598065.101,
"sample_rate": 198.82754760640623,
"timestamps": [
8.15,
8.16
],
"data": {
"ax": [
9.889045000076294,
9.698618143200875
],
"ay": [
-0.09820778757333756,
-0.13653277784585954
],
"az": [
-0.17845073595643043,
-0.14012574568390848
],
"qw": [
0.6869235096042648,
0.6868557952294714
],
"qx": [
0.03461920498869666,
0.034735847377314714
],
"qy": [
-0.7243495047894222,
-0.7243973636306342
],
"qz": [
0.047491025575671385,
0.0476549790856847
]
}
}
PPG packet
{
"device": "BioPoint",
"id": "BP_7A1F",
"name": "my_bp",
"mac": "C2:EC:EF:30:0D:10",
"packet_type": "ppg",
"status": "ok",
"received_at": 1730370225.150,
"timestamps": [1730370225.123, 1730370225.133, 1730370225.143],
"data": {
"ir": [2738.0, 2753.0, 2765.0],
"r": [3141.0, 3154.0, 3168.0],
"g": [3465.0, 3474.0, 3482.0],
"b": [2890.0, 2903.0, 2915.0]
},
"sample_rate": 100.0
}
Status packet
{
"device": "BioPoint",
"id": "BP_7A1F",
"name": "my_bp",
"mac": "C2:EC:EF:30:0D:10",
"packet_type": "status",
"status": "ok",
"received_at": 1782574161.811,
"timestamps": [
1782574161.811
],
"data": {
"temperature": [
27.607770919799805
],
"battery_%": [
92.0
],
"memory_used_kbytes": [
370808.490234375
]
}
}
Processing Data Packets
For Python, we recommend using sifi-bridge-py.