diff --git a/README.md b/README.md
index 638f3d7b1..7dc42d788 100644
--- a/README.md
+++ b/README.md
@@ -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)
diff --git a/api_client/async_imu_example.py b/api_client/async_imu_example.py
index f85e40061..1ed2607f4 100644
--- a/api_client/async_imu_example.py
+++ b/api_client/async_imu_example.py
@@ -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:
diff --git a/api_client/src/RemoteControl.py b/api_client/src/RemoteControl.py
index b57bb940c..ab1ac5c0b 100644
--- a/api_client/src/RemoteControl.py
+++ b/api_client/src/RemoteControl.py
@@ -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:
"""
@@ -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
@@ -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
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index 1c5451007..2eccc300e 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -16,7 +16,7 @@
-
+
diff --git a/app/src/main/assets/server_config.properties b/app/src/main/assets/server_config.properties
index b502bb34d..02a941da9 100644
--- a/app/src/main/assets/server_config.properties
+++ b/app/src/main/assets/server_config.properties
@@ -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
diff --git a/app/src/main/java/net/sourceforge/opencamera/ExtendedAppInterface.java b/app/src/main/java/net/sourceforge/opencamera/ExtendedAppInterface.java
index 931217de6..bb93a2f8b 100644
--- a/app/src/main/java/net/sourceforge/opencamera/ExtendedAppInterface.java
+++ b/app/src/main/java/net/sourceforge/opencamera/ExtendedAppInterface.java
@@ -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
*/
@@ -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)) {
@@ -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 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);
}
@@ -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) {
diff --git a/app/src/main/java/net/sourceforge/opencamera/PreferenceKeys.java b/app/src/main/java/net/sourceforge/opencamera/PreferenceKeys.java
index 4c91b33ba..1b1a3a82e 100644
--- a/app/src/main/java/net/sourceforge/opencamera/PreferenceKeys.java
+++ b/app/src/main/java/net/sourceforge/opencamera/PreferenceKeys.java
@@ -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";
@@ -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) {
diff --git a/app/src/main/java/net/sourceforge/opencamera/sensorlogging/RawSensorInfo.java b/app/src/main/java/net/sourceforge/opencamera/sensorlogging/RawSensorInfo.java
index 9b12f3c85..c21e49e66 100644
--- a/app/src/main/java/net/sourceforge/opencamera/sensorlogging/RawSensorInfo.java
+++ b/app/src/main/java/net/sourceforge/opencamera/sensorlogging/RawSensorInfo.java
@@ -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;
@@ -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 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 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 mUsedSensorMap;
+ private final Map mUsedSensorMap;
private final Map mSensorWriterMap;
private final Map mLastSensorFilesMap;
@@ -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);
@@ -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 {
@@ -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;
@@ -313,5 +408,6 @@ public void disableSensors() {
Log.d(TAG, "disableSensors");
}
mSensorManager.unregisterListener(this);
+ mLocationManager.removeUpdates(this);
}
}
diff --git a/app/src/main/java/net/sourceforge/opencamera/sensorremote/RemoteRpcRequestHandler.java b/app/src/main/java/net/sourceforge/opencamera/sensorremote/RemoteRpcRequestHandler.java
index 97c5a12d7..0072f8e20 100644
--- a/app/src/main/java/net/sourceforge/opencamera/sensorremote/RemoteRpcRequestHandler.java
+++ b/app/src/main/java/net/sourceforge/opencamera/sensorremote/RemoteRpcRequestHandler.java
@@ -63,7 +63,9 @@ RemoteRpcResponse handleInvalidRequest() {
return mResponseBuilder.error("Invalid request", mContext);
}
- RemoteRpcResponse handleImuRequest(long durationMillis, boolean wantAccel, boolean wantGyro, boolean wantMagnetic) {
+ RemoteRpcResponse handleImuRequest(long durationMillis, boolean wantAccel,
+ boolean wantGyro, boolean wantMagnetic,
+ boolean wantGravity, boolean wantRotation) {
if (mRawSensorInfo != null && !mRawSensorInfo.isRecording()) {
// TODO: custom rates?
Callable recStartCallable = () -> {
@@ -75,7 +77,7 @@ RemoteRpcResponse handleImuRequest(long durationMillis, boolean wantAccel, boole
// prefEditor.apply();
Date currentDate = new Date();
- mContext.getApplicationInterface().startImu(wantAccel, wantGyro, wantMagnetic, currentDate);
+ mContext.getApplicationInterface().startImu(wantAccel, wantGyro, wantMagnetic, wantGravity, wantRotation, currentDate);
return null;
};
diff --git a/app/src/main/java/net/sourceforge/opencamera/sensorremote/RemoteRpcServer.java b/app/src/main/java/net/sourceforge/opencamera/sensorremote/RemoteRpcServer.java
index 456ae0c7b..aa7a6b483 100644
--- a/app/src/main/java/net/sourceforge/opencamera/sensorremote/RemoteRpcServer.java
+++ b/app/src/main/java/net/sourceforge/opencamera/sensorremote/RemoteRpcServer.java
@@ -26,7 +26,7 @@
import java.util.regex.Pattern;
/**
- * OpenCamera Sensors server v. 0.0
+ * OpenCamera Sensors server v. 0.1.2
*
* Accepted message types:
* - get IMU (accelerometer/gyroscope)
@@ -39,7 +39,8 @@
public class RemoteRpcServer extends Thread {
private static final String TAG = "RemoteRpcServer";
private static final int SOCKET_WAIT_TIME_MS = 1000;
- private static final String IMU_REQUEST_REGEX = "(imu\\?duration=)(\\d+)(&accel=)(\\d)(&gyro=)(\\d)(&magnetic=)(\\d)";
+ private static final String IMU_REQUEST_REGEX =
+ "(imu\\?duration=)(\\d+)(&accel=)(\\d)(&gyro=)(\\d)(&magnetic=)(\\d)(&gravity=)(\\d)(&rotation=)(\\d)";
private static final Pattern IMU_REQUEST_PATTERN = Pattern.compile(IMU_REQUEST_REGEX);
private final Properties mConfig;
@@ -77,11 +78,16 @@ private void handleRequest(String msg, PrintStream outputStream, BufferedOutputS
boolean wantAccel = Integer.parseInt(imuRequestMatcher.group(4)) == 1;
boolean wantGyro = Integer.parseInt(imuRequestMatcher.group(6)) == 1;
boolean wantMagnetic = Integer.parseInt(imuRequestMatcher.group(8)) == 1;
+ boolean wantGravity = Integer.parseInt(imuRequestMatcher.group(10)) == 1;
+ boolean wantRotation = Integer.parseInt(imuRequestMatcher.group(12)) == 1;
+
if (MyDebug.LOG) {
Log.d(TAG, "received IMU control request, duration = " + duration);
}
- RemoteRpcResponse imuResponse = mRequestHandler.handleImuRequest(duration, wantAccel, wantGyro, wantMagnetic);
+ RemoteRpcResponse imuResponse = mRequestHandler.handleImuRequest(
+ duration, wantAccel, wantGyro, wantMagnetic, wantGravity, wantRotation
+ );
outputStream.println(imuResponse.toString());
if (MyDebug.LOG) {
diff --git a/app/src/main/res/xml/preferences.xml b/app/src/main/res/xml/preferences.xml
index da66c9eb3..85e4e0915 100644
--- a/app/src/main/res/xml/preferences.xml
+++ b/app/src/main/res/xml/preferences.xml
@@ -125,6 +125,18 @@
android:defaultValue="true"
/>
+
+
+
+
+
+
+
+
+
+