-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathRNBarometerModule.java
More file actions
228 lines (202 loc) · 7.64 KB
/
RNBarometerModule.java
File metadata and controls
228 lines (202 loc) · 7.64 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
package com.sensorworks.RNBarometer;
import android.app.Activity;
import android.content.Context;
import android.content.Intent;
import android.hardware.Sensor;
import android.hardware.SensorEvent;
import android.hardware.SensorManager;
import android.hardware.SensorEventListener;
import android.util.Log;
import com.facebook.react.bridge.ActivityEventListener;
import com.facebook.react.bridge.LifecycleEventListener;
import com.facebook.react.bridge.BaseActivityEventListener;
import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.bridge.ReactContextBaseJavaModule;
import com.facebook.react.bridge.Promise;
import com.facebook.react.bridge.Arguments;
import com.facebook.react.bridge.ReactMethod;
import com.facebook.react.bridge.ReadableMap;
import com.facebook.react.bridge.WritableMap;
import com.facebook.react.bridge.Callback;
import com.facebook.react.module.annotations.ReactModule;
import com.facebook.react.modules.core.DeviceEventManagerModule;
import com.facebook.react.modules.core.RCTNativeAppEventEmitter;
@ReactModule(name = RNBarometerModule.NAME)
public class RNBarometerModule extends ReactContextBaseJavaModule implements LifecycleEventListener, SensorEventListener {
public static final String NAME = "RNBarometer";
public static final int DEFAULT_INTERVAL_MS = 200; // 5 Hz
public static final double DEFAULT_SMOOTHING_FACTOR = 0.7;
private static final int ignoreSamples = 10;
private final ReactApplicationContext reactContext;
private final SensorManager mSensorManager;
private final Sensor mPressureSensor;
private boolean isRunning;
private int mIntervalMillis = DEFAULT_INTERVAL_MS;
private double mSmoothingFactor = DEFAULT_SMOOTHING_FACTOR;
private long mLastSampleTime;
private double mInitialAltitude;
private double mRelativeAltitude;
private double mRawPressure;
private double mAltitudeASL;
private double mLocalPressurehPa;
private int mIgnoredSamples;
public RNBarometerModule(ReactApplicationContext reactContext) {
super(reactContext);
this.reactContext = reactContext;
this.reactContext.addLifecycleEventListener(this);
mSensorManager = (SensorManager) reactContext.getSystemService(reactContext.SENSOR_SERVICE);
mPressureSensor = mSensorManager.getDefaultSensor(Sensor.TYPE_PRESSURE);
mLocalPressurehPa = SensorManager.PRESSURE_STANDARD_ATMOSPHERE;
mRawPressure = 0;
mAltitudeASL = 0;
mIgnoredSamples = 0;
mLastSampleTime = 0;
mRelativeAltitude = 0;
mInitialAltitude = -1;
mIntervalMillis = DEFAULT_INTERVAL_MS;
mSmoothingFactor = DEFAULT_SMOOTHING_FACTOR;
isRunning = false;
}
@Override
public String getName() {
return NAME;
}
@Override
public void onAccuracyChanged(Sensor sensor, int accuracy) {
}
@Override
public void onHostResume() {
if (isRunning) {
mSensorManager.registerListener(this, mPressureSensor, mIntervalMillis * 1000);
}
}
@Override
public void onHostPause() {
if (isRunning) {
mSensorManager.unregisterListener(this);
}
}
@Override
public void onHostDestroy() {
this.stopObserving();
}
//------------------------------------------------------------------------------------------------
// React interface
// Required for RN built in EventEmitter Calls.
@ReactMethod
public void addListener(String eventName) {}
@ReactMethod
public void removeListeners(Integer count) {}
@ReactMethod
// Determines if this device is capable of providing barometric updates
public void isSupported(Promise promise) {
promise.resolve(mPressureSensor != null);
}
@ReactMethod
// Sets the interval between event samples
public void setInterval(int interval) {
mIntervalMillis = interval;
boolean shouldStart = isRunning;
stopObserving();
if(shouldStart) {
startObserving(null);
}
}
@ReactMethod
// Sets the local pressure in hectopascals
public void setLocalPressure(double pressurehPa) {
mLocalPressurehPa = pressurehPa;
}
@ReactMethod
// Sets smoothing factor [0 -1].
// Note: More smoothing means more latency before
// the smoothed value has "caught up with" current
// conditions.
public void setSmoothingFactor(double smoothingFactor) {
if (smoothingFactor >= 0 && smoothingFactor <= 1.0) {
mSmoothingFactor = smoothingFactor;
}
}
@ReactMethod
// Gets smoothing factor
public void getSmoothingFactor(Promise promise) {
promise.resolve(mSmoothingFactor);
}
@ReactMethod
// Starts observing pressure
public void startObserving(Promise promise) {
if (mPressureSensor == null) {
promise.reject("-1",
"Pressure sensor not available; will not provide barometer data.");
return;
}
isRunning = true;
mSensorManager.registerListener(this, mPressureSensor, mIntervalMillis * 1000);
promise.resolve(mIntervalMillis);
}
@ReactMethod
// Stops observing pressure
public void stopObserving() {
mSensorManager.unregisterListener(this);
mRawPressure = 0;
mAltitudeASL = 0;
mLastSampleTime = 0;
mRelativeAltitude = 0;
mIgnoredSamples = 0;
mInitialAltitude = -1;
isRunning = false;
}
//------------------------------------------------------------------------------------------------
// Internal methods
@Override
public void onSensorChanged(SensorEvent sensorEvent) {
long tempMs = System.currentTimeMillis();
long timeSinceLastUpdate = tempMs - mLastSampleTime;
if (timeSinceLastUpdate >= mIntervalMillis) {
double lastAltitudeASL = mAltitudeASL;
// Get the smoothed raw pressure in millibar/hPa
mRawPressure = (sensorEvent.values[0] * (((double)1.0) - mSmoothingFactor) + mRawPressure * mSmoothingFactor);
// Calculate standard atmosphere altitude in metres
mAltitudeASL = getAltitude(SensorManager.PRESSURE_STANDARD_ATMOSPHERE, mRawPressure);
// Calculate our vertical speed in metres per second
double verticalSpeed = ((mAltitudeASL - lastAltitudeASL) / timeSinceLastUpdate) * 1000;
// Calculate our altitude based on our local pressure
double altitude = getAltitude(mLocalPressurehPa, mRawPressure);
// Calculate our relative altitude. This reflects the change in the current altitude,
// not the absolute altitude. So a hiking app might use this object to track the
// user’s elevation gain over the course of a hike for example.
if (mInitialAltitude == -1) {
if (mIgnoredSamples < ignoreSamples) {
mIgnoredSamples++;
} else {
mInitialAltitude = mAltitudeASL;
}
} else {
mRelativeAltitude = mAltitudeASL - mInitialAltitude;
}
// Send change events to the Javascript side via the React Native bridge
WritableMap map = Arguments.createMap();
map.putDouble("timestamp", (double) tempMs);
map.putDouble("pressure", mRawPressure);
map.putDouble("altitudeASL", mAltitudeASL);
map.putDouble("altitude", altitude);
map.putDouble("relativeAltitude", mRelativeAltitude);
map.putDouble("verticalSpeed", verticalSpeed);
try {
reactContext.getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter.class)
.emit("barometerUpdate", map);
} catch (RuntimeException e) {
Log.e("ERROR", "Error sending event over the React bridge");
}
mLastSampleTime = tempMs;
}
}
// Computes the Altitude in meters from the atmospheric pressure and the pressure at sea level.
// p0 pressure at sea level
// p atmospheric pressure
// returns an altitude in meters
private static double getAltitude(double p0, double p) {
final double coef = 1.0 / 5.255;
return 44330.0 * (1.0 - Math.pow(p / p0, coef));
}
}