krz/moonrise

Night-sky companion for iPhone, Apple Watch and Vision Pro: what's up tonight, the Moon, planets, launches and missions, and a Library of the solar system. SwiftUI app with a Cloudflare Workers backend. astronomy cloudflare-workers ios night-sky space swiftui visionos watchos

App/Sources/MotionPointer.swift

main
moonrise/App/Sources/MotionPointer.swift history · blame · raw

39 lines · 1447 bytes

4 symbols in this file
 1import CoreMotion
 2import Observation
 3import SkyEngine
 4import SkyRenderer
 5
 6/// Points the sky where the back of the phone points, using true north.
 7@MainActor
 8@Observable
 9final class MotionPointer {
10    private(set) var active = false
11    private(set) var direction = Horizontal(altitude: 45, azimuth: 180)
12    var available: Bool { CMMotionManager.availableAttitudeReferenceFrames().contains(.xTrueNorthZVertical) }
13
14    private let manager = CMMotionManager()
15
16    func start() {
17        guard available, !active else { return }
18        active = true
19        manager.deviceMotionUpdateInterval = 1.0 / 60
20        manager.startDeviceMotionUpdates(using: .xTrueNorthZVertical, to: .main) { [weak self] motion, _ in
21            guard let self, let m = motion?.attitude.rotationMatrix else { return }
22            MainActor.assumeIsolated { self.update(Pointing.direction(m31: m.m31, m32: m.m32, m33: m.m33)) }
23        }
24    }
25
26    func stop() {
27        manager.stopDeviceMotionUpdates()
28        active = false
29    }
30
31    /// Light low-pass filter; azimuth is smoothed across the 0/360 wrap.
32    private func update(_ h: Horizontal) {
33        let k = 0.25
34        let dAz = remainder(h.azimuth - direction.azimuth, 360)
35        var az = direction.azimuth + k * dAz
36        if az < 0 { az += 360 } else if az >= 360 { az -= 360 }
37        direction = Horizontal(altitude: direction.altitude + k * (h.altitude - direction.altitude), azimuth: az)
38    }
39}