All Downloads are FREE. Search and download functionalities are using the official Maven repository.

lucuma.core.math.RadialVelocity.scala Maven / Gradle / Ivy

There is a newer version: 0.108.0
Show newest version
// Copyright (c) 2016-2023 Association of Universities for Research in Astronomy, Inc. (AURA)
// For license information see LICENSE or https://opensource.org/licenses/BSD-3-Clause

package lucuma.core.math

import cats.*
import coulomb.*
import coulomb.ops.algebra.cats.all.given
import coulomb.policy.spire.standard.given
import coulomb.syntax.*
import lucuma.core.math.Constants.SpeedOfLight
import lucuma.core.math.units.*
import lucuma.core.optics.Format
import monocle.Prism
import spire.std.bigDecimal.*

/**
  * Representation of a radial velocity in meters per second
  * Valid range is (-C, C) where C is the speed of light
  * Radial Velocity is often represented as RV
  *
  * The selection of units is based on references to velocities as low as 20 cm/s
  * https://en.wikipedia.org/wiki/Radial_velocity
  */
final case class RadialVelocity private (rv: Quantity[BigDecimal, MetersPerSecond]) {

  // Direct conversion via coulomb turns to be too slow
  def toDoubleKilometersPerSecond: Double = rv.value.toDouble / 1000

  /**
    * Converts the radial velocity to a Redshift, approximate
    * a return value of None should be understood as an infinity Redshift
    */
  def toRedshift: Option[Redshift] =
    if (rv.value.abs < SpeedOfLight.value) {
      val i = (rv / SpeedOfLight).value
      val t = (1 + i) / (1 - i)
      Some(Redshift(BigDecimal.decimal(java.lang.Math.sqrt(t.toDouble) - 1).round(rv.value.mc)))
    } else None

  override def toString =
    s"RadialVelocity(${rv.toUnit[KilometersPerSecond].toValue[Double].show})"
}

object RadialVelocity {

  val fromMetersPerSecond: Prism[BigDecimal, RadialVelocity] =
    Prism[BigDecimal, RadialVelocity](b =>
      Some(b)
        .filter(_.abs <= SpeedOfLight.value)
        .flatMap(v => RadialVelocity(v.withUnit[MetersPerSecond]))
    )(_.rv.value)

  val kilometerspersecond: Format[BigDecimal, RadialVelocity] =
    Format[BigDecimal, RadialVelocity](
      b =>
        Some(b)
          .filter(_.abs <= SpeedOfLight.toValue[BigDecimal].toUnit[KilometersPerSecond].value)
          .flatMap(v => RadialVelocity(v.withUnit[KilometersPerSecond])),
      rv => rv.rv.toUnit[KilometersPerSecond].value
    )

  /**
    * Construct a RadialVelocity if the value is in the allowed range
    * @group Constructors
    */
  def apply(rv: Quantity[BigDecimal, MetersPerSecond]): Option[RadialVelocity] =
    if (rv.value.abs < SpeedOfLight.value) Some(new RadialVelocity(rv)) else None

  /**
    * Attempts to construct a RadialVelocity, it will fail if the value is outside the allowed range
    * @group Constructors
    */
  def unsafeFromQuantity(rv: Quantity[BigDecimal, MetersPerSecond]): RadialVelocity =
    apply(rv).getOrElse(sys.error(s"Value of rv $rv not allowed"))

  /**
    * `Zero RadialVelocity`
    * @group Constructors
    */
  val Zero: RadialVelocity = new RadialVelocity(0.withUnit[MetersPerSecond])

  /** @group Typeclass Instances */
  given Order[RadialVelocity] =
    Order.by(_.rv)

  /** @group Typeclass Instances */
  given Show[RadialVelocity] =
    Show.fromToString

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy