-
Notifications
You must be signed in to change notification settings - Fork 14
/
Copy pathindex.html
1783 lines (1654 loc) · 113 KB
/
index.html
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
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
<!doctype html><html lang="en">
<head>
<meta content="text/html; charset=utf-8" http-equiv="Content-Type">
<meta content="width=device-width, initial-scale=1, shrink-to-fit=no" name="viewport">
<title>Motion Sensors Explainer</title>
<meta content="ED" name="w3c-status">
<link href="https://www.w3.org/StyleSheets/TR/2021/W3C-ED" rel="stylesheet">
<meta content="Bikeshed version 82ce88815, updated Thu Sep 7 16:33:55 2023 -0700" name="generator">
<link href="https://www.w3.org/TR/motion-sensors/" rel="canonical">
<style>
table {
border-collapse: collapse;
border-spacing: 0px;
}
table, th, td {
padding: 5px;
border: 1px solid black;
}
</style>
<style>/* Boilerplate: style-autolinks */
.css.css, .property.property, .descriptor.descriptor {
color: var(--a-normal-text);
font-size: inherit;
font-family: inherit;
}
.css::before, .property::before, .descriptor::before {
content: "‘";
}
.css::after, .property::after, .descriptor::after {
content: "’";
}
.property, .descriptor {
/* Don't wrap property and descriptor names */
white-space: nowrap;
}
.type { /* CSS value <type> */
font-style: italic;
}
pre .property::before, pre .property::after {
content: "";
}
[data-link-type="property"]::before,
[data-link-type="propdesc"]::before,
[data-link-type="descriptor"]::before,
[data-link-type="value"]::before,
[data-link-type="function"]::before,
[data-link-type="at-rule"]::before,
[data-link-type="selector"]::before,
[data-link-type="maybe"]::before {
content: "‘";
}
[data-link-type="property"]::after,
[data-link-type="propdesc"]::after,
[data-link-type="descriptor"]::after,
[data-link-type="value"]::after,
[data-link-type="function"]::after,
[data-link-type="at-rule"]::after,
[data-link-type="selector"]::after,
[data-link-type="maybe"]::after {
content: "’";
}
[data-link-type].production::before,
[data-link-type].production::after,
.prod [data-link-type]::before,
.prod [data-link-type]::after {
content: "";
}
[data-link-type=element],
[data-link-type=element-attr] {
font-family: Menlo, Consolas, "DejaVu Sans Mono", monospace;
font-size: .9em;
}
[data-link-type=element]::before { content: "<" }
[data-link-type=element]::after { content: ">" }
[data-link-type=biblio] {
white-space: pre;
}
@media (prefers-color-scheme: dark) {
:root {
--selflink-text: black;
--selflink-bg: silver;
--selflink-hover-text: white;
}
}
</style>
<style>/* Boilerplate: style-colors */
/* Any --*-text not paired with a --*-bg is assumed to have a transparent bg */
:root {
color-scheme: light dark;
--text: black;
--bg: white;
--unofficial-watermark: url(https://www.w3.org/StyleSheets/TR/2016/logos/UD-watermark);
--logo-bg: #1a5e9a;
--logo-active-bg: #c00;
--logo-text: white;
--tocnav-normal-text: #707070;
--tocnav-normal-bg: var(--bg);
--tocnav-hover-text: var(--tocnav-normal-text);
--tocnav-hover-bg: #f8f8f8;
--tocnav-active-text: #c00;
--tocnav-active-bg: var(--tocnav-normal-bg);
--tocsidebar-text: var(--text);
--tocsidebar-bg: #f7f8f9;
--tocsidebar-shadow: rgba(0,0,0,.1);
--tocsidebar-heading-text: hsla(203,20%,40%,.7);
--toclink-text: var(--text);
--toclink-underline: #3980b5;
--toclink-visited-text: var(--toclink-text);
--toclink-visited-underline: #054572;
--heading-text: #005a9c;
--hr-text: var(--text);
--algo-border: #def;
--del-text: red;
--del-bg: transparent;
--ins-text: #080;
--ins-bg: transparent;
--a-normal-text: #034575;
--a-normal-underline: #bbb;
--a-visited-text: var(--a-normal-text);
--a-visited-underline: #707070;
--a-hover-bg: rgba(75%, 75%, 75%, .25);
--a-active-text: #c00;
--a-active-underline: #c00;
--blockquote-border: silver;
--blockquote-bg: transparent;
--blockquote-text: currentcolor;
--issue-border: #e05252;
--issue-bg: #fbe9e9;
--issue-text: var(--text);
--issueheading-text: #831616;
--example-border: #e0cb52;
--example-bg: #fcfaee;
--example-text: var(--text);
--exampleheading-text: #574b0f;
--note-border: #52e052;
--note-bg: #e9fbe9;
--note-text: var(--text);
--noteheading-text: hsl(120, 70%, 30%);
--notesummary-underline: silver;
--assertion-border: #aaa;
--assertion-bg: #eee;
--assertion-text: black;
--advisement-border: orange;
--advisement-bg: #fec;
--advisement-text: var(--text);
--advisementheading-text: #b35f00;
--warning-border: red;
--warning-bg: hsla(40,100%,50%,0.95);
--warning-text: var(--text);
--amendment-border: #330099;
--amendment-bg: #F5F0FF;
--amendment-text: var(--text);
--amendmentheading-text: #220066;
--def-border: #8ccbf2;
--def-bg: #def;
--def-text: var(--text);
--defrow-border: #bbd7e9;
--datacell-border: silver;
--indexinfo-text: #707070;
--indextable-hover-text: black;
--indextable-hover-bg: #f7f8f9;
--outdatedspec-bg: rgba(0, 0, 0, .5);
--outdatedspec-text: black;
--outdated-bg: maroon;
--outdated-text: white;
--outdated-shadow: red;
--editedrec-bg: darkorange;
}
@media (prefers-color-scheme: dark) {
:root {
--text: #ddd;
--bg: black;
--unofficial-watermark: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='400' height='400'%3E%3Cg fill='%23100808' transform='translate(200 200) rotate(-45) translate(-200 -200)' stroke='%23100808' stroke-width='3'%3E%3Ctext x='50%25' y='220' style='font: bold 70px sans-serif; text-anchor: middle; letter-spacing: 6px;'%3EUNOFFICIAL%3C/text%3E%3Ctext x='50%25' y='305' style='font: bold 70px sans-serif; text-anchor: middle; letter-spacing: 6px;'%3EDRAFT%3C/text%3E%3C/g%3E%3C/svg%3E");
--logo-bg: #1a5e9a;
--logo-active-bg: #c00;
--logo-text: white;
--tocnav-normal-text: #999;
--tocnav-normal-bg: var(--bg);
--tocnav-hover-text: var(--tocnav-normal-text);
--tocnav-hover-bg: #080808;
--tocnav-active-text: #f44;
--tocnav-active-bg: var(--tocnav-normal-bg);
--tocsidebar-text: var(--text);
--tocsidebar-bg: #080808;
--tocsidebar-shadow: rgba(255,255,255,.1);
--tocsidebar-heading-text: hsla(203,20%,40%,.7);
--toclink-text: var(--text);
--toclink-underline: #6af;
--toclink-visited-text: var(--toclink-text);
--toclink-visited-underline: #054572;
--heading-text: #8af;
--hr-text: var(--text);
--algo-border: #456;
--del-text: #f44;
--del-bg: transparent;
--ins-text: #4a4;
--ins-bg: transparent;
--a-normal-text: #6af;
--a-normal-underline: #555;
--a-visited-text: var(--a-normal-text);
--a-visited-underline: var(--a-normal-underline);
--a-hover-bg: rgba(25%, 25%, 25%, .2);
--a-active-text: #f44;
--a-active-underline: var(--a-active-text);
--borderedblock-bg: rgba(255, 255, 255, .05);
--blockquote-border: silver;
--blockquote-bg: var(--borderedblock-bg);
--blockquote-text: currentcolor;
--issue-border: #e05252;
--issue-bg: var(--borderedblock-bg);
--issue-text: var(--text);
--issueheading-text: hsl(0deg, 70%, 70%);
--example-border: hsl(50deg, 90%, 60%);
--example-bg: var(--borderedblock-bg);
--example-text: var(--text);
--exampleheading-text: hsl(50deg, 70%, 70%);
--note-border: hsl(120deg, 100%, 35%);
--note-bg: var(--borderedblock-bg);
--note-text: var(--text);
--noteheading-text: hsl(120, 70%, 70%);
--notesummary-underline: silver;
--assertion-border: #444;
--assertion-bg: var(--borderedblock-bg);
--assertion-text: var(--text);
--advisement-border: orange;
--advisement-bg: #222218;
--advisement-text: var(--text);
--advisementheading-text: #f84;
--warning-border: red;
--warning-bg: hsla(40,100%,20%,0.95);
--warning-text: var(--text);
--amendment-border: #330099;
--amendment-bg: #080010;
--amendment-text: var(--text);
--amendmentheading-text: #cc00ff;
--def-border: #8ccbf2;
--def-bg: #080818;
--def-text: var(--text);
--defrow-border: #136;
--datacell-border: silver;
--indexinfo-text: #aaa;
--indextable-hover-text: var(--text);
--indextable-hover-bg: #181818;
--outdatedspec-bg: rgba(255, 255, 255, .5);
--outdatedspec-text: black;
--outdated-bg: maroon;
--outdated-text: white;
--outdated-shadow: red;
--editedrec-bg: darkorange;
}
/* In case a transparent-bg image doesn't expect to be on a dark bg,
which is quite common in practice... */
img { background: white; }
}
</style>
<style>/* Boilerplate: style-counters */
body {
counter-reset: example figure issue;
}
.issue {
counter-increment: issue;
}
.issue:not(.no-marker)::before {
content: "Issue " counter(issue);
}
.example {
counter-increment: example;
}
.example:not(.no-marker)::before {
content: "Example " counter(example);
}
.invalid.example:not(.no-marker)::before,
.illegal.example:not(.no-marker)::before {
content: "Invalid Example" counter(example);
}
figcaption {
counter-increment: figure;
}
figcaption:not(.no-marker)::before {
content: "Figure " counter(figure) " ";
}
</style>
<style>/* Boilerplate: style-dfn-panel */
:root {
--dfnpanel-bg: #ddd;
--dfnpanel-text: var(--text);
}
@media (prefers-color-scheme: dark) {
:root {
--dfnpanel-bg: #222;
--dfnpanel-text: var(--text);
}
}
.dfn-panel {
position: absolute;
z-index: 35;
width: 20em;
width: 300px;
height: auto;
max-height: 500px;
overflow: auto;
padding: 0.5em 0.75em;
font: small Helvetica Neue, sans-serif, Droid Sans Fallback;
background: var(--dfnpanel-bg);
color: var(--dfnpanel-text);
border: outset 0.2em;
white-space: normal; /* in case it's moved into a pre */
}
.dfn-panel:not(.on) { display: none; }
.dfn-panel * { margin: 0; padding: 0; text-indent: 0; }
.dfn-panel > b { display: block; }
.dfn-panel a { color: var(--dfnpanel-text); }
.dfn-panel a:not(:hover) { text-decoration: none !important; border-bottom: none !important; }
.dfn-panel > b + b { margin-top: 0.25em; }
.dfn-panel ul { padding: 0 0 0 1em; list-style: none; }
.dfn-panel li a {
white-space: pre;
display: inline-block;
max-width: calc(300px - 1.5em - 1em);
overflow: hidden;
text-overflow: ellipsis;
}
.dfn-panel.activated {
display: inline-block;
position: fixed;
left: .5em;
bottom: 2em;
margin: 0 auto;
max-width: calc(100vw - 1.5em - .4em - .5em);
max-height: 30vh;
}
.dfn-paneled[role="button"] { cursor: pointer; }
</style>
<style>/* Boilerplate: style-dfn-panel */
:root {
--dfnpanel-bg: #ddd;
--dfnpanel-text: var(--text);
}
@media (prefers-color-scheme: dark) {
:root {
--dfnpanel-bg: #222;
--dfnpanel-text: var(--text);
}
}
.dfn-panel {
position: absolute;
z-index: 35;
width: 20em;
width: 300px;
height: auto;
max-height: 500px;
overflow: auto;
padding: 0.5em 0.75em;
font: small Helvetica Neue, sans-serif, Droid Sans Fallback;
background: var(--dfnpanel-bg);
color: var(--dfnpanel-text);
border: outset 0.2em;
white-space: normal; /* in case it's moved into a pre */
}
.dfn-panel:not(.on) { display: none; }
.dfn-panel * { margin: 0; padding: 0; text-indent: 0; }
.dfn-panel > b { display: block; }
.dfn-panel a { color: var(--dfnpanel-text); }
.dfn-panel a:not(:hover) { text-decoration: none !important; border-bottom: none !important; }
.dfn-panel > b + b { margin-top: 0.25em; }
.dfn-panel ul { padding: 0 0 0 1em; list-style: none; }
.dfn-panel li a {
white-space: pre;
display: inline-block;
max-width: calc(300px - 1.5em - 1em);
overflow: hidden;
text-overflow: ellipsis;
}
.dfn-panel.activated {
display: inline-block;
position: fixed;
left: .5em;
bottom: 2em;
margin: 0 auto;
max-width: calc(100vw - 1.5em - .4em - .5em);
max-height: 30vh;
}
.dfn-paneled[role="button"] { cursor: pointer; }
</style>
<style>/* Boilerplate: style-issues */
a[href].issue-return {
float: right;
float: inline-end;
color: var(--issueheading-text);
font-weight: bold;
text-decoration: none;
}
</style>
<style>/* Boilerplate: style-md-lists */
/* This is a weird hack for me not yet following the commonmark spec
regarding paragraph and lists. */
[data-md] > :first-child {
margin-top: 0;
}
[data-md] > :last-child {
margin-bottom: 0;
}
</style>
<style>/* Boilerplate: style-selflinks */
:root {
--selflink-text: white;
--selflink-bg: gray;
--selflink-hover-text: black;
}
.heading, .issue, .note, .example, li, dt {
position: relative;
}
a.self-link {
position: absolute;
top: 0;
left: calc(-1 * (3.5rem - 26px));
width: calc(3.5rem - 26px);
height: 2em;
text-align: center;
border: none;
transition: opacity .2s;
opacity: .5;
}
a.self-link:hover {
opacity: 1;
}
.heading > a.self-link {
font-size: 83%;
}
.example > a.self-link,
.note > a.self-link,
.issue > a.self-link {
/* These blocks are overflow:auto, so positioning outside
doesn't work. */
left: auto;
right: 0;
}
li > a.self-link {
left: calc(-1 * (3.5rem - 26px) - 2em);
}
dfn > a.self-link {
top: auto;
left: auto;
opacity: 0;
width: 1.5em;
height: 1.5em;
background: var(--selflink-bg);
color: var(--selflink-text);
font-style: normal;
transition: opacity .2s, background-color .2s, color .2s;
}
dfn:hover > a.self-link {
opacity: 1;
}
dfn > a.self-link:hover {
color: var(--selflink-hover-text);
}
a.self-link::before { content: "¶"; }
.heading > a.self-link::before { content: "§"; }
dfn > a.self-link::before { content: "#"; }
</style>
<style>/* Boilerplate: style-syntax-highlighting */
code.highlight { padding: .1em; border-radius: .3em; }
pre.highlight, pre > code.highlight { display: block; padding: 1em; margin: .5em 0; overflow: auto; border-radius: 0; }
.highlight:not(.idl) { background: rgba(0, 0, 0, .03); }
c-[a] { color: #990055 } /* Keyword.Declaration */
c-[b] { color: #990055 } /* Keyword.Type */
c-[c] { color: #708090 } /* Comment */
c-[d] { color: #708090 } /* Comment.Multiline */
c-[e] { color: #0077aa } /* Name.Attribute */
c-[f] { color: #669900 } /* Name.Tag */
c-[g] { color: #222222 } /* Name.Variable */
c-[k] { color: #990055 } /* Keyword */
c-[l] { color: #000000 } /* Literal */
c-[m] { color: #000000 } /* Literal.Number */
c-[n] { color: #0077aa } /* Name */
c-[o] { color: #999999 } /* Operator */
c-[p] { color: #999999 } /* Punctuation */
c-[s] { color: #a67f59 } /* Literal.String */
c-[t] { color: #a67f59 } /* Literal.String.Single */
c-[u] { color: #a67f59 } /* Literal.String.Double */
c-[cp] { color: #708090 } /* Comment.Preproc */
c-[c1] { color: #708090 } /* Comment.Single */
c-[cs] { color: #708090 } /* Comment.Special */
c-[kc] { color: #990055 } /* Keyword.Constant */
c-[kn] { color: #990055 } /* Keyword.Namespace */
c-[kp] { color: #990055 } /* Keyword.Pseudo */
c-[kr] { color: #990055 } /* Keyword.Reserved */
c-[ld] { color: #000000 } /* Literal.Date */
c-[nc] { color: #0077aa } /* Name.Class */
c-[no] { color: #0077aa } /* Name.Constant */
c-[nd] { color: #0077aa } /* Name.Decorator */
c-[ni] { color: #0077aa } /* Name.Entity */
c-[ne] { color: #0077aa } /* Name.Exception */
c-[nf] { color: #0077aa } /* Name.Function */
c-[nl] { color: #0077aa } /* Name.Label */
c-[nn] { color: #0077aa } /* Name.Namespace */
c-[py] { color: #0077aa } /* Name.Property */
c-[ow] { color: #999999 } /* Operator.Word */
c-[mb] { color: #000000 } /* Literal.Number.Bin */
c-[mf] { color: #000000 } /* Literal.Number.Float */
c-[mh] { color: #000000 } /* Literal.Number.Hex */
c-[mi] { color: #000000 } /* Literal.Number.Integer */
c-[mo] { color: #000000 } /* Literal.Number.Oct */
c-[sb] { color: #a67f59 } /* Literal.String.Backtick */
c-[sc] { color: #a67f59 } /* Literal.String.Char */
c-[sd] { color: #a67f59 } /* Literal.String.Doc */
c-[se] { color: #a67f59 } /* Literal.String.Escape */
c-[sh] { color: #a67f59 } /* Literal.String.Heredoc */
c-[si] { color: #a67f59 } /* Literal.String.Interpol */
c-[sx] { color: #a67f59 } /* Literal.String.Other */
c-[sr] { color: #a67f59 } /* Literal.String.Regex */
c-[ss] { color: #a67f59 } /* Literal.String.Symbol */
c-[vc] { color: #0077aa } /* Name.Variable.Class */
c-[vg] { color: #0077aa } /* Name.Variable.Global */
c-[vi] { color: #0077aa } /* Name.Variable.Instance */
c-[il] { color: #000000 } /* Literal.Number.Integer.Long */
@media (prefers-color-scheme: dark) {
.highlight:not(.idl) { background: rgba(255, 255, 255, .05); }
c-[a] { color: #d33682 } /* Keyword.Declaration */
c-[b] { color: #d33682 } /* Keyword.Type */
c-[c] { color: #2aa198 } /* Comment */
c-[d] { color: #2aa198 } /* Comment.Multiline */
c-[e] { color: #268bd2 } /* Name.Attribute */
c-[f] { color: #b58900 } /* Name.Tag */
c-[g] { color: #cb4b16 } /* Name.Variable */
c-[k] { color: #d33682 } /* Keyword */
c-[l] { color: #657b83 } /* Literal */
c-[m] { color: #657b83 } /* Literal.Number */
c-[n] { color: #268bd2 } /* Name */
c-[o] { color: #657b83 } /* Operator */
c-[p] { color: #657b83 } /* Punctuation */
c-[s] { color: #6c71c4 } /* Literal.String */
c-[t] { color: #6c71c4 } /* Literal.String.Single */
c-[u] { color: #6c71c4 } /* Literal.String.Double */
c-[ch] { color: #2aa198 } /* Comment.Hashbang */
c-[cp] { color: #2aa198 } /* Comment.Preproc */
c-[cpf] { color: #2aa198 } /* Comment.PreprocFile */
c-[c1] { color: #2aa198 } /* Comment.Single */
c-[cs] { color: #2aa198 } /* Comment.Special */
c-[kc] { color: #d33682 } /* Keyword.Constant */
c-[kn] { color: #d33682 } /* Keyword.Namespace */
c-[kp] { color: #d33682 } /* Keyword.Pseudo */
c-[kr] { color: #d33682 } /* Keyword.Reserved */
c-[ld] { color: #657b83 } /* Literal.Date */
c-[nc] { color: #268bd2 } /* Name.Class */
c-[no] { color: #268bd2 } /* Name.Constant */
c-[nd] { color: #268bd2 } /* Name.Decorator */
c-[ni] { color: #268bd2 } /* Name.Entity */
c-[ne] { color: #268bd2 } /* Name.Exception */
c-[nf] { color: #268bd2 } /* Name.Function */
c-[nl] { color: #268bd2 } /* Name.Label */
c-[nn] { color: #268bd2 } /* Name.Namespace */
c-[py] { color: #268bd2 } /* Name.Property */
c-[ow] { color: #657b83 } /* Operator.Word */
c-[mb] { color: #657b83 } /* Literal.Number.Bin */
c-[mf] { color: #657b83 } /* Literal.Number.Float */
c-[mh] { color: #657b83 } /* Literal.Number.Hex */
c-[mi] { color: #657b83 } /* Literal.Number.Integer */
c-[mo] { color: #657b83 } /* Literal.Number.Oct */
c-[sa] { color: #6c71c4 } /* Literal.String.Affix */
c-[sb] { color: #6c71c4 } /* Literal.String.Backtick */
c-[sc] { color: #6c71c4 } /* Literal.String.Char */
c-[dl] { color: #6c71c4 } /* Literal.String.Delimiter */
c-[sd] { color: #6c71c4 } /* Literal.String.Doc */
c-[se] { color: #6c71c4 } /* Literal.String.Escape */
c-[sh] { color: #6c71c4 } /* Literal.String.Heredoc */
c-[si] { color: #6c71c4 } /* Literal.String.Interpol */
c-[sx] { color: #6c71c4 } /* Literal.String.Other */
c-[sr] { color: #6c71c4 } /* Literal.String.Regex */
c-[ss] { color: #6c71c4 } /* Literal.String.Symbol */
c-[fm] { color: #268bd2 } /* Name.Function.Magic */
c-[vc] { color: #cb4b16 } /* Name.Variable.Class */
c-[vg] { color: #cb4b16 } /* Name.Variable.Global */
c-[vi] { color: #cb4b16 } /* Name.Variable.Instance */
c-[vm] { color: #cb4b16 } /* Name.Variable.Magic */
c-[il] { color: #657b83 } /* Literal.Number.Integer.Long */
}
</style>
<body class="h-entry">
<div class="head">
<p data-fill-with="logo"><a class="logo" href="https://www.w3.org/"> <img alt="W3C" height="48" src="https://www.w3.org/StyleSheets/TR/2021/logos/W3C" width="72"> </a> </p>
<h1 class="p-name no-ref" id="title">Motion Sensors Explainer</h1>
<p id="w3c-state"><a href="https://www.w3.org/standards/types#ED">Editor’s Draft</a>, <time class="dt-updated" datetime="2023-11-02">2 November 2023</time></p>
<details open>
<summary>More details about this document</summary>
<div data-fill-with="spec-metadata">
<dl>
<dt>This version:
<dd><a class="u-url" href="https://w3c.github.io/motion-sensors/">https://w3c.github.io/motion-sensors/</a>
<dt>Latest published version:
<dd><a href="https://www.w3.org/TR/motion-sensors/">https://www.w3.org/TR/motion-sensors/</a>
<dt>Previous Versions:
<dd><a href="https://www.w3.org/TR/2017/NOTE-motion-sensors-20170511/" rel="prev">https://www.w3.org/TR/2017/NOTE-motion-sensors-20170511/</a>
<dt>Version History:
<dd><a href="https://github.com/w3c/motion-sensors/commits/gh-pages/index.bs">https://github.com/w3c/motion-sensors/commits/gh-pages/index.bs</a>
<dt>Feedback:
<dd><span><a href="mailto:public-device-apis@w3.org?subject=%5Bmotion-sensors%5D%20YOUR%20TOPIC%20HERE">public-device-apis@w3.org</a> with subject line “<kbd>[motion-sensors] <i data-lt>… message topic …</i></kbd>” (<a href="https://lists.w3.org/Archives/Public/public-device-apis/" rel="discussion">archives</a>)</span>
<dd><a href="https://github.com/w3c/motion-sensors/issues/">GitHub</a>
<dt class="editor">Editor:
<dd class="editor p-author h-card vcard" data-editor-id="57705"><a class="p-name fn u-url url" href="https://intel.com/">Kenneth Rohde Christiansen</a> (<span class="p-org org">Intel Corporation</span>)
<dt class="editor">Former Editor:
<dd class="editor p-author h-card vcard" data-editor-id="78335"><a class="p-name fn u-url url" href="http://intel.com/">Alexander Shalamov</a> (<span class="p-org org">Intel Corporation</span>)
<dt>Bug Reports:
<dd><a href="https://www.github.com/w3c/motion-sensors/issues/new">via the w3c/motion-sensors repository on GitHub</a>
</dl>
</div>
</details>
<div data-fill-with="warning"></div>
<p class="copyright" data-fill-with="copyright"><a href="https://www.w3.org/Consortium/Legal/ipr-notice#Copyright">Copyright</a> © 2023 <a href="https://www.w3.org/">World Wide Web Consortium</a>. <abbr title="World Wide Web Consortium">W3C</abbr><sup>®</sup> <a href="https://www.w3.org/Consortium/Legal/ipr-notice#Legal_Disclaimer">liability</a>, <a href="https://www.w3.org/Consortium/Legal/ipr-notice#W3C_Trademarks">trademark</a> and <a href="https://www.w3.org/Consortium/Legal/copyright-software" rel="license" title="W3C Software and Document License">permissive document license</a> rules apply. </p>
<hr title="Separator for header">
</div>
<div class="p-summary" data-fill-with="abstract">
<h2 class="no-num no-toc no-ref heading settled" id="abstract"><span class="content">Abstract</span></h2>
<p>This explainer is an introduction to low-level and high-level motion sensors,
their relationship, inner workings and common use-cases. Common cases of event
filtering and sensor fusion are introduced with examples, showing how to apply
that on sensors following the Generic Sensor API specification.</p>
</div>
<h2 class="no-num no-toc no-ref heading settled" id="sotd"><span class="content">Status of this document</span></h2>
<div data-fill-with="status">
<p> This is a public copy of the editors’ draft.
It is provided for discussion only and may change at any moment.
Its publication here does not imply endorsement of its contents by W3C.
Don’t cite this document other than as work in progress. </p>
<p> If you wish to make comments regarding this document, please send them to <a href="mailto:public-device-apis@w3.org?Subject=%5Bmotion-sensors%5D%20PUT%20SUBJECT%20HERE">public-device-apis@w3.org</a> (<a href="mailto:public-device-apis-request@w3.org?subject=subscribe">subscribe</a>, <a href="https://lists.w3.org/Archives/Public/public-device-apis/">archives</a>).
When sending e-mail,
please put the text “motion-sensors” in the subject,
preferably like this:
“[motion-sensors] <em>…summary of comment…</em>”.
All comments are welcome. </p>
<p> This document was produced by the <a href="https://www.w3.org/groups/wg/das">Devices and Sensors Working Group</a>. </p>
<p> This document was produced by a group operating under
the <a href="https://www.w3.org/Consortium/Patent-Policy/">W3C Patent Policy</a>.
W3C maintains a <a href="https://www.w3.org/groups/wg/das/ipr" rel="disclosure">public list of any patent disclosures</a> made in connection with the deliverables of the group;
that page also includes instructions for disclosing a patent.
An individual who has actual knowledge of a patent which the individual believes contains <a href="https://www.w3.org/Consortium/Patent-Policy-20200915/#def-essential">Essential Claim(s)</a> must disclose the information in accordance with <a href="https://www.w3.org/Consortium/Patent-Policy-20200915/#sec-Disclosure">section 6 of the W3C Patent Policy</a>. </p>
<p> This document is governed by the <a href="https://www.w3.org/2023/Process-20230612/" id="w3c_process_revision">12 June 2023 W3C Process Document</a>. </p>
<p></p>
</div>
<div data-fill-with="at-risk"></div>
<nav data-fill-with="table-of-contents" id="toc">
<h2 class="no-num no-toc no-ref" id="contents">Table of Contents</h2>
<ol class="toc" role="directory">
<li><a href="#intro"><span class="secno">1</span> <span class="content">Introduction</span></a>
<li><a href="#security-and-privacy"><span class="secno">2</span> <span class="content">Security and Privacy Considerations</span></a>
<li>
<a href="#low-level-sensors"><span class="secno">3</span> <span class="content">Low-level Sensors</span></a>
<ol class="toc">
<li><a href="#accelerometer-sensor"><span class="secno">3.1</span> <span class="content">Accelerometer</span></a>
<li><a href="#gyroscope-sensor"><span class="secno">3.2</span> <span class="content">Gyroscope</span></a>
<li><a href="#magnetometer-sensor"><span class="secno">3.3</span> <span class="content">Magnetometer</span></a>
</ol>
<li>
<a href="#highlevel-sensors"><span class="secno">4</span> <span class="content">High-level Sensors</span></a>
<ol class="toc">
<li><a href="#fusion-sensors"><span class="secno">4.1</span> <span class="content">Common fusion sensors</span></a>
<li>
<a href="#pass-filters"><span class="secno">4.2</span> <span class="content">Low and high pass filters</span></a>
<ol class="toc">
<li><a href="#low-pass-filters"><span class="secno">4.2.1</span> <span class="content">Low-pass filter</span></a>
<li><a href="#high-pass-filters"><span class="secno">4.2.2</span> <span class="content">High-pass filter</span></a>
</ol>
<li><a href="#absolute-orientation"><span class="secno">4.3</span> <span class="content">Absolute Orientation Sensor</span></a>
<li><a href="#geomagnetic-orientation"><span class="secno">4.4</span> <span class="content">Geomagnetic Orientation Sensor</span></a>
<li>
<a href="#relative-orientation"><span class="secno">4.5</span> <span class="content">Relative Orientation Sensor</span></a>
<ol class="toc">
<li><a href="#complementary-filters"><span class="secno">4.5.1</span> <span class="content">Complementary filter</span></a>
</ol>
<li><a href="#gravity-and-linear-acceleration"><span class="secno">4.6</span> <span class="content">Gravity and Linear Acceleration Sensor</span></a>
<li><a href="#usecases-and-requirements"><span class="secno">4.7</span> <span class="content">Use Cases and Requirements</span></a>
</ol>
<li>
<a href="#index"><span class="secno"></span> <span class="content">Index</span></a>
<ol class="toc">
<li><a href="#index-defined-here"><span class="secno"></span> <span class="content">Terms defined by this specification</span></a>
<li><a href="#index-defined-elsewhere"><span class="secno"></span> <span class="content">Terms defined by reference</span></a>
</ol>
<li>
<a href="#references"><span class="secno"></span> <span class="content">References</span></a>
<ol class="toc">
<li><a href="#normative"><span class="secno"></span> <span class="content">Normative References</span></a>
<li><a href="#informative"><span class="secno"></span> <span class="content">Informative References</span></a>
</ol>
</ol>
</nav>
<main>
<h2 class="heading settled" data-level="1" id="intro"><span class="secno">1. </span><span class="content">Introduction</span><a class="self-link" href="#intro"></a></h2>
<p>There are a handful of different motion sensors available in modern hardware such
as phones.</p>
<p>The motion sensors extends the Generic Sensor API <a data-link-type="biblio" href="#biblio-generic-sensor" title="Generic Sensor API">[GENERIC-SENSOR]</a> to expose
a class of low-level and fusion sensors. This document explains the relationship
between these sensors.</p>
<p>The low-level sensors include:</p>
<ul>
<li data-md>
<p><a data-link-type="dfn" href="#accelerometer" id="ref-for-accelerometer">Accelerometer</a></p>
<li data-md>
<p><a data-link-type="dfn" href="#gyroscope" id="ref-for-gyroscope">Gyroscope</a></p>
<li data-md>
<p><a data-link-type="dfn" href="#magnetometer-magnetometers" id="ref-for-magnetometer-magnetometers">Magnetometer</a></p>
</ul>
<p>Multiple new sensors can be created using the data from these above sensors
in different ways. These are commonly known as fusion sensors.</p>
<h2 class="heading settled" data-level="2" id="security-and-privacy"><span class="secno">2. </span><span class="content">Security and Privacy Considerations</span><a class="self-link" href="#security-and-privacy"></a></h2>
<p>There are no specific security and privacy considerations
beyond those described in the Generic Sensor API <a data-link-type="biblio" href="#biblio-generic-sensor" title="Generic Sensor API">[GENERIC-SENSOR]</a>.</p>
<h2 class="heading settled" data-level="3" id="low-level-sensors"><span class="secno">3. </span><span class="content">Low-level Sensors</span><a class="self-link" href="#low-level-sensors"></a></h2>
<h3 class="heading settled" data-level="3.1" id="accelerometer-sensor"><span class="secno">3.1. </span><span class="content">Accelerometer</span><a class="self-link" href="#accelerometer-sensor"></a></h3>
<p>A raw <dfn class="dfn-paneled" data-dfn-type="dfn" data-noexport id="accelerometer">accelerometer</dfn> sensor measures changes in acceleration in 3 different
directions, but is affected by <i><a data-link-type="dfn" href="https://w3c.github.io/accelerometer#gravity" id="ref-for-gravity">gravity</a></i>. The <a data-link-type="dfn" href="https://w3c.github.io/accelerometer#accelerometer-interface" id="ref-for-accelerometer-interface">Accelerometer interface</a> is defined in <a data-link-type="biblio" href="#biblio-accelerometer" title="Accelerometer">[ACCELEROMETER]</a> specification.</p>
<p>The <a data-link-type="dfn" href="#accelerometer" id="ref-for-accelerometer①">Accelerometer</a> sensor is an <dfn class="dfn-paneled" data-dfn-type="dfn" data-noexport id="inertial-frame-sensor">inertial-frame sensor</dfn>, this means that when the device
is in free fall, the acceleration is 0 m/s<sup>2</sup> in the falling direction, and
when a device is laying flat on a table, the acceleration in upwards direction will be equal
to the Earth <a data-link-type="dfn" href="https://w3c.github.io/accelerometer#gravity" id="ref-for-gravity①">gravity</a>, i.e. g ≡ 9.8 m/s<sup>2</sup> as it is measuring the force of the table
pushing the device upwards.</p>
<p>Accelerometers are less useful by themselves and often take part in other fusion sensors,
but they do have some purposes like registering shakes, steps and the like.</p>
<p>Often for such use-cases the developer is interested in the <a data-link-type="dfn" href="https://w3c.github.io/accelerometer#linear-acceleration" id="ref-for-linear-acceleration">linear acceleration</a> which is
the acceleration without the <a data-link-type="dfn" href="https://w3c.github.io/accelerometer#gravity" id="ref-for-gravity②">gravity</a>, called <a data-link-type="dfn" href="https://w3c.github.io/accelerometer#gravity" id="ref-for-gravity③">gravity</a> compensation
(See <a data-link-type="dfn" href="#linear-acceleration-sensor" id="ref-for-linear-acceleration-sensor">Linear Acceleration Sensor</a>); or the developer is interested in the isolated <a data-link-type="dfn" href="https://w3c.github.io/accelerometer#gravity" id="ref-for-gravity④">gravity</a>,
in order to know the <a data-link-type="dfn" href="https://w3c.github.io/accelerometer#gravity" id="ref-for-gravity⑤">gravity</a> vector (see <a data-link-type="dfn" href="#gravity-sensor" id="ref-for-gravity-sensor">Gravity Sensor</a>), which can be useful for some
kinds of sensor fusion like creating a magnetic compass.</p>
<p>For acceleration, you usually care about the big changes and want to avoid noise, like the <a data-link-type="dfn" href="https://w3c.github.io/accelerometer#gravity" id="ref-for-gravity⑥">gravity</a>, thus a <a data-link-type="dfn" href="#high-pass-filter" id="ref-for-high-pass-filter">high-pass filter</a> can help isolate the <a data-link-type="dfn" href="https://w3c.github.io/accelerometer#linear-acceleration" id="ref-for-linear-acceleration①">linear acceleration</a> and a <a data-link-type="dfn" href="#low-pass-filter" id="ref-for-low-pass-filter">low-pass
filter</a> can help isolate the <a data-link-type="dfn" href="https://w3c.github.io/accelerometer#gravity" id="ref-for-gravity⑦">gravity</a>. A <a data-link-type="dfn" href="#low-pass-filter" id="ref-for-low-pass-filter①">low-pass filter</a> can thus be useful for measuring a
tilt. Unfortunately, any <a data-link-type="dfn" href="#high-pass-filter" id="ref-for-high-pass-filter①">high-pass filter</a> or <a data-link-type="dfn" href="#low-pass-filter" id="ref-for-low-pass-filter②">low-pass filter</a> introduces a delay, which may or may not be
acceptable.</p>
<p>Notice, as accelerometers report <i>acceleration</i>, you need to integrate to get <i>velocity</i>:</p>
<p>v = ∫a×∂t</p>
<p>And again to get <i>position</i>:</p>
<p>x = ∫v×∂t</p>
<p>An integral creates drift, and a double integral amplifies that:</p>
<p>a = g×sin(θ), x = ½×at<sup>2</sup></p>
<p>So position from an accelerometer is very imprecise and not very useful.</p>
<h3 class="heading settled" data-level="3.2" id="gyroscope-sensor"><span class="secno">3.2. </span><span class="content">Gyroscope</span><a class="self-link" href="#gyroscope-sensor"></a></h3>
<p>A <dfn class="dfn-paneled" data-dfn-type="dfn" data-noexport id="gyroscope">gyroscope</dfn> senses <i><a data-link-type="dfn" href="https://w3c.github.io/gyroscope#angular-velocity" id="ref-for-angular-velocity">angular velocity</a></i>, relative to itself, thus it measures its own rotation,
using an inertial force called the Coriolis effect. Gyroscopes oscillate at relative high frequency in
order to measure this and are thus one of the most power hungry motion sensors. This also means
that they can easily be affected by other vibrations, like a vibration (rumble) motor or speaker
on the same device. The <a data-link-type="dfn" href="https://w3c.github.io/gyroscope#gyroscope-interface" id="ref-for-gyroscope-interface">Gyroscope interface</a> is defined in <a data-link-type="biblio" href="#biblio-gyroscope" title="Gyroscope">[GYROSCOPE]</a> specification.</p>
<p>In order to get rotation (angle) from a gyroscope, which senses <a data-link-type="dfn" href="https://w3c.github.io/gyroscope#angular-velocity" id="ref-for-angular-velocity①">angular velocity</a>, you need to
perform a single integration.</p>
<p>f ≡ frequency</p>
<p>∫cos(2π×ft)) = (1/(2π×f)) × sin(2π×ft)</p>
<p>But be aware that integration turns noise into drift. As we see above, the integration gets a
1/f outside, meaning that high frequency (f) noise disappears with integration, i.e. a noise
of frequency will drop by a factor of a 100, but a very low frequency will be amplified,
meaning the gyroscope will drift over time.</p>
<p>So in order to do it well you need to do it quickly and as you see below, we multiply with the
∂t, so any error in the reported time difference will manifest itself like the drift above.</p>
<p>θ<sub>n</sub> = θ<sub>n-1</sub> + ω × ∂t</p>
<p>With ω denoting the <a data-link-type="dfn" href="https://w3c.github.io/gyroscope#angular-velocity" id="ref-for-angular-velocity②">angular velocity</a> and θ, the resulting angle.</p>
<p>Most gyroscope sensors perform <a data-link-type="dfn" href="https://w3c.github.io/sensors#calibration" id="ref-for-calibration">calibration</a> by applying some sort of <i>drift compensation</i> in hardware for known low frequency caused by adjacent hardware on the device.</p>
<h3 class="heading settled" data-level="3.3" id="magnetometer-sensor"><span class="secno">3.3. </span><span class="content">Magnetometer</span><a class="self-link" href="#magnetometer-sensor"></a></h3>
<p><dfn class="dfn-paneled" data-dfn-for="magnetometer" data-dfn-type="dfn" data-noexport id="magnetometer-magnetometers">Magnetometers</dfn> are <i><a data-link-type="dfn" href="https://w3c.github.io/magnetometer#magnetic-field" id="ref-for-magnetic-field">magnetic field</a> sensors</i>, which means that without
any strong magnetic influence close by, it will sense the Earth’s <a data-link-type="dfn" href="https://w3c.github.io/magnetometer#magnetic-field" id="ref-for-magnetic-field①">magnetic field</a>, which more or less points
in the direction of North, but not true North. The <a data-link-type="dfn" href="https://w3c.github.io/magnetometer#magnetometer-interface" id="ref-for-magnetometer-interface">Magnetometer interface</a> is defined in <a data-link-type="biblio" href="#biblio-magnetometer" title="Magnetometer">[MAGNETOMETER]</a> specification.</p>
<p>As said, magnetometers are very sensitive to outside influence, like anything on a table that
has been slightly magnetized, and it is even affected by other things inside a device, though
the device manufacturer can compensate for this somewhat. In practise though, these sensors
work quite well for most common use-cases.</p>
<p>As long as nothing that is magnetized in the surrounding is moving around, the magnetometer
readings are stable enough to be used to isolate <a data-link-type="dfn" href="https://w3c.github.io/accelerometer#gravity" id="ref-for-gravity⑧">gravity</a> as mentioned above.</p>
<p>Magnetometer is a 3-axis sensor, which means it gives a 3D vector pointing to the strongest <a data-link-type="dfn" href="https://w3c.github.io/magnetometer#magnetic-field" id="ref-for-magnetic-field②">magnetic field</a>. It also means that it does not enforce a specific device orientation in order
to work.</p>
<p>In order to tell how the device is being held, though, you need a <a data-link-type="dfn" href="https://w3c.github.io/accelerometer#gravity" id="ref-for-gravity⑨">gravity</a> vector, which as a bare
minimum requires an accelerometer in the case of low pass filtering, and additionally a gyroscope
if more precise readings are needed. This is called <dfn data-dfn-type="dfn" data-noexport id="tilt-compensation">tilt compensation<a class="self-link" href="#tilt-compensation"></a></dfn>.</p>
<p>The most common use-case for magnetometers are as part of sensor fusion, in order to generate an
Orientation Sensor which is stationary to the Earth plane, or a compass, which is basically the
former with corrections to the declination depending on geolocation position, such that it points
to the true North.</p>
<h2 class="heading settled" data-level="4" id="highlevel-sensors"><span class="secno">4. </span><span class="content">High-level Sensors</span><a class="self-link" href="#highlevel-sensors"></a></h2>
<p>As mentioned above, each sensor has its own issues, such as noise and drift, and often need some
kind of compensation using input from a different sensor. Put another way, one sensor might not
be very precise on its own, but the sum of multiple sensory input can be much more stable.</p>
<p>Unfortunately, sensors require power, and the more sensors and the higher the measuring frequency,
higher the power consumption. The gyroscope is typically considered more power hungry than the
rest, as it needs to vibrate at a certain frequency in order to measure the <a data-link-type="dfn" href="https://w3c.github.io/gyroscope#angular-velocity" id="ref-for-angular-velocity③">angular velocity</a>.</p>
<p>For the above reasons, it is always important to consider the minimum set of sensors which
solves a task satisfactory. As many devices today can do certain kinds of sensor fusion in
hardware, it most often makes sense to use these from a power and performance point of view.</p>
<h3 class="heading settled" data-level="4.1" id="fusion-sensors"><span class="secno">4.1. </span><span class="content">Common fusion sensors</span><a class="self-link" href="#fusion-sensors"></a></h3>
<p>Below is a list of fusion sensors and what sensors they usually are made up of:</p>
<table>
<thead>
<tr>
<th>Sensor type
<th>Underlying physical sensors
<tbody>
<tr>
<td><a data-link-type="dfn" href="#relative-orientation-sensor" id="ref-for-relative-orientation-sensor">Relative Orientation Sensor</a>
<td><a data-link-type="dfn" href="#accelerometer" id="ref-for-accelerometer②">Accelerometer</a>, <a data-link-type="dfn" href="#gyroscope" id="ref-for-gyroscope①">Gyroscope</a>, MUST NOT USE <a data-link-type="dfn" href="#magnetometer-magnetometers" id="ref-for-magnetometer-magnetometers①">Magnetometer</a>
<tr>
<td><a data-link-type="dfn" href="#absolute-orientation-sensor" id="ref-for-absolute-orientation-sensor">Absolute Orientation Sensor</a>
<td><a data-link-type="dfn" href="#accelerometer" id="ref-for-accelerometer③">Accelerometer</a>, <a data-link-type="dfn" href="#magnetometer-magnetometers" id="ref-for-magnetometer-magnetometers②">Magnetometer</a>, AND (when present) <a data-link-type="dfn" href="#gyroscope" id="ref-for-gyroscope②">Gyroscope</a>
<tr>
<td><a data-link-type="dfn" href="#geomagnetic-orientation-sensor" id="ref-for-geomagnetic-orientation-sensor">Geomagnetic Orientation Sensor</a>
<td><a data-link-type="dfn" href="#accelerometer" id="ref-for-accelerometer④">Accelerometer</a>, <a data-link-type="dfn" href="#magnetometer-magnetometers" id="ref-for-magnetometer-magnetometers③">Magnetometer</a>, MUST NOT USE <a data-link-type="dfn" href="#gyroscope" id="ref-for-gyroscope③">Gyroscope</a>
<tr>
<td><a data-link-type="dfn" href="#gravity-sensor" id="ref-for-gravity-sensor①">Gravity Sensor</a>
<td><a data-link-type="dfn" href="#accelerometer" id="ref-for-accelerometer⑤">Accelerometer</a>, <a data-link-type="dfn" href="#gyroscope" id="ref-for-gyroscope④">Gyroscope</a>
<tr>
<td><a data-link-type="dfn" href="#linear-acceleration-sensor" id="ref-for-linear-acceleration-sensor①">Linear Acceleration Sensor</a>
<td><a data-link-type="dfn" href="#accelerometer" id="ref-for-accelerometer⑥">Accelerometer</a>, AND EITHER <a data-link-type="dfn" href="#gyroscope" id="ref-for-gyroscope⑤">Gyroscope</a> OR <a data-link-type="dfn" href="#magnetometer-magnetometers" id="ref-for-magnetometer-magnetometers④">Magnetometer</a>
</table>
<h3 class="heading settled" data-level="4.2" id="pass-filters"><span class="secno">4.2. </span><span class="content">Low and high pass filters</span><a class="self-link" href="#pass-filters"></a></h3>
<p>As mentioned earlier, it is possible to remove noise (high or low frequency) using
low and high pass filters. As the names say, the filters let low or high frequencies
pass and thus cut of, or minimize, the effect of unwanted frequencies.</p>
<h4 class="heading settled" data-level="4.2.1" id="low-pass-filters"><span class="secno">4.2.1. </span><span class="content">Low-pass filter</span><a class="self-link" href="#low-pass-filters"></a></h4>
<p>A common way to create a <dfn class="dfn-paneled" data-dfn-type="dfn" data-noexport id="low-pass-filter">low-pass filter</dfn> is to only use a percentage of
the latest value and take the rest from the existing value. In a way this means that
the filter remembers common values and thus smoothens out uncommon values which most
often are a result of noise. As it uses a big percentage of the existing value, this
solution introduces a delay in registering the actual events.</p>
<div class="example" id="example-f3515ccc">
<a class="self-link" href="#example-f3515ccc"></a>
<pre class="highlight"><c- a>class</c-> LowPassFilterData <c- p>{</c->
<c- kr>constructor</c-><c- p>(</c->reading<c- p>,</c-> bias<c- p>)</c-> <c- p>{</c->
Object<c- p>.</c->assign<c- p>(</c-><c- k>this</c-><c- p>,</c-> <c- p>{</c-> x<c- o>:</c-> reading<c- p>.</c->x<c- p>,</c-> y<c- o>:</c-> reading<c- p>.</c->y<c- p>,</c-> z<c- o>:</c-> reading<c- p>.</c->z <c- p>});</c->
<c- k>this</c-><c- p>.</c->bias <c- o>=</c-> bias<c- p>;</c->
<c- p>}</c->
update<c- p>(</c->reading<c- p>)</c-> <c- p>{</c->
<c- k>this</c-><c- p>.</c->x <c- o>=</c-> <c- k>this</c-><c- p>.</c->x <c- o>*</c-> <c- k>this</c-><c- p>.</c->bias <c- o>+</c-> reading<c- p>.</c->x <c- o>*</c-> <c- p>(</c-><c- mf>1</c-> <c- o>-</c-> <c- k>this</c-><c- p>.</c->bias<c- p>);</c->
<c- k>this</c-><c- p>.</c->y <c- o>=</c-> <c- k>this</c-><c- p>.</c->y <c- o>*</c-> <c- k>this</c-><c- p>.</c->bias <c- o>+</c-> reading<c- p>.</c->y <c- o>*</c-> <c- p>(</c-><c- mf>1</c-> <c- o>-</c-> <c- k>this</c-><c- p>.</c->bias<c- p>);</c->
<c- k>this</c-><c- p>.</c->z <c- o>=</c-> <c- k>this</c-><c- p>.</c->z <c- o>*</c-> <c- k>this</c-><c- p>.</c->bias <c- o>+</c-> reading<c- p>.</c->z <c- o>*</c-> <c- p>(</c-><c- mf>1</c-> <c- o>-</c-> <c- k>this</c-><c- p>.</c->bias<c- p>);</c->
<c- p>}</c->
<c- p>};</c->
<c- a>const</c-> accl <c- o>=</c-> <c- ow>new</c-> Accelerometer<c- p>({</c-> frequency<c- o>:</c-> <c- mf>20</c-> <c- p>});</c->
<c- c1>// Isolate gravity with low-pass filter.</c->
<c- a>const</c-> filter <c- o>=</c-> <c- ow>new</c-> LowPassFilterData<c- p>(</c->accl<c- p>,</c-> <c- mf>0.8</c-><c- p>);</c->
accl<c- p>.</c->onreading <c- o>=</c-> <c- p>()</c-> <c- p>=></c-> <c- p>{</c->
filter<c- p>.</c->update<c- p>(</c->accl<c- p>);</c-> <c- c1>// Pass latest values through filter.</c->
console<c- p>.</c->log<c- p>(</c-><c- sb>`Isolated gravity (</c-><c- si>${</c->filter<c- p>.</c->x<c- si>}</c-><c- sb>, </c-><c- si>${</c->filter<c- p>.</c->y<c- si>}</c-><c- sb>, </c-><c- si>${</c->filter<c- p>.</c->z<c- si>}</c-><c- sb>)`</c-><c- p>);</c->
<c- p>}</c->
accl<c- p>.</c->start<c- p>();</c->
</pre>
</div>
<h4 class="heading settled" data-level="4.2.2" id="high-pass-filters"><span class="secno">4.2.2. </span><span class="content">High-pass filter</span><a class="self-link" href="#high-pass-filters"></a></h4>
<p><dfn class="dfn-paneled" data-dfn-type="dfn" data-noexport id="high-pass-filter">High-pass filter</dfn> works like a <a data-link-type="dfn" href="#low-pass-filter" id="ref-for-low-pass-filter③">low-pass filter</a>, but allows only high frequencies to pass through.</p>
<p>This can be useful to get rid of the drift which builds up over time
with <a data-link-type="dfn" href="#gyroscope" id="ref-for-gyroscope⑥">gyroscope</a> readings.</p>
<div class="example" id="example-ab2bf684">
<a class="self-link" href="#example-ab2bf684"></a>
<pre class="highlight"><c- a>class</c-> HighPassFilterData <c- p>{</c->
<c- kr>constructor</c-><c- p>(</c->reading<c- p>,</c-> cutoffFrequency<c- p>)</c-> <c- p>{</c->
Object<c- p>.</c->assign<c- p>(</c-><c- k>this</c-><c- p>,</c-> <c- p>{</c-> x<c- o>:</c-> reading<c- p>.</c->x<c- p>,</c-> y<c- o>:</c-> reading<c- p>.</c->y<c- p>,</c-> z<c- o>:</c-> reading<c- p>.</c->z <c- p>});</c->
<c- k>this</c-><c- p>.</c->cutoff <c- o>=</c-> cutoffFrequency<c- p>;</c->
<c- k>this</c-><c- p>.</c->timestamp <c- o>=</c-> reading<c- p>.</c->timestamp<c- p>;</c->
<c- p>}</c->
update<c- p>(</c->reading<c- p>)</c-> <c- p>{</c->
<c- a>let</c-> dt <c- o>=</c-> reading<c- p>.</c->timestamp <c- o>-</c-> <c- k>this</c-><c- p>.</c->timestamp <c- o>/</c-> <c- mf>1000</c-><c- p>;</c->
<c- k>this</c-><c- p>.</c->timestamp <c- o>=</c-> reading<c- p>.</c->timestamp<c- p>;</c->
<c- k>for</c-> <c- p>(</c-><c- a>let</c-> i <c- k>of</c-> <c- p>[</c-><c- u>"x"</c-><c- p>,</c-> <c- u>"y"</c-><c- p>,</c-> <c- u>"z"</c-><c- p>])</c-> <c- p>{</c->
<c- a>let</c-> alpha <c- o>=</c-> <c- k>this</c-><c- p>.</c->cutoff <c- o>/</c-> <c- p>(</c-><c- k>this</c-><c- p>.</c->cutoff <c- o>+</c-> dt<c- p>);</c->
<c- k>this</c-><c- p>[</c->i<c- p>]</c-> <c- o>=</c-> <c- k>this</c-><c- p>[</c->i<c- p>]</c-> <c- o>+</c-> alpha <c- o>*</c-> <c- p>(</c->reading<c- p>[</c->i<c- p>]</c-> <c- o>-</c-> <c- k>this</c-><c- p>[</c->i<c- p>]);</c->
<c- p>}</c->
<c- p>}</c->
<c- p>};</c->
<c- a>const</c-> gyro <c- o>=</c-> <c- ow>new</c-> Gyroscope<c- p>({</c-> frequency<c- o>:</c-> <c- mf>20</c-> <c- p>});</c->
<c- c1>// Remove drift with a high pass filter.</c->
<c- a>const</c-> filter <c- o>=</c-> <c- ow>new</c-> HighPassFilterData<c- p>(</c->gyro<c- p>,</c-> <c- mf>0.8</c-><c- p>);</c->
gyro<c- p>.</c->onreading <c- o>=</c-> <c- p>()</c-> <c- p>=></c-> <c- p>{</c->
filter<c- p>.</c->update<c- p>(</c->gyro<c- p>);</c-> <c- c1>// Pass latest values through filter.</c->
console<c- p>.</c->log<c- p>(</c-><c- sb>`Steady gyroscope (</c-><c- si>${</c->filter<c- p>.</c->x<c- si>}</c-><c- sb>, </c-><c- si>${</c->filter<c- p>.</c->y<c- si>}</c-><c- sb>, </c-><c- si>${</c->filter<c- p>.</c->z<c- si>}</c-><c- sb>)`</c-><c- p>);</c->
<c- p>}</c->
gyro<c- p>.</c->start<c- p>();</c->
</pre>
</div>
<h3 class="heading settled" data-level="4.3" id="absolute-orientation"><span class="secno">4.3. </span><span class="content">Absolute Orientation Sensor</span><a class="self-link" href="#absolute-orientation"></a></h3>
<p>As mentioned before, the <dfn class="dfn-paneled" data-dfn-type="dfn" data-noexport id="absolute-orientation-sensor">Absolute Orientation Sensor</dfn>, is one of the common use-cases of a <a data-link-type="dfn" href="#magnetometer-magnetometers" id="ref-for-magnetometer-magnetometers⑤">magnetometer</a>, and is a sensor representing an orientation stationary (fixed to the magnetic
field vector and <a data-link-type="dfn" href="https://w3c.github.io/accelerometer#gravity" id="ref-for-gravity①⓪">gravity</a> vector) to the Earth plane. The <a data-link-type="dfn" href="https://w3c.github.io/orientation-sensor#absoluteorientationsensor-interface" id="ref-for-absoluteorientationsensor-interface">AbsoluteOrientationSensor interface</a> is defined in <a data-link-type="biblio" href="#biblio-orientation-sensor" title="Orientation Sensor">[ORIENTATION-SENSOR]</a> specification.</p>
<p>An absolute orientation sensor can be useful for game controls such as a ball-in-a-maze puzzle, or
for a head-mounted display where you want to be able to rotate the display and look in all
directions.</p>
<p>As the reference frame of an absolute orientation sensor is stationary, it is not useful as a
controller for say a driving game on a phone, as it would not allow you to move around,
even slightly or slowly, without affecting your driving direction.
(See <a data-link-type="dfn" href="#relative-orientation-sensor" id="ref-for-relative-orientation-sensor①">Relative Orientation Sensor</a>).</p>
<p>The orientation vector of the <a data-link-type="dfn" href="#absolute-orientation-sensor" id="ref-for-absolute-orientation-sensor①">Absolute Orientation Sensor</a>, can be calculated in the following way:</p>
<p>As the <a data-link-type="dfn" href="#accelerometer" id="ref-for-accelerometer⑦">Accelerometer</a> is an <a data-link-type="dfn" href="#inertial-frame-sensor" id="ref-for-inertial-frame-sensor">inertial-frame sensor</a>, the <a data-link-type="dfn" href="https://w3c.github.io/accelerometer#gravity" id="ref-for-gravity①①">gravity</a> vector will point
towards the sky when the device is mostly stationary, and as long as the device is not in free fall,
there is enough vector length to project the <a data-link-type="dfn" href="https://w3c.github.io/magnetometer#magnetic-field" id="ref-for-magnetic-field③">magnetic field</a> vector onto the ground plane.</p>
<p class="note" role="note"><span class="marker">Note,</span> this will fail at the magnetic poles as the <a data-link-type="dfn" href="https://w3c.github.io/magnetometer#magnetic-field" id="ref-for-magnetic-field④">magnetic field</a> vector will point mostly in
the opposite direction as the <a data-link-type="dfn" href="https://w3c.github.io/accelerometer#gravity" id="ref-for-gravity①②">gravity</a> vector and generally be very unreliable.</p>
<p>By taking the cross product between the the <a data-link-type="dfn" href="https://w3c.github.io/magnetometer#magnetic-field" id="ref-for-magnetic-field⑤">magnetic field</a> vector and <a data-link-type="dfn" href="https://w3c.github.io/accelerometer#gravity" id="ref-for-gravity①③">gravity</a> vector
(see <a data-link-type="dfn" href="#gravity-sensor" id="ref-for-gravity-sensor②">Gravity Sensor</a>), we get a vector which points East on the ground plane, using the right hand rule.</p>
<p>Now if we take the cross product between the <a data-link-type="dfn" href="https://w3c.github.io/accelerometer#gravity" id="ref-for-gravity①④">gravity</a> vector and the newly found East vector, the resulting
vector will point in the northern direction towards the Earth’s <a data-link-type="dfn" href="https://w3c.github.io/magnetometer#magnetic-field" id="ref-for-magnetic-field⑥">magnetic field</a>.</p>
<p>The illustration below represents the case where the device is at rest and y-axis points towards the
North. The reading from the <a data-link-type="dfn" href="#magnetometer-magnetometers" id="ref-for-magnetometer-magnetometers⑥">Magnetometer</a> is {x: 0, y: 11, z: -16} and <a data-link-type="dfn" href="#accelerometer" id="ref-for-accelerometer⑧">Accelerometer</a> reports
{x: 0.11, y: 0.07, z: 9.81} <a data-link-type="dfn" href="https://w3c.github.io/accelerometer#acceleration" id="ref-for-acceleration">acceleration</a>. The uG is a unit vector representing the <a data-link-type="dfn" href="https://w3c.github.io/accelerometer#gravity" id="ref-for-gravity①⑤">gravity</a>,
uB represents <a data-link-type="dfn" href="https://w3c.github.io/magnetometer#magnetic-field" id="ref-for-magnetic-field⑦">magnetic field</a> vector, uE = uB × uG and points East. The uN = uG × uE points to
the northern direction.</p>
<p><img alt="OrientationSensor fusion." src="images/orientation_fusion.png" srcset="images/orientation_fusion.svg" style="display: block;margin: auto;"></p>
<p>That means an <a data-link-type="dfn" href="#absolute-orientation-sensor" id="ref-for-absolute-orientation-sensor②">Absolute Orientation Sensor</a> is a fusion sensor of the <a data-link-type="dfn" href="#magnetometer-magnetometers" id="ref-for-magnetometer-magnetometers⑦">Magnetometer</a> and the <a data-link-type="dfn" href="#accelerometer" id="ref-for-accelerometer⑨">Accelerometer</a>, and potentially the <a data-link-type="dfn" href="#gyroscope" id="ref-for-gyroscope⑦">Gyroscope</a> for better isolated <a data-link-type="dfn" href="https://w3c.github.io/accelerometer#gravity" id="ref-for-gravity①⑥">gravity</a> (see <a data-link-type="dfn" href="#gravity-sensor" id="ref-for-gravity-sensor③">Gravity Sensor</a>).</p>
<h3 class="heading settled" data-level="4.4" id="geomagnetic-orientation"><span class="secno">4.4. </span><span class="content">Geomagnetic Orientation Sensor</span><a class="self-link" href="#geomagnetic-orientation"></a></h3>
<p>A <dfn class="dfn-paneled" data-dfn-type="dfn" data-noexport id="geomagnetic-orientation-sensor">Geomagnetic Orientation Sensor</dfn>, is like a <a data-link-type="dfn" href="#absolute-orientation-sensor" id="ref-for-absolute-orientation-sensor③">Absolute Orientation Sensor</a>, but
doesn’t use the <a data-link-type="dfn" href="#gyroscope" id="ref-for-gyroscope⑧">Gyroscope</a> which means it uses less power. This also means that it
is more sensitive to shakes and movement.</p>
<p>As the main use-case for a <a data-link-type="dfn" href="#geomagnetic-orientation-sensor" id="ref-for-geomagnetic-orientation-sensor①">Geomagnetic Orientation Sensor</a> is to create a compass, or use
compass direction within a mapping application, this is not much of a problem since people
usually hold the device steady for these use-cases.</p>
<p>The actual <i>heading</i> (N, S, E, W) can be found by adjusting the rotation vector with
the local <i>declination compensation</i> calculated from the current geolocation position.</p>
<p>As the sensor uses the <a data-link-type="dfn" href="#accelerometer" id="ref-for-accelerometer①⓪">accelerometer</a> to get a more steady heading, like when walking,
the rotation vector is projected to the plane perpendicular to the <a data-link-type="dfn" href="https://w3c.github.io/accelerometer#gravity" id="ref-for-gravity①⑦">gravity</a> vector (as isolated
from the <a data-link-type="dfn" href="#accelerometer" id="ref-for-accelerometer①①">accelerometer</a>) which more or less represents the ground plane. This also means that
if you are interested in the actual orientation of the <a data-link-type="dfn" href="https://w3c.github.io/accelerometer#gravity" id="ref-for-gravity①⑧">gravity</a> vector, then use the <a data-link-type="dfn" href="#magnetometer-magnetometers" id="ref-for-magnetometer-magnetometers⑧">magnetometer</a> directly instead.</p>
<h3 class="heading settled" data-level="4.5" id="relative-orientation"><span class="secno">4.5. </span><span class="content">Relative Orientation Sensor</span><a class="self-link" href="#relative-orientation"></a></h3>
<p>On most sensor hubs, <a data-link-type="dfn" href="https://w3c.github.io/accelerometer#gravity" id="ref-for-gravity①⑨">gravity</a> is isolated from the <a data-link-type="dfn" href="#accelerometer" id="ref-for-accelerometer①②">accelerometer</a> using the <a data-link-type="dfn" href="#gyroscope" id="ref-for-gyroscope⑨">gyroscope</a>,
and the <a data-link-type="dfn" href="https://w3c.github.io/accelerometer#linear-acceleration" id="ref-for-linear-acceleration②">linear acceleration</a> is isolated by removing the isolated <a data-link-type="dfn" href="https://w3c.github.io/accelerometer#gravity" id="ref-for-gravity②⓪">gravity</a>, from the <a data-link-type="dfn" href="#accelerometer" id="ref-for-accelerometer①③">accelerometer</a> values.</p>
<p>This avoids the delay which low and high pass filters introduce.</p>
<p>One way of doing this is using a Kalman filter or <a data-link-type="dfn" href="#complementary-filter" id="ref-for-complementary-filter">complementary filter</a>, which leads us to
the <dfn class="dfn-paneled" data-dfn-type="dfn" data-noexport id="relative-orientation-sensor">Relative Orientation Sensor</dfn>. As a <a data-link-type="dfn" href="#complementary-filter" id="ref-for-complementary-filter①">complementary filter</a> yields quite good
results and is easy to implement in hardware, this is a common solution.</p>
<h4 class="heading settled" data-level="4.5.1" id="complementary-filters"><span class="secno">4.5.1. </span><span class="content">Complementary filter</span><a class="self-link" href="#complementary-filters"></a></h4>