package org.terst.nav.temp // Temporarily placing in 'temp' due to permissions import android.hardware.GeomagneticField import android.location.Location import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.asStateFlow import kotlinx.coroutines.flow.update import java.util.Date /** * Data class representing processed heading information. * @param trueHeading The heading relative to true North (0-359.9 degrees). * @param magneticHeading The heading relative to magnetic North (0-359.9 degrees). * @param magneticVariation The difference between true and magnetic North at the current location (+E, -W). * @param cog Course Over Ground (0-359.9 degrees). */ data class HeadingInfo( val trueHeading: Float, val magneticHeading: Float, val magneticVariation: Float, val cog: Float ) /** * Processor for handling heading data, including magnetic variation calculations * using the Android GeomagneticField. */ class HeadingDataProcessor { private val _headingInfoFlow = MutableStateFlow(HeadingInfo(0f, 0f, 0f, 0f)) val headingInfoFlow: StateFlow = _headingInfoFlow.asStateFlow() private var currentLatitude: Double = 0.0 private var currentLongitude: Double = 0.0 private var currentAltitude: Double = 0.0 /** * Updates the current geographic location for magnetic variation calculations. */ fun updateLocation(latitude: Double, longitude: Double, altitude: Double) { currentLatitude = latitude currentLongitude = longitude currentAltitude = altitude // Recalculate magnetic variation if location changes updateHeadingInfo(_headingInfoFlow.value.trueHeading, _headingInfoFlow.value.cog, true) } /** * Processes a new true heading and Course Over Ground (COG) value. * @param newTrueHeading The new true heading in degrees. * @param newCog The new COG in degrees. */ fun updateTrueHeadingAndCog(newTrueHeading: Float, newCog: Float) { updateHeadingInfo(newTrueHeading, newCog, true) } /** * Processes a new magnetic heading and Course Over Ground (COG) value. * @param newMagneticHeading The new magnetic heading in degrees. * @param newCog The new COG in degrees. */ fun updateMagneticHeadingAndCog(newMagneticHeading: Float, newCog: Float) { updateHeadingInfo(newMagneticHeading, newCog, false) } private fun updateHeadingInfo(heading: Float, cog: Float, isTrueHeadingInput: Boolean) { val magneticVariation = calculateMagneticVariation() val (finalTrueHeading, finalMagneticHeading) = if (isTrueHeadingInput) { Pair(heading, (heading - magneticVariation + 360) % 360) } else { Pair((heading + magneticVariation + 360) % 360, heading) } _headingInfoFlow.update { it.copy( trueHeading = finalTrueHeading, magneticHeading = finalMagneticHeading, magneticVariation = magneticVariation, cog = cog ) } } /** * Calculates the magnetic variation (declination) for the current location. * @return Magnetic variation in degrees (+E, -W). */ private fun calculateMagneticVariation(): Float { // GeomagneticField requires current time in milliseconds val currentTimeMillis = System.currentTimeMillis() // Create a dummy Location object to get altitude if only lat/lon are updated // GeomagneticField needs altitude, using 0 if not provided val geoField = GeomagneticField( currentLatitude.toFloat(), currentLongitude.toFloat(), currentAltitude.toFloat(), // Altitude in meters currentTimeMillis ) return geoField.declination // Declination is the magnetic variation } // Helper function to normalize angles (0-359.9) - though modulo handles this for positive floats private fun normalizeAngle(angle: Float): Float { return (angle % 360 + 360) % 360 } }