Movesense API

Package

Client Module

Movesense BLE client for managing connections, configuring streams, and handling incoming data frames.
  • This client uses the NexusBLESdk to interact with Movesense sensors over BLE.

  • It provides methods for discovering sensors, connecting to them, configuring the data streams, starting and stopping the streams, and disconnecting.

  • It also handles incoming stream frames, parsing the payloads according to the Movesense profile, and can dump raw frames or write parsed rows to a specified writer.

  • The client is designed to work with ECG, heart rate, and temperature data from Movesense sensors, and can be extended to support additional stream types if needed.

class Movesense.client.MovesenseClient(gateway)[source]

Bases: object

Parameters:

gateway (GatewayClient)

discover(sensor_count, scan_timeout_ms)[source]
Discovers Movesense sensors by scanning for BLE devices with the specified name prefix.
  • The method uses the gateway to perform a BLE scan for the specified duration, filtering devices based on the Movesense name prefix.

  • It then selects the addresses of the discovered devices, up to the specified sensor count, using the select_addresses utility function.

  • The method returns a list of BLE addresses for the discovered Movesense sensors, which can be used for connecting and streaming data from those sensors.

  • If no sensors are found or the scan times out, it returns an empty list. If the number of discovered sensors exceeds the specified sensor count, it selects the appropriate number of addresses based on the selection criteria defined in the select_addresses function.

Parameters:
  • sensor_count (int)

  • scan_timeout_ms (int)

Return type:

list[str]

connect(addresses, timeout_s)[source]
Connects to the specified Movesense sensors using their BLE addresses.
  • The method uses the gateway to establish connections to the sensors with the given addresses, applying a timeout for the connection process.

  • It returns a list of SensorConnection objects representing the active connections to the sensors. Each SensorConnection contains information about the connected sensor, such as its address and sensor ID.

  • If any connection attempt fails or times out, it raises an exception or returns an empty list depending on the implementation of the gateway’s connect method. The client is designed to manage multiple connections simultaneously, allowing for coordinated streaming and data handling from multiple sensors.

Parameters:
  • addresses (list[str])

  • timeout_s (float)

Return type:

list[SensorConnection]

configure(*, sampling_rate_hz, subscribe_timeout_s, write_timeout_s, without_response)[source]
Configures the connected Movesense sensors for streaming data.
  • The method subscribes to the notification characteristic for each connected sensor, using a retry mechanism with the specified timeout. The effective subscribe timeout is calculated based on the number of connections to ensure sufficient time for all subscriptions to be established.

  • It then writes the subscribe commands to the sensors to start the ECG, heart rate, and temperature streams, using the specified write timeout and response settings.

  • The sampling rate for the ECG stream is validated against the supported rates, and if valid, it is set for use in parsing the incoming data frames. If the sampling rate is not supported, it raises a ValueError.

  • The method ensures that the sensors are properly configured to start streaming data, and it can be called after connecting to the sensors to prepare them for data collection.

Parameters:
  • sampling_rate_hz (int)

  • subscribe_timeout_s (float)

  • write_timeout_s (float)

  • without_response (bool)

start_streams(*, write_timeout_s, without_response)[source]
Parameters:
  • write_timeout_s (float)

  • without_response (bool)

Return type:

dict[str, float | None]

stop_streams(*, write_timeout_s, without_response)[source]
Parameters:
  • write_timeout_s (float)

  • without_response (bool)

disconnect_all(timeout_s)[source]
Parameters:

timeout_s (float)

set_raw_dump_file(raw_dump_file)[source]
set_parsed_row_writer(parsed_row_writer)[source]
set_ecg_path_suffix(suffix)[source]
Parameters:

suffix (str)

handle_stream_frame(frame, monitor, wall_time)[source]
Handles an incoming stream frame from a Movesense sensor.
  • The method first checks the payload of the frame to ensure it has the expected structure and contains at least the packet type and stream ID.

  • It then retrieves the address corresponding to the sensor ID in the frame using the _address_for_sensor_id helper method.

  • The raw frame is dumped to a file if a raw dump file is configured, including relevant metadata such as wall time, sensor ID, gateway timestamp, and address.

  • If measurement recording is active in the monitor, it writes parsed rows extracted from the payload to the parsed row writer if configured.

  • The method checks the packet type and stream ID to determine how to handle the frame:
    • For ECG data packets, it calls the monitor’s handle_ecg_frame method to process the ECG data.

    • For heart rate and temperature data packets, it extracts the values and records samples in the monitor using record_hr_sample and record_temp_sample methods respectively.

  • If the packet type is not a data packet or if the stream ID is not recognized, it simply returns without further processing. This allows for handling only relevant frames while ignoring others that may not be of interest.

  • The method is designed to be called for each incoming stream frame, allowing for real-time processing of data from Movesense sensors as it arrives.

  • Returns None after processing the frame.

Parameters:

Profile Module

Movesense BLE profile parsing and utilities.

Movesense.profile.parse_ecg_packet_timestamp_us(payload)[source]
Parses the ECG packet timestamp in microseconds from the given payload.
  • Movesense ECG packets have a 4-byte timestamp in milliseconds at offset 2.

  • This function converts it to microseconds by multiplying by 1000.

  • If the payload is too short or not a data packet, it raises a ValueError.

  • Returns the timestamp in microseconds as an integer.

Parameters:

payload (bytes)

Return type:

int

Movesense.profile.parse_ecg_packet_sample_count(payload)[source]
Parses the number of ECG samples in the given payload.
  • Movesense ECG packets have a 4-byte timestamp in milliseconds at offset 2, followed by ECG samples starting at offset 6.

  • The number of samples can be determined by the total payload length minus the 6 bytes of header, divided by the sample size (4 bytes for 32-bit samples, or 2 bytes for 16-bit samples).

  • If the payload is too short or not a data packet, it returns 0.

  • If the payload length indicates a full packet of 16 samples (64 bytes of data) or 32 samples (32 bytes of data), it returns the fixed sample count of 16.

  • Otherwise, it calculates the sample count based on the remaining payload length after the header. If the remaining length is not a multiple of the sample size, it returns 0 to indicate an invalid packet.

  • Returns the number of ECG samples as an integer.

Parameters:

payload (bytes)

Return type:

int

Movesense.profile.parse_ecg_sample_values_mv(payload)[source]
Parses the ECG sample values in millivolts from the given payload.
  • Movesense ECG packets have a 4-byte timestamp in milliseconds at offset 2, followed by ECG samples starting at offset 6.

  • Each ECG sample is a 32-bit signed integer representing the raw ADC value, which can be converted to millivolts by multiplying by the scale factor (0.38147 mV per LSB).

  • If the payload is too short or not a data packet, it returns an empty list.

  • If the payload length indicates a full packet of 16 samples (64 bytes of data) or 32 samples (32 bytes of data), it parses the fixed number of samples accordingly.

  • Otherwise, it parses as many samples as indicated by the remaining payload length after the header, ensuring that it does not read beyond the payload length. If the remaining length is not a multiple of the sample size, it returns an empty list to indicate an invalid packet.

  • Returns a list of ECG sample values in millivolts as floats.

Parameters:

payload (bytes)

Return type:

list[float]

Movesense.profile.parse_hr_value(payload)[source]
Parses the heart rate value in beats per minute from the given payload.
  • Movesense heart rate packets have a 4-byte timestamp in milliseconds at offset 2, followed by a 4-byte float value representing the heart rate in bpm.

  • If the payload is too short or not a data packet, it returns None.

  • If the payload length is sufficient to contain the heart rate value, it parses the float value at the expected offset. If the payload length is not sufficient, it returns None to indicate an invalid packet.

  • Returns the heart rate value in beats per minute as a float, or None if it cannot be parsed.

Parameters:

payload (bytes)

Return type:

float | None

Movesense.profile.parse_temp_value(payload)[source]
Parses the temperature value in degrees Celsius from the given payload.
  • Movesense temperature packets have a 4-byte timestamp in milliseconds at offset 2, followed by a 4-byte float value representing the temperature in Celsius.

  • If the payload is too short or not a data packet, it returns None.

  • If the payload length is sufficient to contain the temperature value, it parses the float value at the expected offset. If the payload length is not sufficient, it returns None to indicate an invalid packet.

  • Returns the temperature value in degrees Celsius as a float, or None if it cannot be parsed.

Parameters:

payload (bytes)

Return type:

float | None

Movesense.profile.parse_ecg_sample_timestamps_ms(payload, sampling_rate_hz)[source]
Parses the ECG sample timestamps in milliseconds from the given payload and sampling rate.
  • Movesense ECG packets have a 4-byte timestamp in milliseconds at offset 2, followed by ECG samples starting at offset 6.

  • The sample timestamps can be calculated based on the packet timestamp and the sampling rate. The first sample timestamp corresponds to the packet timestamp, and subsequent samples are spaced by the inverse of the sampling rate (e.g., 5 ms for 200 Hz).

  • If the payload is too short or not a data packet, it returns an empty list.

  • If the payload length indicates a full packet of 16 samples (64 bytes of data) or 32 samples (32 bytes of data), it calculates the timestamps for the fixed number of samples accordingly.

  • Otherwise, it calculates the timestamps for as many samples as indicated by the remaining payload length after the header, ensuring that it does not read beyond the payload length. If the remaining length is not a multiple of the sample size, it returns an empty list to indicate an invalid packet.

  • Returns a list of ECG sample timestamps in milliseconds as integers.

Parameters:
  • payload (bytes)

  • sampling_rate_hz (int)

Return type:

list[int]

Movesense.profile.summarize_payload(payload, sampling_rate_hz)[source]
Summarizes the given payload by extracting key information based on the packet type and stream ID.
  • For data packets, it extracts the packet timestamp, sample count, and sample values for ECG streams, and the value for heart rate and temperature streams.

  • It returns a dictionary containing the packet type, stream ID, payload length, and any parsed values or timestamps. This summary can be used for logging or debugging purposes to understand the contents of the payload without needing to parse all details.

  • If the payload is not a data packet or is too short, it returns a summary with basic information and empty values.

  • The summary includes:
    • “packet_type”: The type of the packet (e.g., data or response).

    • “stream_id”: The stream ID indicating the type of data (e.g., ECG, heart rate, temperature).

    • “payload_hex”: The hexadecimal representation of the raw payload for reference.

    • “payload_len”: The length of the payload in bytes.

    • For ECG packets: “packet_timestamp_ms”, “packet_timestamp_us”, “ecg_sample_count”, “ecg_values_mv”, “sample_timestamps_ms”, “first_sample_timestamp_ms”, “last_sample_timestamp_ms”.

    • For heart rate packets: “hr_value”.

    • For temperature packets: “temp_c”.

  • Returns a dictionary summarizing the payload contents.

Parameters:
  • payload (bytes)

  • sampling_rate_hz (int)

Return type:

dict

Movesense.profile.iter_parsed_rows(payload, *, sampling_rate_hz, address, sensor_id, gateway_timestamp_us)[source]
Parses the given payload and yields rows of data based on the packet type and stream ID.
  • For ECG data packets, it extracts the sample timestamps and values, and yields a row for each sample with detailed information including the sensor ID, stream type, timestamps, sample index, sampling rate, value in millivolts, and unit.

  • For heart rate and temperature data packets, it extracts the value and yields a single row with the corresponding information.

  • If the payload is not a data packet or is too short, it returns an empty list.

  • The rows are returned as a list of dictionaries, where each dictionary represents a single data point with standardized keys for address, sensor_id, stream type, timestamps, sample index, sampling rate, value, and unit. This format can be easily converted to a DataFrame or other structured format for analysis.

  • Returns a list of parsed rows extracted from the payload.

Parameters:
  • payload (bytes)

  • sampling_rate_hz (int)

  • address (str | None)

  • sensor_id (int)

  • gateway_timestamp_us (int)

Return type:

list[dict]

Movesense.profile.build_subscribe_command(stream_id, path)[source]
Parameters:
  • stream_id (int)

  • path (str)

Return type:

str

Movesense.profile.build_stop_command(stream_id)[source]
Parameters:

stream_id (int)

Return type:

str

Movesense.profile.is_movesense_match(name)[source]
Parameters:

name (str)

Return type:

bool

Movesense.profile.select_addresses(matches, count)[source]
Parameters:

count (int)

Return type:

list[str]