ZuzApp is an Android application designed for scientific experiments and movement tracking. It captures real-time gyroscope (angular velocity) and rotation vector (orientation) data, filters sensor noise through calibration, and logs comprehensive movement data to both a local CSV file and a remote Supabase backend.
- Dual Sensor Monitoring:
- Gyroscope: Tracks angular velocity (rate of rotation) with direction preservation
- Rotation Vector: Captures absolute orientation (pitch, roll, yaw angles)
- Intelligent Calibration:
- Calculates baseline gyroscope noise when the device is stationary upon startup.
- Establishes yaw baseline for relative rotation tracking.
- Applies a noise threshold (0.5 deg/s) to ensure only significant movements are recorded.
- Dual Logging System:
- Local: Saves data to CSV files in the format
Subject__Session__Timestamp.csvstored in internal storage. - Cloud: Syncs session metadata and movement records to Supabase.
- Local: Saves data to CSV files in the format
- Optimized Performance:
- Uploads movement records in batches (size 20) to reduce network overhead.
- Keeps the screen active during recording to prevent sensor dozing.
- Session Management: Supports manual Session IDs or auto-generates UUIDs.
- Android Studio.
- A Supabase project.
The SupabaseClient.java relies on a Config class to fetch credentials. This file is required for the app to build.
Create a new file Config.java in the package com.haifa.zuzapp:
package com.haifa.zuzapp;
public class Config {
// Replace with your actual Supabase Project URL and Anon Key
private static final String SUPABASE_URL = "https://your-project-id.supabase.co";
private static final String SUPABASE_ANON_KEY = "your-public-anon-key";
public static String getSupabaseUrl() {
return SUPABASE_URL;
}
public static String getSupabaseAnonKey() {
return SUPABASE_ANON_KEY;
}
}You must create two tables in your Supabase project to match the JSON objects constructed in SupabaseClient.java. Run the following SQL in your Supabase SQL Editor:
-- Table: sessions
create table public.sessions (
session_id text not null,
experimenter_code text not null,
start_time text,
start_time_millis bigint,
end_time text,
end_time_millis bigint,
duration_ms bigint,
status text, -- 'started' or 'completed'
device_model text,
android_version text,
file_path text,
primary key (session_id, experimenter_code)
);
-- Table: movement_records
create table public.movement_records (
id bigint generated by default as identity primary key,
session_id text not null,
experimenter_code text not null,
timestamp text, -- Stores time as HH:mm:ss.SSS
elapsed_time_ms bigint,
magnitude float,
raw_delta float,
pitch float,
roll float,
calibrated_yaw float,
yaw float -- raw yaw
);
- Calibration (Auto-start):
- Upon opening the app, it attempts to calibrate both the gyroscope and rotation vector sensor.
- Keep the device perfectly still until the status text turns green and shows the calibrated baseline values.
- Note: You cannot start a session without calibration.
- Setup Session:
- Experimenter Code: Enter the subject name or researcher code (Required).
- Session ID: Leave blank to auto-generate a generic UUID, or type a specific ID.
- Recording:
- Press START SESSION.
- Perform the movement tasks. The screen shows the current Gyro Delta (Degrees/second).
- Data is saved locally and uploaded to the cloud in batches.
- Stopping:
- Press STOP SESSION.
- The app finalizes the CSV file and updates the session end time/duration in the database.
Files are stored in the app's private files directory. Format:
SessionID,ExperimenterCode,Timestamp,ElapsedTimeMs,Magnitude,RawDelta,Pitch,Roll,CalibratedYaw,RawYaw
uuid-1234,SUBJ_01,14:30:01.050,50,12.45,12.60,5.23,-2.15,0.00,45.32
...
| Column | Type | Description |
|---|---|---|
| SessionID | String | Unique session identifier |
| ExperimenterCode | String | Subject/experimenter identifier |
| Timestamp | Time | HH:mm:ss.SSS format |
| ElapsedTimeMs | Long | Milliseconds since session start |
| Magnitude | Float | Calibrated gyroscope magnitude (signed, deg/s) |
| RawDelta | Float | Raw gyroscope reading (deg/s) |
| Pitch | Float | Forward/backward tilt (degrees) |
| Roll | Float | Left/right tilt (degrees) |
| CalibratedYaw | Float | Rotation from calibrated position (degrees) |
| RawYaw | Float | Absolute yaw angle (degrees) |
- Read Raw Value: Z-axis gyroscope reading (radians/sec)
- Convert to Degrees:
rawDelta = toDegrees(z_axis_value) - Subtract Baseline:
magnitude = max(0, |rawDelta| - baselineNoise) - Restore Sign:
delta = copySign(magnitude, rawDelta)(preserves rotation direction) - Apply Threshold: If
|delta| < 0.5 deg/s, setdelta = 0
- Get Rotation Matrix: Convert rotation vector to 3x3 rotation matrix
- Extract Orientation: Calculate pitch, roll, yaw from rotation matrix
- Convert to Degrees: All angles converted from radians to degrees
- Calibrate Yaw:
calibratedYaw = currentYaw - baselineYaw
- MainActivity: Handles UI,
SensorEventListenerfor both gyroscope and rotation vector, and calibration logic. - MovementLogger: Manages the local
FileWriterand buffers data for the network. - SupabaseClient: Handles REST API calls (POST/PATCH) using
HttpURLConnectionand a backgroundExecutorService.
See MOVEMENT_ALGORITHM_SUMMARY.md for a comprehensive technical overview of the movement tracking algorithm, including detailed sensor explanations, calibration methodology, and use cases.