1 /*
2 * Copyright (C) 2016 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 #include <stdlib.h>
18 #include <string.h>
19 #include <timer.h>
20 #include <heap.h>
21 #include <plat/rtc.h>
22 #include <plat/syscfg.h>
23 #include <hostIntf.h>
24 #include <nanohubPacket.h>
25 #include <floatRt.h>
26
27 #include <seos.h>
28
29 #include <nanohub_math.h>
30 #include <sensors.h>
31 #include <limits.h>
32
33 #define WINDOW_ORIENTATION_APP_VERSION 2
34
35 #define LOG_TAG "[WO]"
36
37 #define LOGW(fmt, ...) do { \
38 osLog(LOG_WARN, LOG_TAG " " fmt, ##__VA_ARGS__); \
39 } while (0);
40
41 #define LOGI(fmt, ...) do { \
42 osLog(LOG_INFO, LOG_TAG " " fmt, ##__VA_ARGS__); \
43 } while (0);
44
45 #define LOGD(fmt, ...) do { \
46 if (DBG_ENABLE) { \
47 osLog(LOG_DEBUG, LOG_TAG " " fmt, ##__VA_ARGS__); \
48 } \
49 } while (0);
50
51 #define DBG_ENABLE 0
52
53 #define ACCEL_MIN_RATE_HZ SENSOR_HZ(15) // 15 HZ
54 #define ACCEL_MAX_LATENCY_NS 40000000ull // 40 ms in nsec
55
56 // all time units in usec, angles in degrees
57 #define RADIANS_TO_DEGREES (180.0f / M_PI)
58
59 #define NS2US(x) (x >> 10) // convert nsec to approx usec
60
61 #define PROPOSAL_MIN_SETTLE_TIME NS2US(40000000ull) // 40 ms
62 #define PROPOSAL_MAX_SETTLE_TIME NS2US(400000000ull) // 400 ms
63 #define PROPOSAL_TILT_ANGLE_KNEE 20 // 20 deg
64 #define PROPOSAL_SETTLE_TIME_SLOPE NS2US(12000000ull) // 12 ms/deg
65
66 #define PROPOSAL_MIN_TIME_SINCE_FLAT_ENDED NS2US(500000000ull) // 500 ms
67 #define PROPOSAL_MIN_TIME_SINCE_SWING_ENDED NS2US(300000000ull) // 300 ms
68 #define PROPOSAL_MIN_TIME_SINCE_ACCELERATION_ENDED NS2US(500000000ull) // 500 ms
69
70 #define FLAT_ANGLE 80
71 #define FLAT_TIME NS2US(1000000000ull) // 1 sec
72
73 #define SWING_AWAY_ANGLE_DELTA 20
74 #define SWING_TIME NS2US(300000000ull) // 300 ms
75
76 #define MAX_FILTER_DELTA_TIME NS2US(1000000000ull) // 1 sec
77 #define FILTER_TIME_CONSTANT NS2US(200000000ull) // 200 ms
78
79 #define NEAR_ZERO_MAGNITUDE 1.0f // m/s^2
80 #define ACCELERATION_TOLERANCE 4.0f
81 #define STANDARD_GRAVITY 9.8f
82 #define MIN_ACCELERATION_MAGNITUDE (STANDARD_GRAVITY - ACCELERATION_TOLERANCE)
83 #define MAX_ACCELERATION_MAGNITUDE (STANDARD_GRAVITY + ACCELERATION_TOLERANCE)
84
85 #define MAX_TILT 80
86 #define TILT_OVERHEAD_ENTER -40
87 #define TILT_OVERHEAD_EXIT -15
88
89 #define ADJACENT_ORIENTATION_ANGLE_GAP 45
90
91 // TILT_HISTORY_SIZE has to be greater than the time constant
92 // max(FLAT_TIME, SWING_TIME) multiplied by the highest accel sample rate after
93 // interpolation (1.0 / MIN_ACCEL_INTERVAL).
94 #define TILT_HISTORY_SIZE 64
95 #define TILT_REFERENCE_PERIOD NS2US(1800000000000ull) // 30 min
96 #define TILT_REFERENCE_BACKOFF NS2US(300000000000ull) // 5 min
97
98 // Allow up to 2.5x of the desired rate (ACCEL_MIN_RATE_HZ)
99 // The concerns are complexity and (not so much) the size of tilt_history.
100 #define MIN_ACCEL_INTERVAL NS2US(26666667ull) // 26.7 ms for 37.5 Hz
101
102 #define EVT_SENSOR_ACC_DATA_RDY sensorGetMyEventType(SENS_TYPE_ACCEL)
103 #define EVT_SENSOR_WIN_ORIENTATION_DATA_RDY sensorGetMyEventType(SENS_TYPE_WIN_ORIENTATION)
104
105 static int8_t Tilt_Tolerance[4][2] = {
106 /* ROTATION_0 */ { -25, 70 },
107 /* ROTATION_90 */ { -25, 65 },
108 /* ROTATION_180 */ { -25, 60 },
109 /* ROTATION_270 */ { -25, 65 }
110 };
111
112 struct WindowOrientationTask {
113 uint32_t tid;
114 uint32_t handle;
115 uint32_t accelHandle;
116
117 uint64_t last_filtered_time;
118 struct TripleAxisDataPoint last_filtered_sample;
119
120 uint64_t tilt_reference_time;
121 uint64_t accelerating_time;
122 uint64_t predicted_rotation_time;
123 uint64_t flat_time;
124 uint64_t swinging_time;
125
126 uint32_t tilt_history_time[TILT_HISTORY_SIZE];
127 int tilt_history_index;
128 int8_t tilt_history[TILT_HISTORY_SIZE];
129
130 int8_t current_rotation;
131 int8_t prev_valid_rotation;
132 int8_t proposed_rotation;
133 int8_t predicted_rotation;
134
135 bool flat;
136 bool swinging;
137 bool accelerating;
138 bool overhead;
139 };
140
141 static struct WindowOrientationTask mTask;
142
143 static const struct SensorInfo mSi =
144 {
145 .sensorName = "Window Orientation",
146 .sensorType = SENS_TYPE_WIN_ORIENTATION,
147 .numAxis = NUM_AXIS_EMBEDDED,
148 .interrupt = NANOHUB_INT_NONWAKEUP,
149 .minSamples = 20
150 };
151
isTiltAngleAcceptable(int rotation,int8_t tilt_angle)152 static bool isTiltAngleAcceptable(int rotation, int8_t tilt_angle)
153 {
154 return ((tilt_angle >= Tilt_Tolerance[rotation][0])
155 && (tilt_angle <= Tilt_Tolerance[rotation][1]));
156 }
157
isOrientationAngleAcceptable(int current_rotation,int rotation,int orientation_angle)158 static bool isOrientationAngleAcceptable(int current_rotation, int rotation,
159 int orientation_angle)
160 {
161 // If there is no current rotation, then there is no gap.
162 // The gap is used only to introduce hysteresis among advertised orientation
163 // changes to avoid flapping.
164 int lower_bound, upper_bound;
165
166 LOGD("current %d, new %d, orientation %d",
167 (int)current_rotation, (int)rotation, (int)orientation_angle);
168
169 if (current_rotation >= 0) {
170 // If the specified rotation is the same or is counter-clockwise
171 // adjacent to the current rotation, then we set a lower bound on the
172 // orientation angle.
173 // For example, if currentRotation is ROTATION_0 and proposed is
174 // ROTATION_90, then we want to check orientationAngle > 45 + GAP / 2.
175 if ((rotation == current_rotation)
176 || (rotation == (current_rotation + 1) % 4)) {
177 lower_bound = rotation * 90 - 45
178 + ADJACENT_ORIENTATION_ANGLE_GAP / 2;
179 if (rotation == 0) {
180 if ((orientation_angle >= 315)
181 && (orientation_angle < lower_bound + 360)) {
182 return false;
183 }
184 } else {
185 if (orientation_angle < lower_bound) {
186 return false;
187 }
188 }
189 }
190
191 // If the specified rotation is the same or is clockwise adjacent,
192 // then we set an upper bound on the orientation angle.
193 // For example, if currentRotation is ROTATION_0 and rotation is
194 // ROTATION_270, then we want to check orientationAngle < 315 - GAP / 2.
195 if ((rotation == current_rotation)
196 || (rotation == (current_rotation + 3) % 4)) {
197 upper_bound = rotation * 90 + 45
198 - ADJACENT_ORIENTATION_ANGLE_GAP / 2;
199 if (rotation == 0) {
200 if ((orientation_angle <= 45)
201 && (orientation_angle > upper_bound)) {
202 return false;
203 }
204 } else {
205 if (orientation_angle > upper_bound) {
206 return false;
207 }
208 }
209 }
210 }
211 return true;
212 }
213
isPredictedRotationAcceptable(uint64_t now,int8_t tilt_angle)214 static bool isPredictedRotationAcceptable(uint64_t now, int8_t tilt_angle)
215 {
216 // piecewise linear settle_time qualification:
217 // settle_time_needed =
218 // 1) PROPOSAL_MIN_SETTLE_TIME, for |tilt_angle| < PROPOSAL_TILT_ANGLE_KNEE.
219 // 2) linearly increasing with |tilt_angle| at slope PROPOSAL_SETTLE_TIME_SLOPE
220 // until it reaches PROPOSAL_MAX_SETTLE_TIME.
221 int abs_tilt = (tilt_angle >= 0) ? tilt_angle : -tilt_angle;
222 uint64_t settle_time_needed = PROPOSAL_MIN_SETTLE_TIME;
223 if (abs_tilt > PROPOSAL_TILT_ANGLE_KNEE) {
224 settle_time_needed += PROPOSAL_SETTLE_TIME_SLOPE
225 * (abs_tilt - PROPOSAL_TILT_ANGLE_KNEE);
226 }
227 if (settle_time_needed > PROPOSAL_MAX_SETTLE_TIME) {
228 settle_time_needed = PROPOSAL_MAX_SETTLE_TIME;
229 }
230 LOGD("settle_time_needed ~%llu (msec), settle_time ~%llu (msec)",
231 settle_time_needed >> 10, (now - mTask.predicted_rotation_time) >> 10);
232
233 // The predicted rotation must have settled long enough.
234 if (now < mTask.predicted_rotation_time + settle_time_needed) {
235 LOGD("...rejected by settle_time");
236 return false;
237 }
238
239 // The last flat state (time since picked up) must have been sufficiently
240 // long ago.
241 if (now < mTask.flat_time + PROPOSAL_MIN_TIME_SINCE_FLAT_ENDED) {
242 LOGD("...rejected by flat_time");
243 return false;
244 }
245
246 // The last swing state (time since last movement to put down) must have
247 // been sufficiently long ago.
248 if (now < mTask.swinging_time + PROPOSAL_MIN_TIME_SINCE_SWING_ENDED) {
249 LOGD("...rejected by swing_time");
250 return false;
251 }
252
253 // The last acceleration state must have been sufficiently long ago.
254 if (now < mTask.accelerating_time
255 + PROPOSAL_MIN_TIME_SINCE_ACCELERATION_ENDED) {
256 LOGD("...rejected by acceleration_time");
257 return false;
258 }
259
260 // Looks good!
261 return true;
262 }
263
clearPredictedRotation()264 static void clearPredictedRotation()
265 {
266 mTask.predicted_rotation = -1;
267 mTask.predicted_rotation_time = 0;
268 }
269
clearTiltHistory()270 static void clearTiltHistory()
271 {
272 mTask.tilt_history_time[0] = 0;
273 mTask.tilt_history_index = 1;
274 mTask.tilt_reference_time = 0;
275 }
276
reset()277 static void reset()
278 {
279 mTask.last_filtered_time = 0;
280 mTask.proposed_rotation = -1;
281
282 mTask.flat_time = 0;
283 mTask.flat = false;
284
285 mTask.swinging_time = 0;
286 mTask.swinging = false;
287
288 mTask.accelerating_time = 0;
289 mTask.accelerating = false;
290
291 mTask.overhead = false;
292
293 clearPredictedRotation();
294 clearTiltHistory();
295 }
296
updatePredictedRotation(uint64_t now,int rotation)297 static void updatePredictedRotation(uint64_t now, int rotation)
298 {
299 if (mTask.predicted_rotation != rotation) {
300 mTask.predicted_rotation = rotation;
301 mTask.predicted_rotation_time = now;
302 }
303 }
304
isAccelerating(float magnitude)305 static bool isAccelerating(float magnitude)
306 {
307 return ((magnitude < MIN_ACCELERATION_MAGNITUDE)
308 || (magnitude > MAX_ACCELERATION_MAGNITUDE));
309 }
310
addTiltHistoryEntry(uint64_t now,int8_t tilt)311 static void addTiltHistoryEntry(uint64_t now, int8_t tilt)
312 {
313 uint64_t old_reference_time, delta;
314 size_t i;
315 int index;
316
317 if (mTask.tilt_reference_time == 0) {
318 // set reference_time after reset()
319
320 mTask.tilt_reference_time = now - 1;
321 } else if (mTask.tilt_reference_time + TILT_REFERENCE_PERIOD < now) {
322 // uint32_t tilt_history_time[] is good up to 71 min (2^32 * 1e-6 sec).
323 // proactively shift reference_time every 30 min,
324 // all history entries are within 4.3sec interval (15Hz x 64 samples)
325
326 old_reference_time = mTask.tilt_reference_time;
327 mTask.tilt_reference_time = now - TILT_REFERENCE_BACKOFF;
328
329 delta = mTask.tilt_reference_time - old_reference_time;
330 for (i = 0; i < TILT_HISTORY_SIZE; ++i) {
331 mTask.tilt_history_time[i] = (mTask.tilt_history_time[i] > delta)
332 ? (mTask.tilt_history_time[i] - delta) : 0;
333 }
334 }
335
336 index = mTask.tilt_history_index;
337 mTask.tilt_history[index] = tilt;
338 mTask.tilt_history_time[index] = now - mTask.tilt_reference_time;
339
340 index = ((index + 1) == TILT_HISTORY_SIZE) ? 0 : (index + 1);
341 mTask.tilt_history_index = index;
342 mTask.tilt_history_time[index] = 0;
343 }
344
nextTiltHistoryIndex(int index)345 static int nextTiltHistoryIndex(int index)
346 {
347 int next = (index == 0) ? (TILT_HISTORY_SIZE - 1): (index - 1);
348 return ((mTask.tilt_history_time[next] != 0) ? next : -1);
349 }
350
isFlat(uint64_t now)351 static bool isFlat(uint64_t now)
352 {
353 int i = mTask.tilt_history_index;
354 for (; (i = nextTiltHistoryIndex(i)) >= 0;) {
355 if (mTask.tilt_history[i] < FLAT_ANGLE) {
356 break;
357 }
358 if (mTask.tilt_reference_time + mTask.tilt_history_time[i] + FLAT_TIME <= now) {
359 // Tilt has remained greater than FLAT_ANGLE for FLAT_TIME.
360 return true;
361 }
362 }
363 return false;
364 }
365
isSwinging(uint64_t now,int8_t tilt)366 static bool isSwinging(uint64_t now, int8_t tilt)
367 {
368 int i = mTask.tilt_history_index;
369 for (; (i = nextTiltHistoryIndex(i)) >= 0;) {
370 if (mTask.tilt_reference_time + mTask.tilt_history_time[i] + SWING_TIME
371 < now) {
372 break;
373 }
374 if (mTask.tilt_history[i] + SWING_AWAY_ANGLE_DELTA <= tilt) {
375 // Tilted away by SWING_AWAY_ANGLE_DELTA within SWING_TIME.
376 // This is one-sided protection. No latency will be added when
377 // picking up the device and rotating.
378 return true;
379 }
380 }
381 return false;
382 }
383
add_samples(struct TripleAxisDataEvent * ev)384 static bool add_samples(struct TripleAxisDataEvent *ev)
385 {
386 int i, tilt_tmp;
387 int orientation_angle, nearest_rotation;
388 float x, y, z, alpha, magnitude;
389 uint64_t now_nsec = ev->referenceTime, now;
390 uint64_t then, time_delta;
391 struct TripleAxisDataPoint *last_sample;
392 size_t sampleCnt = ev->samples[0].firstSample.numSamples;
393 bool skip_sample;
394 bool accelerating, flat, swinging;
395 bool change_detected;
396 int8_t old_proposed_rotation, proposed_rotation;
397 int8_t tilt_angle;
398
399 for (i = 0; i < sampleCnt; i++) {
400
401 x = ev->samples[i].x;
402 y = ev->samples[i].y;
403 z = ev->samples[i].z;
404
405 // Apply a low-pass filter to the acceleration up vector in cartesian space.
406 // Reset the orientation listener state if the samples are too far apart in time.
407
408 now_nsec += i > 0 ? ev->samples[i].deltaTime : 0;
409 now = NS2US(now_nsec); // convert to ~usec
410
411 last_sample = &mTask.last_filtered_sample;
412 then = mTask.last_filtered_time;
413 time_delta = now - then;
414
415 if ((now < then) || (now > then + MAX_FILTER_DELTA_TIME)) {
416 reset();
417 skip_sample = true;
418 } else {
419 // alpha is the weight on the new sample
420 alpha = floatFromUint64(time_delta) / floatFromUint64(FILTER_TIME_CONSTANT + time_delta);
421 x = alpha * (x - last_sample->x) + last_sample->x;
422 y = alpha * (y - last_sample->y) + last_sample->y;
423 z = alpha * (z - last_sample->z) + last_sample->z;
424
425 skip_sample = false;
426 }
427
428 // poor man's interpolator for reduced complexity:
429 // drop samples when input sampling rate is 2.5x higher than requested
430 if (!skip_sample && (time_delta < MIN_ACCEL_INTERVAL)) {
431 skip_sample = true;
432 } else {
433 mTask.last_filtered_time = now;
434 mTask.last_filtered_sample.x = x;
435 mTask.last_filtered_sample.y = y;
436 mTask.last_filtered_sample.z = z;
437 }
438
439 accelerating = false;
440 flat = false;
441 swinging = false;
442
443 if (!skip_sample) {
444 // Calculate the magnitude of the acceleration vector.
445 magnitude = sqrtf(x * x + y * y + z * z);
446
447 if (magnitude < NEAR_ZERO_MAGNITUDE) {
448 LOGD("Ignoring sensor data, magnitude too close to zero.");
449 clearPredictedRotation();
450 } else {
451 // Determine whether the device appears to be undergoing
452 // external acceleration.
453 if (isAccelerating(magnitude)) {
454 accelerating = true;
455 mTask.accelerating_time = now;
456 }
457
458 // Calculate the tilt angle.
459 // This is the angle between the up vector and the x-y plane
460 // (the plane of the screen) in a range of [-90, 90] degrees.
461 // -90 degrees: screen horizontal and facing the ground (overhead)
462 // 0 degrees: screen vertical
463 // 90 degrees: screen horizontal and facing the sky (on table)
464 tilt_tmp = (int)(asinf(z / magnitude) * RADIANS_TO_DEGREES);
465 tilt_tmp = (tilt_tmp > 127) ? 127 : tilt_tmp;
466 tilt_tmp = (tilt_tmp < -128) ? -128 : tilt_tmp;
467 tilt_angle = tilt_tmp;
468 addTiltHistoryEntry(now, tilt_angle);
469
470 // Determine whether the device appears to be flat or swinging.
471 if (isFlat(now)) {
472 flat = true;
473 mTask.flat_time = now;
474 }
475 if (isSwinging(now, tilt_angle)) {
476 swinging = true;
477 mTask.swinging_time = now;
478 }
479
480 // If the tilt angle is too close to horizontal then we cannot
481 // determine the orientation angle of the screen.
482 if (tilt_angle <= TILT_OVERHEAD_ENTER) {
483 mTask.overhead = true;
484 } else if (tilt_angle >= TILT_OVERHEAD_EXIT) {
485 mTask.overhead = false;
486 }
487
488 if (mTask.overhead) {
489 LOGD("Ignoring sensor data, device is overhead: %d", (int)tilt_angle);
490 clearPredictedRotation();
491 } else if (fabsf(tilt_angle) > MAX_TILT) {
492 LOGD("Ignoring sensor data, tilt angle too high: %d", (int)tilt_angle);
493 clearPredictedRotation();
494 } else {
495 // Calculate the orientation angle.
496 // This is the angle between the x-y projection of the up
497 // vector onto the +y-axis, increasing clockwise in a range
498 // of [0, 360] degrees.
499 orientation_angle = (int)(-atan2f(-x, y) * RADIANS_TO_DEGREES);
500 if (orientation_angle < 0) {
501 // atan2 returns [-180, 180]; normalize to [0, 360]
502 orientation_angle += 360;
503 }
504
505 // Find the nearest rotation.
506 nearest_rotation = (orientation_angle + 45) / 90;
507 if (nearest_rotation == 4) {
508 nearest_rotation = 0;
509 }
510 // Determine the predicted orientation.
511 if (isTiltAngleAcceptable(nearest_rotation, tilt_angle)
512 && isOrientationAngleAcceptable(mTask.current_rotation,
513 nearest_rotation,
514 orientation_angle)) {
515 LOGD("Predicted: tilt %d, orientation %d, predicted %d",
516 (int)tilt_angle, (int)orientation_angle, (int)mTask.predicted_rotation);
517 updatePredictedRotation(now, nearest_rotation);
518 } else {
519 LOGD("Ignoring sensor data, no predicted rotation: "
520 "tilt %d, orientation %d",
521 (int)tilt_angle, (int)orientation_angle);
522 clearPredictedRotation();
523 }
524 }
525 }
526
527 mTask.flat = flat;
528 mTask.swinging = swinging;
529 mTask.accelerating = accelerating;
530
531 // Determine new proposed rotation.
532 old_proposed_rotation = mTask.proposed_rotation;
533 if ((mTask.predicted_rotation < 0)
534 || isPredictedRotationAcceptable(now, tilt_angle)) {
535
536 mTask.proposed_rotation = mTask.predicted_rotation;
537 }
538 proposed_rotation = mTask.proposed_rotation;
539
540 if ((proposed_rotation != old_proposed_rotation)
541 && (proposed_rotation >= 0)) {
542 mTask.current_rotation = proposed_rotation;
543
544 change_detected = (proposed_rotation != mTask.prev_valid_rotation);
545 mTask.prev_valid_rotation = proposed_rotation;
546
547 if (change_detected) {
548 return true;
549 }
550 }
551 }
552 }
553
554 return false;
555 }
556
557
windowOrientationPower(bool on,void * cookie)558 static bool windowOrientationPower(bool on, void *cookie)
559 {
560 if (on == false && mTask.accelHandle != 0) {
561 sensorRelease(mTask.tid, mTask.accelHandle);
562 mTask.accelHandle = 0;
563 osEventUnsubscribe(mTask.tid, EVT_SENSOR_ACC_DATA_RDY);
564 }
565
566 sensorSignalInternalEvt(mTask.handle, SENSOR_INTERNAL_EVT_POWER_STATE_CHG, on, 0);
567
568 return true;
569 }
570
windowOrientationSetRate(uint32_t rate,uint64_t latency,void * cookie)571 static bool windowOrientationSetRate(uint32_t rate, uint64_t latency, void *cookie)
572 {
573 int i;
574
575 if (mTask.accelHandle == 0) {
576 for (i = 0; sensorFind(SENS_TYPE_ACCEL, i, &mTask.accelHandle) != NULL; i++) {
577 if (sensorRequest(mTask.tid, mTask.accelHandle, ACCEL_MIN_RATE_HZ, ACCEL_MAX_LATENCY_NS)) {
578 // clear hysteresis
579 mTask.current_rotation = -1;
580 mTask.prev_valid_rotation = -1;
581 reset();
582 osEventSubscribe(mTask.tid, EVT_SENSOR_ACC_DATA_RDY);
583 break;
584 }
585 }
586 }
587
588 if (mTask.accelHandle != 0)
589 sensorSignalInternalEvt(mTask.handle, SENSOR_INTERNAL_EVT_RATE_CHG, rate, latency);
590
591 return true;
592 }
593
windowOrientationFirmwareUpload(void * cookie)594 static bool windowOrientationFirmwareUpload(void *cookie)
595 {
596 sensorSignalInternalEvt(mTask.handle, SENSOR_INTERNAL_EVT_FW_STATE_CHG,
597 1, 0);
598 return true;
599 }
600
windowOrientationFlush(void * cookie)601 static bool windowOrientationFlush(void *cookie)
602 {
603 return osEnqueueEvt(sensorGetMyEventType(SENS_TYPE_WIN_ORIENTATION), SENSOR_DATA_EVENT_FLUSH, NULL);
604 }
605
windowOrientationHandleEvent(uint32_t evtType,const void * evtData)606 static void windowOrientationHandleEvent(uint32_t evtType, const void* evtData)
607 {
608 struct TripleAxisDataEvent *ev;
609 union EmbeddedDataPoint sample;
610 bool rotation_changed;
611
612 if (evtData == SENSOR_DATA_EVENT_FLUSH)
613 return;
614
615 switch (evtType) {
616 case EVT_SENSOR_ACC_DATA_RDY:
617 ev = (struct TripleAxisDataEvent *)evtData;
618 rotation_changed = add_samples(ev);
619
620 if (rotation_changed) {
621 LOGI("rotation changed to: ******* %d *******\n",
622 (int)mTask.proposed_rotation);
623
624 // send a single int32 here so no memory alloc/free needed.
625 sample.idata = mTask.proposed_rotation;
626 if (!osEnqueueEvt(EVT_SENSOR_WIN_ORIENTATION_DATA_RDY, sample.vptr, NULL)) {
627 LOGW("osEnqueueEvt failure");
628 }
629 }
630 break;
631 }
632 }
633
634 static const struct SensorOps mSops =
635 {
636 .sensorPower = windowOrientationPower,
637 .sensorFirmwareUpload = windowOrientationFirmwareUpload,
638 .sensorSetRate = windowOrientationSetRate,
639 .sensorFlush = windowOrientationFlush,
640 };
641
window_orientation_start(uint32_t tid)642 static bool window_orientation_start(uint32_t tid)
643 {
644 mTask.tid = tid;
645
646 mTask.current_rotation = -1;
647 mTask.prev_valid_rotation = -1;
648 reset();
649
650 mTask.handle = sensorRegister(&mSi, &mSops, NULL, true);
651
652 return true;
653 }
654
windowOrientationEnd()655 static void windowOrientationEnd()
656 {
657 }
658
659 INTERNAL_APP_INIT(
660 APP_ID_MAKE(NANOHUB_VENDOR_GOOGLE, 3),
661 WINDOW_ORIENTATION_APP_VERSION,
662 window_orientation_start,
663 windowOrientationEnd,
664 windowOrientationHandleEvent);
665