Skip to content

Add gravity, rotation vector, GPS #59

Draft
wants to merge 9 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,9 @@ This project is based on [Open Camera](https://opencamera.org.uk/) — a popul
-```{VIDEO_NAME}_gyro.csv```, data format: ```X-data, Y-data, Z-data, timestamp (ns)```
- ```{VIDEO_NAME}_accel.csv```, data format: ```X-data, Y-data, Z-data, timestamp (ns)```
- ```{VIDEO_NAME}_magnetic.csv```, data format: ```X-data, Y-data, Z-data, timestamp (ns)```
- ```{VIDEO_NAME}_location```, data format: ```latitude, lognitude, altitude, timestamp (ns)```
- ```{VIDEO_NAME}_rotation```, data format: ```azimuth, pitch, roll, timestamp (ns)```
- ```{VIDEO_NAME}_gravity```, data format: ```X-data, Y-data, Z-data, timestamp (ns)```
- ```{VIDEO_NAME}_timestamps.csv```, data format: ```timestamp (ns)```
- ```{VIDEO_NAME}_flash.csv```, data format: ```timestamp (ns)``` (timestamps of when the flash fired)

Expand Down
4 changes: 2 additions & 2 deletions api_client/async_imu_example.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,13 @@ def main():
remote = RemoteControl(HOST)

with ThreadPoolExecutor(max_workers=1) as executor:
future = executor.submit(remote.get_imu, 10000, True, False)
future = executor.submit(remote.get_imu, 10000, True, True, False, False, False)
# Do something else
print("doing other stuff...")
time.sleep(10)
print("done doing other stuff")
# Get result when needed
accel_data, gyro_data = future.result()
accel_data, gyro_data, _, _ = future.result()
# Process result somehow (here just file output)
print("Accelerometer data length: %d" % len(accel_data))
with open("accel.csv", "w+") as accel:
Expand Down
17 changes: 13 additions & 4 deletions api_client/src/RemoteControl.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,9 @@
BUFFER_SIZE = 4096
PROPS_PATH = '../app/src/main/assets/server_config.properties'
SUPPORTED_SERVER_VERSIONS = [
'v.0.1.1'
'v.0.1.2'
]
NUM_SENSORS = 3
NUM_SENSORS = 5

class RemoteControl:
"""
Expand Down Expand Up @@ -40,18 +40,23 @@ def get_imu(self, duration_ms, want_accel, want_gyro, want_magnetic):
:param want_accel: (boolean) request accelerometer recording
:param want_gyro: (boolean) request gyroscope recording
:param want_gyro: (boolean) request magnetometer recording
:return: Tuple (accel_data, gyro_data, magnetic_data) - csv data strings
:param want_gravity: (boolean) request gravity recording
:param want_rotation: (boolean) request rotation recording
:return: Tuple (accel_data, gyro_data, magnetic_data, gravity_data, rotation_data) - csv data strings
If one of the sensors wasn't requested, the corresponding data is None
"""
accel = int(want_accel)
gyro = int(want_gyro)
magnetic = int(want_magnetic)
status, socket_file = self._send_and_get_response_status(
'imu?duration=%d&accel=%d&gyro=%d&magnetic=%d\n' % (duration_ms, accel, gyro, magnetic)
'imu?duration=%d&accel=%d&gyro=%d&magnetic=%d&gravity=%d&rotation=%d\n'
% (duration_ms, accel, gyro, magnetic, gravity, rotation)
)
accel_data = None
gyro_data = None
magnetic_data = None
gravity_data = None
rotation_data = None

for i in range(NUM_SENSORS):
# read filename or end marker
Expand All @@ -74,6 +79,10 @@ def get_imu(self, duration_ms, want_accel, want_gyro, want_magnetic):
gyro_data = data
elif msg.endswith("magnetic.csv"):
magnetic_data = data
elif msg.endswith("gravity.csv"):
gravity_data = data
elif msg.endswith("rotation.csv"):
rotation_data = data

socket_file.close()
return accel_data, gyro_data, magnetic_data
Expand Down
2 changes: 1 addition & 1 deletion app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />

<uses-permission android:name="android.permission.ACCESS_BACKGROUND_LOCATION" />
<uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="android.permission.RECORD_AUDIO" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
Expand Down
2 changes: 1 addition & 1 deletion app/src/main/assets/server_config.properties
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
RPC_PORT=6969
SERVER_VERSION=v.0.1.1
SERVER_VERSION=v.0.1.2
VIDEO_START_REQUEST=video_start
VIDEO_STOP_REQUEST=video_stop
GET_VIDEO_REQUEST=get_video
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,14 @@ private boolean getMagneticPref() {
return mSharedPreferences.getBoolean(PreferenceKeys.MagnetometerPrefKey, true);
}

private boolean getRotationPref() {
return mSharedPreferences.getBoolean(PreferenceKeys.RotationPreferenceKey, true);
}

private boolean getGravityPref() {
return mSharedPreferences.getBoolean(PreferenceKeys.GravityPreferenceKey, true);
}

/**
* Retrieves gyroscope and accelerometer sample rate preference and converts it to number
*/
Expand All @@ -109,7 +117,11 @@ public boolean getSaveFramesPref() {
return mSharedPreferences.getBoolean(PreferenceKeys.saveFramesPreferenceKey, false);
}

public void startImu(boolean wantAccel, boolean wantGyro, boolean wantMagnetic, Date currentDate) {
public void startImu(
boolean wantAccel, boolean wantGyro,
boolean wantMagnetic, boolean wantGravity,
boolean wantRotation, Date currentDate
) {
if (wantAccel) {
int accelSampleRate = getSensorSampleRatePref(PreferenceKeys.AccelSampleRatePreferenceKey);
if (!mRawSensorInfo.enableSensor(Sensor.TYPE_ACCELEROMETER, accelSampleRate)) {
Expand All @@ -129,11 +141,34 @@ public void startImu(boolean wantAccel, boolean wantGyro, boolean wantMagnetic,
}
}

if (!mRawSensorInfo.enableSensor(RawSensorInfo.TYPE_GPS, 0)) {
mMainActivity.getPreview().showToast(null, "GPS unavailable");
}


//mRawSensorInfo.startRecording(mMainActivity, mLastVideoDate, get Pref(), getAccelPref())
if (wantRotation) {
int rotationSampleRate = getSensorSampleRatePref(PreferenceKeys.RotationSampleRatePreferenceKey);
if (!mRawSensorInfo.enableSensor(Sensor.TYPE_ROTATION_VECTOR, rotationSampleRate)) {
mMainActivity.getPreview().showToast(null, "Rotation vector unavailable");
}
}

if (wantGravity) {
int gravitySampleRate = getSensorSampleRatePref(PreferenceKeys.GravitySampleRatePreferenceKey);
if (!mRawSensorInfo.enableSensor(Sensor.TYPE_GRAVITY, gravitySampleRate)) {
mMainActivity.getPreview().showToast(null, "Gravity unavailable");
}
}

//mRawSensorInfo.startRecording(mMainActivity, mLastVideoDate, get Pref(), getAccelPref())
Map<Integer, Boolean> wantSensorRecordingMap = new HashMap<>();
wantSensorRecordingMap.put(Sensor.TYPE_ACCELEROMETER, getAccelPref());
wantSensorRecordingMap.put(Sensor.TYPE_GYROSCOPE, getGyroPref());
wantSensorRecordingMap.put(Sensor.TYPE_MAGNETIC_FIELD, getMagneticPref());
wantSensorRecordingMap.put(Sensor.TYPE_GRAVITY, getGravityPref());
wantSensorRecordingMap.put(Sensor.TYPE_ROTATION_VECTOR, getRotationPref());
wantSensorRecordingMap.put(RawSensorInfo.TYPE_GPS, true);
mRawSensorInfo.startRecording(mMainActivity, currentDate, wantSensorRecordingMap);
}

Expand All @@ -146,7 +181,7 @@ public void startingVideo() {
// Extracting sample rates from shared preferences
try {
mMainActivity.getPreview().showToast("Starting video with IMU recording...", true);
startImu(getAccelPref(), getGyroPref(), getMagneticPref(), mLastVideoDate);
startImu(getAccelPref(), getGyroPref(), getMagneticPref(), getGravityPref(), getRotationPref(), mLastVideoDate);
// TODO: add message to strings.xml
} catch (NumberFormatException e) {
if (MyDebug.LOG) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -313,6 +313,10 @@ public static String getVideoQualityPreferenceKey(int cameraId, boolean high_spe

public static final String GyroPreferenceKey = "preference_gyro";

public static final String GravityPreferenceKey = "preference_gravity";

public static final String RotationPreferenceKey = "preference_rotation";

public static final String MagnetometerPrefKey = "preference_magnetometer";

public static final String SupportsMagnetometerKey = "preference_supports_magnetometer";
Expand All @@ -329,6 +333,10 @@ public static String getVideoQualityPreferenceKey(int cameraId, boolean high_spe

public static final String MagneticSampleRatePreferenceKey = "preference_magnetic_sample_rate";

public static final String GravitySampleRatePreferenceKey = "preference_gravity_sample_rate";

public static final String RotationSampleRatePreferenceKey = "preference_rotation_sample_rate";

public static final String FlashStrobeFreqPreferenceKey = "preference_strobe_freq";

public static String getVideoFPSPreferenceKey(int cameraId) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,13 @@
import android.hardware.SensorEvent;
import android.hardware.SensorEventListener;
import android.hardware.SensorManager;
import android.location.Location;
import android.location.LocationListener;
import android.location.LocationManager;
import android.location.LocationProvider;
import android.net.Uri;
import android.os.ParcelFileDescriptor;
import android.os.Bundle;
import android.util.Log;

import net.sourceforge.opencamera.MainActivity;
Expand All @@ -32,28 +37,42 @@
* Assumes all the used sensor types are motion or position sensors
* and output [x, y, z] values -- the class should be updated if that changes
*/
public class RawSensorInfo implements SensorEventListener {
public class RawSensorInfo implements SensorEventListener, LocationListener {
private static final String TAG = "RawSensorInfo";
private static final String CSV_SEPARATOR = ",";
public static final int TYPE_GPS = 0xabcd;
private static final List<Integer> SENSOR_TYPES = Collections.unmodifiableList(
Arrays.asList(Sensor.TYPE_ACCELEROMETER, Sensor.TYPE_GYROSCOPE, Sensor.TYPE_MAGNETIC_FIELD)
Arrays.asList(
TYPE_GPS, Sensor.TYPE_ACCELEROMETER, Sensor.TYPE_GYROSCOPE,
Sensor.TYPE_MAGNETIC_FIELD, Sensor.TYPE_GRAVITY, Sensor.TYPE_ROTATION_VECTOR)
);

private final float[] accelerometerReading = new float[3];
private final float[] magnetometerReading = new float[3];

private final float[] rotationMatrix = new float[9];
private final float[] orientationAngles = new float[3];

private static final Map<Integer, String> SENSOR_TYPE_NAMES;
static {
SENSOR_TYPE_NAMES = new HashMap<>();
SENSOR_TYPE_NAMES.put(Sensor.TYPE_ACCELEROMETER, "accel");
SENSOR_TYPE_NAMES.put(Sensor.TYPE_GYROSCOPE, "gyro");
SENSOR_TYPE_NAMES.put(Sensor.TYPE_MAGNETIC_FIELD, "magnetic");
SENSOR_TYPE_NAMES.put(Sensor.TYPE_GRAVITY, "gravity");
SENSOR_TYPE_NAMES.put(Sensor.TYPE_ROTATION_VECTOR, "rotation");
SENSOR_TYPE_NAMES.put(TYPE_GPS, "location");
}

final private SensorManager mSensorManager;
private final LocationManager mLocationManager;
/* final private Sensor mSensorGyro;
final private Sensor mSensorAccel;
final private Sensor mSensorMagnetic;
private PrintWriter mGyroBufferedWriter;
private PrintWriter mAccelBufferedWriter;*/
private boolean mIsRecording;
private final Map<Integer, Sensor> mUsedSensorMap;
private final Map<Integer, Object> mUsedSensorMap;
private final Map<Integer, PrintWriter> mSensorWriterMap;
private final Map<Integer, File> mLastSensorFilesMap;

Expand All @@ -67,12 +86,17 @@ public boolean isSensorAvailable(int sensorType) {

public RawSensorInfo(MainActivity context) {
mSensorManager = (SensorManager) context.getSystemService(Context.SENSOR_SERVICE);
mLocationManager = (LocationManager)context.getSystemService(Context.LOCATION_SERVICE);
mUsedSensorMap = new HashMap<>();
mSensorWriterMap = new HashMap<>();
mLastSensorFilesMap = new HashMap<>();

for (Integer sensorType : SENSOR_TYPES) {
mUsedSensorMap.put(sensorType, mSensorManager.getDefaultSensor(sensorType));
if (sensorType != TYPE_GPS) {
mUsedSensorMap.put(sensorType, mSensorManager.getDefaultSensor(sensorType));
} else {
mUsedSensorMap.put(sensorType, new Object());
}
}
/* mSensorGyro = mSensorManager.getDefaultSensor(Sensor.TYPE_GYROSCOPE);
mSensorAccel = mSensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);
Expand All @@ -91,30 +115,93 @@ public RawSensorInfo(MainActivity context) {
}

public int getSensorMinDelay(int sensorType) {
Sensor sensor = mUsedSensorMap.get(sensorType);
Object sensor = mUsedSensorMap.get(sensorType);
if (sensor != null) {
return sensor.getMinDelay();
} else {
// Unsupported sensorType
if (MyDebug.LOG) {
Log.d(TAG, "Unsupported sensor type was provided");
if (sensor instanceof Sensor) {
return ((Sensor)sensor).getMinDelay();
}
return 0;
}
// Unsupported sensorType
if (MyDebug.LOG) {
Log.d(TAG, "Unsupported sensor type was provided");
}
return 0;
}

@Override
public void onProviderEnabled(String provider) {
}

@Override
public void onProviderDisabled(String provider) {
}

@Override
public void onStatusChanged(String provider, int status, Bundle extras) {
}

private class MyEvent {
public int accuracy;
public int type;
// public Sensor sensor;
public long timestamp;
public float[] values;
}

@Override
public void onLocationChanged(Location location) {
if( location != null && ( location.getLatitude() != 0.0d || location.getLongitude() != 0.0d ) ) {
MyEvent event = new MyEvent();
event.type = TYPE_GPS;
event.timestamp = location.getElapsedRealtimeNanos();
event.values = new float[]{(float) location.getLatitude(), (float) location.getLongitude(), (float) location.getAltitude()};
_onSensorChanged(event);
}
}

@Override
public void onSensorChanged(SensorEvent event) {
{
MyEvent e = new MyEvent();
e.accuracy = event.accuracy;
e.type = event.sensor.getType();
e.timestamp = event.timestamp;
e.values = event.values;
_onSensorChanged(e);
}
if (event.sensor.getType() == Sensor.TYPE_ACCELEROMETER) {
SensorManager.getRotationMatrix(rotationMatrix, null, accelerometerReading, magnetometerReading);
SensorManager.getOrientation(rotationMatrix, orientationAngles);

MyEvent e = new MyEvent();
e.accuracy = event.accuracy;
e.type = Sensor.TYPE_ROTATION_VECTOR;
e.timestamp = event.timestamp;
e.values = orientationAngles;
_onSensorChanged(e);

}
}

private void _onSensorChanged(MyEvent event) {
if (mIsRecording) {
if (event.type == Sensor.TYPE_ACCELEROMETER) {
System.arraycopy(event.values, 0, accelerometerReading,
0, accelerometerReading.length);
} else if (event.type == Sensor.TYPE_MAGNETIC_FIELD) {
System.arraycopy(event.values, 0, magnetometerReading,
0, magnetometerReading.length);
}

StringBuilder sensorData = new StringBuilder();
for (int j = 0; j < 3; j++) {
sensorData.append(event.values[j]).append(CSV_SEPARATOR);
}
sensorData.append(event.timestamp).append("\n");

Sensor sensor = mUsedSensorMap.get(event.sensor.getType());
Object sensor = mUsedSensorMap.get(event.type);
if (sensor != null) {
PrintWriter sensorWriter = mSensorWriterMap.get(event.sensor.getType());
PrintWriter sensorWriter = mSensorWriterMap.get(event.type);
if (sensorWriter != null) {
sensorWriter.write(sensorData.toString());
} else {
Expand Down Expand Up @@ -288,9 +375,17 @@ public boolean enableSensor(int sensorType, int sampleRate) {
Log.d(TAG, "enableSensor");
}

Sensor sensor = mUsedSensorMap.get(sensorType);
Object sensor = mUsedSensorMap.get(sensorType);
if (sensor != null) {
mSensorManager.registerListener(this, sensor, sampleRate);
if (sensorType != TYPE_GPS) {
mSensorManager.registerListener(this, (Sensor)sensor, sampleRate);
} else {
try {
mLocationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER, 0, 0, this);
} catch (SecurityException e) {
return false;
}
}
return true;
} else {
return false;
Expand All @@ -313,5 +408,6 @@ public void disableSensors() {
Log.d(TAG, "disableSensors");
}
mSensorManager.unregisterListener(this);
mLocationManager.removeUpdates(this);
}
}
Loading