smd

package module
Version: v0.0.0-...-a32136e Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jan 17, 2018 License: MIT Imports: 23 Imported by: 0

README

Space Mission Design (smd)

Space Mission Design allows one to perform an initial space mission design, around a given celestial body or between celestial bodies.

This package was written to support my thesis and my astrodynamics courses (ASEN 6008 Space Mission Design / Interplanetary Mission Design) at the University of Colorado Boulder.

Build Status Coverage Status goreport

Features

Note: this list may not be up to date with the latest developments.

  • Propagation of an orbit around a celestial body
  • Direct closed-loop optimization of continuous thrust via Naasz and Ruggiero control laws.
  • VSOP87 support via the amazing https://github.com/soniakeys/meeus
  • Patched conics for interplanetary missions
  • Stream orbital elements as CSV for live visualization of how they change
  • Export as a set of NASA Cosmographia files (cf. http://cosmoguide.org/) for really cool visualization of the overall mission
  • Export mission state as CSV (cf. the examples/statOD/main.go)

Usage

If running smd and planning on changing reference frames (e.g. when doing patched conics) to attempting to include third body dynamics, you will need to define the SMD_CONFIG environment variable. This must define whether using VSOP87 or SPICE for frame transformations. An example of such a file is found in conf.toml. Important: this configuration file must be called conf.toml (but it can be placed in any directory). Note: the availability of this file will only occur in the function which gets the heliocentric orbit of a given planet. So definitely make sure this is configured before running a long simulation or it will crash when you're looking away.

Documentation

Index

Constants

View Source
const (

	// OptiΔaCL allows to optimize thrust for semi major axis change
	OptiΔaCL ControlLaw
	// OptiΔiCL allows to optimize thrust for inclination change
	OptiΔiCL
	// OptiΔeCL allows to optimize thrust for eccentricity change
	OptiΔeCL
	// OptiΔΩCL allows to optimize thrust forRAAN change
	OptiΔΩCL
	// OptiΔωCL allows to optimize thrust for argument of perigee change
	OptiΔωCL
	// Ruggiero uses the eponym method of combining the control laws
	Ruggiero ControlLawType = iota + 1
	// Naasz is another type of combination of control law
	Naasz
)
View Source
const (
	// EarthRotationRate is the average Earth rotation rate in radians per second.
	EarthRotationRate = 7.292115900231276e-5
	// EarthRotationRate2 is another value (project of StatOD).
	EarthRotationRate2 = 7.29211585275553e-5
)
View Source
const (
	// AU is one astronomical unit in kilometers.
	AU = 1.49597870700e8
)
View Source
const (
	// StepSize is the default step size of propagation.
	StepSize = 10 * time.Second
)

Variables

View Source
var (
	DSS34Canberra  = NewSpecialStation("DSS34Canberra", 0.691750, 0, -35.398333, 148.981944, σρ, σρDot, 6)
	DSS65Madrid    = NewSpecialStation("DSS65Madrid", 0.834939, 0, 40.427222, 4.250556, σρ, σρDot, 6)
	DSS13Goldstone = NewSpecialStation("DSS13Goldstone", 1.07114904, 0, 35.247164, 243.205, σρ, σρDot, 6)
)
View Source
var Earth = CelestialObject{"Earth", 6378.1363, 149598023, 3.98600433e5, 23.4393, 0.00005, 924645.0, 1082.6269e-6, -2.5324e-6, -1.6204e-6, 7.292115900231276e-5, nil}

Earth is home.

View Source
var Jupiter = CelestialObject{"Jupiter", 71492.0, 778298361, 1.266865361e8, 3.13, 1.30326966, 48.2e6, 0.01475, 0, -0.00058, 0, nil}

Jupiter is big.

View Source
var Mars = CelestialObject{"Mars", 3396.19, 227939282.5616, 4.28283100e4, 25.19, 1.85, 576000, 1964e-6, 36e-6, -18e-6, 3.878785053314509e-05, nil}

Mars is the vacation place.

View Source
var Neptune = CelestialObject{"Neptune", 24622.0, 30.110387 * AU, 6.8365299e6, 1.767, 0.72, 0, 0, 0, 0, 0, nil}

Neptune is giant. TODO: SOI

View Source
var Pluto = CelestialObject{"Pluto", 1151.0, 5915799000, 9. * 1e2, 118.0, 17.14216667, 1, 0, 0, 0, 0, nil}

Pluto is not a planet and had that down ranking coming. It should have stayed in its lane. WARNING: Pluto SOI is not defined.

View Source
var Saturn = CelestialObject{"Saturn", 60268.0, 1429394133, 3.7931208e7, 0.93, 2.485, 0, 0.01645, 0, -0.001, 0, nil}

Saturn floats and that's really cool. TODO: SOI

View Source
var Sun = CelestialObject{"Sun", 695700, -1, 1.32712440017987e11, 0.0, 0.0, -1, 0, 0, 0, 0, nil}

Sun is our closest star.

View Source
var Uranus = CelestialObject{"Uranus", 25559.0, 2875038615, 5.7939513e6, 1.02, 0.773, 0, 0.012, 0, 0, 0, nil}

Uranus is no joke. TODO: SOI

View Source
var Venus = CelestialObject{"Venus", 6051.8, 108208601, 3.24858599e5, 117.36, 3.39458, 0.616e6, 0.000027, 0, 0, 0, nil}

Venus is poisonous.

Functions

func Cartesian2Spherical

func Cartesian2Spherical(a []float64) (b []float64)

Cartesian2Spherical returns the provided Cartesian coordinates vector in spherical.

func Cross

func Cross(a, b []float64) []float64

Cross performs the Cross product.

func Deg2rad

func Deg2rad(a float64) float64

Deg2rad converts degrees to radians, and enforced only positive numbers.

func DenseIdentity

func DenseIdentity(n int) *mat64.Dense

DenseIdentity returns an identity matrix of type Dense and of the provided size.

func Dot

func Dot(a, b []float64) float64

Dot performs the inner product.

func ECEF2ECI

func ECEF2ECI(R []float64, θgst float64) []float64

ECEF2ECI converts the provided ECEF vector to ECI for the θgst given in degrees.

func ECI2ECEF

func ECI2ECEF(R []float64, θgst float64) []float64

ECI2ECEF converts the provided ECI vector to ECEF for the θgst given in degrees.

func GAFromVinf

func GAFromVinf(vInfInVec, vInfOutVec []float64, body CelestialObject) (ψ, rP, bT, bR, B, θ float64)

GAFromVinf computes gravity assist parameters about a given body from the V infinity vectors. All angles are in radians!

func GARPeriapsis

func GARPeriapsis(vInf, ψ float64, body CelestialObject) float64

GARPeriapsis computes the radius of periapsis from the turn angle about a given body.

func GATurnAngle

func GATurnAngle(vInf, rP float64, body CelestialObject) float64

GATurnAngle computes the turn angle about a given body based on the radius of periapsis.

func GEO2ECEF

func GEO2ECEF(altitude, latitude, longitude float64) []float64

GEO2ECEF converts the provided parameters (in km and radians) to the ECEF vector. Note that the first parameter is the altitude, not the radius from the center of the body!

func Hohmann

func Hohmann(rI, vI, rF, vF float64, body CelestialObject) (vDeparture, vArrival float64, tof time.Duration)

Hohmann computes an Hohmann transfer. It returns the departure and arrival velocities, and the time of flight. To get final computations: ΔvInit = vDepature - vI ΔvFinal = vArrival - vF

func Lambert

func Lambert(Ri, Rf *mat64.Vector, Δt0 time.Duration, ttype TransferType, body CelestialObject) (Vi, Vf *mat64.Vector, φ float64, err error)

Lambert solves the Lambert boundary problem: Given the initial and final radii and a central body, it returns the needed initial and final velocities along with φ which is the square of the difference in eccentric anomaly. Note that the direction of motion is computed directly in this function to simplify the generation of Pork chop plots.

func MxV33

func MxV33(m mat64.Matrix, v []float64) (o []float64)

MxV33 multiplies a matrix with a vector. Note that there is no dimension check!

func Norm

func Norm(v []float64) float64

Norm returns the Norm of a given vector which is supposed to be 3x1.

func PCPGenerator

func PCPGenerator(initPlanet, arrivalPlanet CelestialObject, initLaunch, maxLaunch, initArrival, maxArrival time.Time, ptsPerLaunchDay, ptsPerArrivalDay float64, transferType TransferType, plotC3, verbose, output bool) (c3Map, tofMap, vinfMap map[time.Time][]float64, vInfInitVecs, vInfArriVecs map[time.Time][]mat64.Vector)

PCPGenerator generates the PCP files to perform contour plots in Matlab (and eventually prints the command).

func R1

func R1(x float64) *mat64.Dense

R1 rotation about the 1st axis.

func R2

func R2(x float64) *mat64.Dense

R2 rotation about the 2nd axis.

func R3

func R3(x float64) *mat64.Dense

R3 rotation about the 3rd axis.

func R3R1R3

func R3R1R3(θ1, θ2, θ3 float64) *mat64.Dense

R3R1R3 performs a 3-1-3 Euler parameter rotation. From Schaub and Junkins (the one in Vallado is wrong... surprinsingly, right? =/)

func Rad2deg

func Rad2deg(a float64) float64

Rad2deg converts radians to degrees, and enforced only positive numbers.

func Rad2deg180

func Rad2deg180(a float64) float64

Rad2deg180 converts radians to degrees, and enforce between +/-180.

func Radii2ae

func Radii2ae(rA, rP float64) (a, e float64)

Radii2ae returns the semi major axis and the eccentricty from the radii.

func Rot313Vec

func Rot313Vec(θ1, θ2, θ3 float64, vI []float64) []float64

Rot313Vec converts a given vector from PQW frame to ECI frame.

func SCLogInit

func SCLogInit(name string) kitlog.Logger

SCLogInit initializes the logger.

func ScaledDenseIdentity

func ScaledDenseIdentity(n int, s float64) *mat64.Dense

ScaledDenseIdentity returns an identity matrix time of type Dense a scaling factor of the provided size.

func Sign

func Sign(v float64) float64

Sign returns the Sign of a given number.

func Spherical2Cartesian

func Spherical2Cartesian(a []float64) (b []float64)

Spherical2Cartesian returns the provided spherical coordinates vector in Cartesian.

func StreamStates

func StreamStates(conf ExportConfig, stateChan <-chan (State))

StreamStates streams the output of the channel to the provided file.

func Unit

func Unit(a []float64) (b []float64)

Unit returns the Unit vector of a given vector.

Types

type AntiTangential

type AntiTangential struct {
	// contains filtered or unexported fields
}

AntiTangential defines an antitangential thrust control law

func (AntiTangential) Control

func (cl AntiTangential) Control(o Orbit) []float64

Control implements the ThrustControl interface.

func (AntiTangential) Reason

func (cl AntiTangential) Reason() string

Reason implements the ThrustControl interface.

func (AntiTangential) Type

func (cl AntiTangential) Type() ControlLaw

Type implements the ThrustControl interface.

type BHT1500

type BHT1500 struct{}

BHT1500 is a Busek 1500 EPThruster. Below is the high thrust mode

func (*BHT1500) Max

func (t *BHT1500) Max() (voltage, power uint)

Max implements the EPThruster interface.

func (*BHT1500) Min

func (t *BHT1500) Min() (voltage, power uint)

Min implements the EPThruster interface.

func (*BHT1500) Thrust

func (t *BHT1500) Thrust(voltage, power uint) (thrust, isp float64)

Thrust implements the EPThruster interface.

type BHT8000

type BHT8000 struct{}

BHT8000 is a Busek 1500 EPThruster. Below is the high thrust mode

func (*BHT8000) Max

func (t *BHT8000) Max() (voltage, power uint)

Max implements the EPThruster interface.

func (*BHT8000) Min

func (t *BHT8000) Min() (voltage, power uint)

Min implements the EPThruster interface.

func (*BHT8000) Thrust

func (t *BHT8000) Thrust(voltage, power uint) (thrust, isp float64)

Thrust implements the EPThruster interface.

type BPlane

type BPlane struct {
	Orbit        Orbit
	BR, BT, LTOF float64
	// contains filtered or unexported fields
}

BPlane stores B-plane parameters and allows for differential correction.

func NewBPlane

func NewBPlane(o Orbit) BPlane

NewBPlane returns the B-plane of a given orbit.

func (BPlane) AchieveGoals

func (b BPlane) AchieveGoals(components int) ([]float64, error)

AchieveGoals attempts to achieve the provided goals. Returns an error if no goal is set or is no convergence after a certain number of attempts. Otherwise, returns the velocity vector needed to reach the goal.

func (*BPlane) SetBRGoal

func (b *BPlane) SetBRGoal(value, tolerance float64)

SetBRGoal sets to the B_R goal

func (*BPlane) SetBTGoal

func (b *BPlane) SetBTGoal(value, tolerance float64)

SetBTGoal sets to the B_T goal

func (*BPlane) SetLTOFGoal

func (b *BPlane) SetLTOFGoal(value, tolerance float64)

SetLTOFGoal sets to the LTOF goal

func (BPlane) String

func (b BPlane) String() string

type Cargo

type Cargo struct {
	Arrival time.Time // Time of arrival onto the tug
	*Spacecraft
}

Cargo defines a piece of cargo with arrival date and destination orbit

type CelestialObject

type CelestialObject struct {
	Name   string
	Radius float64

	SOI     float64 // With respect to the Sun
	J2      float64
	J3      float64
	J4      float64
	RotRate float64
	PP      *planetposition.V87Planet
	// contains filtered or unexported fields
}

CelestialObject defines a celestial object. Note: globe and elements may be nil; does not support satellites yet.

func CelestialObjectFromString

func CelestialObjectFromString(name string) (CelestialObject, error)

CelestialObjectFromString returns the object from its name

func (*CelestialObject) Equals

func (c *CelestialObject) Equals(b CelestialObject) bool

Equals returns whether the provided celestial object is the same.

func (CelestialObject) GM

func (c CelestialObject) GM() float64

GM returns μ (which is unexported because it's a lowercase letter)

func (*CelestialObject) HelioOrbit

func (c *CelestialObject) HelioOrbit(dt time.Time) Orbit

HelioOrbit returns the heliocentric position and velocity of this planet at a given time in equatorial coordinates. Note that the whole file is loaded. In fact, if we don't, then whoever is the first to call this function will set the Epoch at which the ephemeris are available, and that sucks.

func (CelestialObject) J

func (c CelestialObject) J(n uint8) float64

J returns the perturbing J_n factor for the provided n. Currently only J2 and J3 are supported.

func (CelestialObject) String

func (c CelestialObject) String() string

String implements the Stringer interface.

type CgBodyFrame

type CgBodyFrame struct {
	Type string `json:"type,omitempty"`
	Name string `json:"name,omitempty"`
}

CgBodyFrame definition.

func (*CgBodyFrame) String

func (c *CgBodyFrame) String() string

type CgCatalog

type CgCatalog struct {
	Version string     `json:"version"`
	Name    string     `json:"name"`
	Items   []*CgItems `json:"items"`
	Require []string   `json:"require,omitempty"`
}

CgCatalog definition.

func (*CgCatalog) String

func (c *CgCatalog) String() string

type CgGeometry

type CgGeometry struct {
	Type   string    `json:"type,omitempty"`
	Mesh   []float64 `json:"meshRotation,omitempty"`
	Size   float64   `json:"size,omitempty"`
	Source string    `json:"source,omitempty"`
}

CgGeometry definition.

type CgInterpolatedState

type CgInterpolatedState struct {
	JD       float64
	Position []float64
	Velocity []float64
}

CgInterpolatedState definition.

func ParseInterpolatedStates

func ParseInterpolatedStates(s string) []*CgInterpolatedState

ParseInterpolatedStates takes a string and converts that into a CgInterpolatedState.

func (*CgInterpolatedState) FromText

func (i *CgInterpolatedState) FromText(record []string)

FromText initializes from text. The `record` parameter must be an array of seven items.

func (*CgInterpolatedState) ToText

func (i *CgInterpolatedState) ToText() string

ToText converts to text for written output.

type CgItems

type CgItems struct {
	Class           string            `json:"class"`
	Name            string            `json:"name"`
	StartTime       string            `json:"startTime"`
	EndTime         string            `json:"endTime"`
	Center          string            `json:"center"`
	TrajectoryFrame string            `json:"trajectoryFrame"`
	Trajectory      *CgTrajectory     `json:"trajectory,omitempty"`
	Bodyframe       *CgBodyFrame      `json:"bodyFrame,omitempty"`
	Geometry        *CgGeometry       `json:"geometry,omitempty"`
	Label           *CgLabel          `json:"label,omitempty"`
	TrajectoryPlot  *CgTrajectoryPlot `json:"trajectoryPlot,omitempty"`
}

CgItems definition.

type CgLabel

type CgLabel struct {
	Color    []float64 `json:"color,omitempty"`
	FadeSize int       `json:"fadeSize,omitempty"`
	ShowText bool      `json:"showText,omitempty"`
}

CgLabel definition.

func (*CgLabel) String

func (l *CgLabel) String() string

type CgTrajectory

type CgTrajectory struct {
	Type   string `json:"type,omitempty"`
	Source string `json:"source,omitempty"`
}

CgTrajectory definition.

func (*CgTrajectory) String

func (t *CgTrajectory) String() string

func (*CgTrajectory) Validate

func (t *CgTrajectory) Validate() error

Validate validates a CgTrajectory.

type CgTrajectoryPlot

type CgTrajectoryPlot struct {
	Color       []float64 `json:"color,omitempty"`
	LineWidth   int       `json:"lineWidth,omitempty"`
	Duration    string    `json:"duration,omitempty"`
	Lead        string    `json:"lead,omitempty"`
	Fade        int       `json:"fade,omitempty"`
	SampleCount int       `json:"sampleCount,omitempty"`
}

CgTrajectoryPlot definition.

type Coast

type Coast struct {
	// contains filtered or unexported fields
}

Coast defines an thrust control law which does not thrust.

func (Coast) Control

func (cl Coast) Control(o Orbit) []float64

Control implements the ThrustControl interface.

func (Coast) Reason

func (cl Coast) Reason() string

Reason implements the ThrustControl interface.

func (Coast) Type

func (cl Coast) Type() ControlLaw

Type implements the ThrustControl interface.

type ControlLaw

type ControlLaw uint8

ControlLaw defines an enum of control laws.

func (ControlLaw) String

func (cl ControlLaw) String() string

type ControlLawType

type ControlLawType uint8

ControlLawType defines the way to sum different Lyuapunov optimal CL

func (ControlLawType) String

func (meth ControlLawType) String() string

type CruiseToDistance

type CruiseToDistance struct {
	// contains filtered or unexported fields
}

CruiseToDistance is a type of waypoint which cruises until a given distance is reached from the central body.

func NewCruiseToDistance

func NewCruiseToDistance(distance float64, further bool, action *WaypointAction) *CruiseToDistance

NewCruiseToDistance defines a new cruising regime until a given distance is reached.

func (*CruiseToDistance) Action

func (wp *CruiseToDistance) Action() *WaypointAction

Action implements the Waypoint interface.

func (*CruiseToDistance) Cleared

func (wp *CruiseToDistance) Cleared() bool

Cleared implements the Waypoint interface.

func (*CruiseToDistance) String

func (wp *CruiseToDistance) String() string

String implements the Waypoint interface.

func (*CruiseToDistance) ThrustDirection

func (wp *CruiseToDistance) ThrustDirection(o Orbit, dt time.Time) (ThrustControl, bool)

ThrustDirection implements the Waypoint interface.

type EPS

type EPS interface {
	Drain(voltage, power uint, dt time.Time) error
}

EPS defines the interface for an electrical power subsystem.

type EPThruster

type EPThruster interface {
	// Returns the minimum power and voltage requirements for this EPThruster.
	Min() (voltage, power uint)
	// Returns the max power and voltage requirements for this EPThruster.
	Max() (voltage, power uint)
	// Returns the thrust in Newtons and isp consumed in seconds.
	Thrust(voltage, power uint) (thrust, isp float64)
}

EPThruster defines a EPThruster interface.

type ExportConfig

type ExportConfig struct {
	Filename     string
	Cosmo        bool
	AsCSV        bool
	Timestamp    bool
	CSVAppend    func(st State) string // Custom export (do not include leading comma)
	CSVAppendHdr func() string         // Header for the custom export
}

ExportConfig configures the exporting of the simulation.

func (ExportConfig) IsUseless

func (c ExportConfig) IsUseless() bool

IsUseless returns whether this config doesn't actually do anything.

type GenericCL

type GenericCL struct {
	// contains filtered or unexported fields
}

GenericCL partially defines a ThrustControl.

func (GenericCL) Reason

func (cl GenericCL) Reason() string

Reason implements the ThrustControl interface.

func (GenericCL) Type

func (cl GenericCL) Type() ControlLaw

Type implements the ThrustControl interface.

type GenericEP

type GenericEP struct {
	// contains filtered or unexported fields
}

GenericEP is a generic EP EPThruster.

func NewGenericEP

func NewGenericEP(thrust, isp float64) *GenericEP

NewGenericEP returns a generic electric prop EPThruster.

func (*GenericEP) Max

func (t *GenericEP) Max() (voltage, power uint)

Max implements the EPThruster interface.

func (*GenericEP) Min

func (t *GenericEP) Min() (voltage, power uint)

Min implements the EPThruster interface.

func (*GenericEP) Thrust

func (t *GenericEP) Thrust(voltage, power uint) (thrust, isp float64)

Thrust implements the EPThruster interface.

type HERMeS

type HERMeS struct{}

HERMeS is based on the NASA & Rocketdyne 12.5kW demo

func (*HERMeS) Max

func (t *HERMeS) Max() (voltage, power uint)

Max implements the EPThruster interface.

func (*HERMeS) Min

func (t *HERMeS) Min() (voltage, power uint)

Min implements the EPThruster interface.

func (*HERMeS) Thrust

func (t *HERMeS) Thrust(voltage, power uint) (thrust, isp float64)

Thrust implements the EPThruster interface.

type HohmannTransfer

type HohmannTransfer struct {
	// contains filtered or unexported fields
}

HohmannTransfer allows to perform an Hohmann transfer.

func NewHohmannTransfer

func NewHohmannTransfer(target Orbit, action *WaypointAction) *HohmannTransfer

NewHohmannTransfer defines a new Hohmann transfer

func (*HohmannTransfer) Action

func (wp *HohmannTransfer) Action() *WaypointAction

Action implements the Waypoint interface.

func (*HohmannTransfer) Cleared

func (wp *HohmannTransfer) Cleared() bool

Cleared implements the Waypoint interface.

func (*HohmannTransfer) String

func (wp *HohmannTransfer) String() string

String implements the Waypoint interface.

func (*HohmannTransfer) ThrustDirection

func (wp *HohmannTransfer) ThrustDirection(o Orbit, dt time.Time) (ThrustControl, bool)

ThrustDirection implements the optimal orbit target.

type HohmannΔv

type HohmannΔv struct {
	ΔvBurnInit, ΔvInit, ΔvFinal float64

	GenericCL
	// contains filtered or unexported fields
}

HohmannΔv computes the Δv needed to go from one orbit to another, and performs an instantaneous Δv.

func NewHohmannΔv

func NewHohmannΔv(target Orbit) HohmannΔv

NewHohmannΔv defines a new inversion control law.

func (*HohmannΔv) Control

func (cl *HohmannΔv) Control(o Orbit) []float64

Control implements the ThrustControl interface.

func (*HohmannΔv) Precompute

func (cl *HohmannΔv) Precompute(o Orbit)

Precompute computes and displays the Hohmann transfer orbit.

type Loiter

type Loiter struct {
	// contains filtered or unexported fields
}

Loiter is a type of waypoint which allows the vehicle to stay at a given position for a given duration.

func NewLoiter

func NewLoiter(duration time.Duration, action *WaypointAction) *Loiter

NewLoiter defines a new loitering waypoint, i.e. "wait until a given time".

func (*Loiter) Action

func (wp *Loiter) Action() *WaypointAction

Action implements the Waypoint interface.

func (*Loiter) Cleared

func (wp *Loiter) Cleared() bool

Cleared implements the Waypoint interface.

func (*Loiter) String

func (wp *Loiter) String() string

String implements the Waypoint interface.

func (*Loiter) ThrustDirection

func (wp *Loiter) ThrustDirection(o Orbit, dt time.Time) (ThrustControl, bool)

ThrustDirection implements the Waypoint interface.

type Maneuver

type Maneuver struct {
	R, N, C float64
	// contains filtered or unexported fields
}

Maneuver stores a maneuver in the VNC frame

func NewManeuver

func NewManeuver(R, N, C float64) Maneuver

func (Maneuver) String

func (m Maneuver) String() string

func (Maneuver) Δv

func (m Maneuver) Δv() float64

Δv returns the Δv in km/s

type Measurement

type Measurement struct {
	Visible                  bool    // Stores whether or not the attempted measurement was visible from the station.
	Range, RangeRate         float64 // Store the range and range rate
	TrueRange, TrueRangeRate float64 // Store the true range and range rate
	Timeθgst                 float64
	State                    State
	Station                  Station
}

Measurement stores a measurement of a station.

func (Measurement) CSV

func (m Measurement) CSV() string

CSV returns the data as CSV (does *not* include the new line)

func (Measurement) HTilde

func (m Measurement) HTilde() *mat64.Dense

HTilde returns the H tilde matrix for this given measurement.

func (Measurement) IsNil

func (m Measurement) IsNil() bool

IsNil returns the state vector as a mat64.Vector

func (Measurement) ShortCSV

func (m Measurement) ShortCSV() string

ShortCSV returns the noisy data as CSV (does *not* include the new line)

func (Measurement) StateVector

func (m Measurement) StateVector() *mat64.Vector

StateVector returns the state vector as a mat64.Vector

func (Measurement) String

func (m Measurement) String() string

type Mission

type Mission struct {
	Vehicle                    *Spacecraft  // As pointer because SC may be altered during propagation.
	Orbit                      *Orbit       // As pointer because the orbit changes during propagation.
	Φ                          *mat64.Dense // STM
	StartDT, StopDT, CurrentDT time.Time
	// contains filtered or unexported fields
}

Mission defines a mission and does the propagation.

func NewMission

func NewMission(s *Spacecraft, o *Orbit, start, end time.Time, perts Perturbations, computeSTM bool, conf ExportConfig) *Mission

NewMission is the same as NewPreciseMission with the default step size.

func NewPreciseMission

func NewPreciseMission(s *Spacecraft, o *Orbit, start, end time.Time, perts Perturbations, step time.Duration, computeSTM bool, conf ExportConfig) *Mission

NewPreciseMission returns a new Mission instance with custom provided time step.

func (*Mission) Func

func (a *Mission) Func(t float64, f []float64) (fDot []float64)

Func is the integration function using Gaussian VOP as per Ruggiero et al. 2011.

func (*Mission) GetState

func (a *Mission) GetState() (s []float64)

GetState returns the state for the integrator for the Gaussian VOP.

func (*Mission) LogStatus

func (a *Mission) LogStatus()

LogStatus returns the status of the propagation and vehicle.

func (*Mission) Propagate

func (a *Mission) Propagate()

Propagate starts the propagation.

func (*Mission) PropagateUntil

func (a *Mission) PropagateUntil(dt time.Time, autoClose bool)

PropagateUntil propagates until the given time is reached.

func (*Mission) RegisterStateChan

func (a *Mission) RegisterStateChan(c chan (State))

RegisterStateChan appends a new channel where to publish states as they are computed WARNING: One *should not* write to this channel, but no check is done. Don't be dumb.

func (*Mission) SetState

func (a *Mission) SetState(t float64, s []float64)

SetState sets the updated state.

func (*Mission) Stop

func (a *Mission) Stop(t float64) bool

Stop implements the stop call of the integrator. To stop the propagation, call StopPropagation().

func (*Mission) StopPropagation

func (a *Mission) StopPropagation()

StopPropagation is used to stop the propagation before it is completed.

type OptimalThrust

type OptimalThrust struct {
	GenericCL
	// contains filtered or unexported fields
}

OptimalThrust is an optimal thrust.

func (OptimalThrust) Control

func (cl OptimalThrust) Control(o Orbit) []float64

Control implements the ThrustControl interface.

type OptimalΔOrbit

type OptimalΔOrbit struct {
	Initd bool

	Distanceε, Eccentricityε, Angleε float64
	GenericCL
	// contains filtered or unexported fields
}

OptimalΔOrbit combines all the control laws from Ruggiero et al.

func NewOptimalΔOrbit

func NewOptimalΔOrbit(target Orbit, method ControlLawType, laws []ControlLaw) *OptimalΔOrbit

NewOptimalΔOrbit generates a new OptimalΔOrbit based on the provided target orbit.

func (*OptimalΔOrbit) Control

func (cl *OptimalΔOrbit) Control(o Orbit) []float64

Control implements the ThrustControl interface.

func (*OptimalΔOrbit) SetEpsilons

func (cl *OptimalΔOrbit) SetEpsilons(distanceε, eccentricityε, angleε float64)

SetEpsilons changes the target of this optimal control

func (*OptimalΔOrbit) SetTarget

func (cl *OptimalΔOrbit) SetTarget(target Orbit)

SetTarget changes the target of this optimal control

func (*OptimalΔOrbit) String

func (cl *OptimalΔOrbit) String() string

type Orbit

type Orbit struct {
	Origin CelestialObject // Orbit origin
	// contains filtered or unexported fields
}

Orbit defines an orbit via its orbital elements.

func NewOrbitFromOE

func NewOrbitFromOE(a, e, i, Ω, ω, ν float64, c CelestialObject) *Orbit

NewOrbitFromOE creates an orbit from the orbital elements. WARNING: Angles must be in degrees not radians.

func NewOrbitFromRV

func NewOrbitFromRV(R, V []float64, c CelestialObject) *Orbit

NewOrbitFromRV returns orbital elements from the R and V vectors. Needed for prop

func (Orbit) Apoapsis

func (o Orbit) Apoapsis() float64

Apoapsis returns the apoapsis.

func (Orbit) CosΦfpa

func (o Orbit) CosΦfpa() float64

CosΦfpa returns the cosine of the flight path angle. WARNING: As per Vallado page 105, *do not* use math.Acos(o.CosΦfpa()) to get the flight path angle as you'll have a quadran problem. Instead use math.Atan2(o.SinΦfpa(), o.CosΦfpa()).

func (*Orbit) Elements

func (o *Orbit) Elements() (a, e, i, Ω, ω, ν, λ, tildeω, u float64)

Elements returns the nine orbital elements in radians which work for circular and elliptical orbits

func (Orbit) Energyξ

func (o Orbit) Energyξ() float64

Energyξ returns the specific mechanical energy ξ.

func (Orbit) Equals

func (o Orbit) Equals(o1 Orbit) (bool, error)

Equals returns whether two orbits are identical with free true anomaly. Use StrictlyEquals to also check true anomaly.

func (Orbit) EqualsWithin

func (o Orbit) EqualsWithin(o1 Orbit, distanceε, eccentricityε, angleε float64) (bool, error)

EqualsWithin returns whether two orbits are identical with free true anomaly and within provided bounds.

func (Orbit) H

func (o Orbit) H() []float64

H returns the orbital angular momentum vector.

func (Orbit) HNorm

func (o Orbit) HNorm() float64

HNorm returns the norm of orbital angular momentum.

func (Orbit) MeanAnomaly

func (o Orbit) MeanAnomaly() float64

MeanAnomaly returns the mean anomaly for hyperbolic orbits only.

func (Orbit) Periapsis

func (o Orbit) Periapsis() float64

Periapsis returns the apoapsis.

func (Orbit) Period

func (o Orbit) Period() time.Duration

Period returns the period of this orbit.

func (Orbit) R

func (o Orbit) R() (R []float64)

R returns the radius vector.

func (Orbit) RNorm

func (o Orbit) RNorm() float64

RNorm returns the norm of the radius vector, but without computing the radius vector. If only the norm is needed, it is encouraged to use this function instead of norm(o.R()).

func (Orbit) RV

func (o Orbit) RV() ([]float64, []float64)

RV helps with the cache.

func (Orbit) SemiParameter

func (o Orbit) SemiParameter() float64

SemiParameter returns the apoapsis.

func (Orbit) SinCosE

func (o Orbit) SinCosE() (sinE, cosE float64)

SinCosE returns the eccentric anomaly trig functions (sin and cos).

func (Orbit) SinΦfpa

func (o Orbit) SinΦfpa() float64

SinΦfpa returns the cosine of the flight path angle. WARNING: As per Vallado page 105, *do not* use math.Asin(o.SinΦfpa()) to get the flight path angle as you'll have a quadran problem. Instead use math.Atan2(o.SinΦfpa(), o.CosΦfpa()).

func (Orbit) StrictlyEquals

func (o Orbit) StrictlyEquals(o1 Orbit) (bool, error)

StrictlyEquals returns whether two orbits are identical.

func (Orbit) String

func (o Orbit) String() string

String implements the stringer interface (hence the value receiver)

func (*Orbit) ToXCentric

func (o *Orbit) ToXCentric(b CelestialObject, dt time.Time)

ToXCentric converts this orbit the provided celestial object centric equivalent. Panics if the vehicle is not within the SOI of the object. Panics if already in this frame.

func (Orbit) V

func (o Orbit) V() (V []float64)

V returns the velocity vector.

func (Orbit) VNorm

func (o Orbit) VNorm() float64

VNorm returns the norm of the velocity vector, but without computing the velocity vector. If only the norm is needed, it is encouraged to use this function instead of norm(o.GetV()).

type OrbitEstimate

type OrbitEstimate struct {
	Φ      *mat64.Dense  // STM
	Orbit  Orbit         // estimated orbit
	Perts  Perturbations // perturbations to account for
	StopDT time.Time     // end time of te integration
	// contains filtered or unexported fields
}

OrbitEstimate is an ode.Integrable which allows to propagate an orbit via its initial estimate.

func NewOrbitEstimate

func NewOrbitEstimate(n string, o Orbit, p Perturbations, epoch time.Time, step time.Duration) *OrbitEstimate

NewOrbitEstimate returns a new Estimate of an orbit given the perturbations to be taken into account. The only supported state is [\vec{r} \vec{v}]T (for now at least).

func (*OrbitEstimate) Func

func (e *OrbitEstimate) Func(t float64, f []float64) (fDot []float64)

Func does the math. Returns a new state.

func (*OrbitEstimate) GetState

func (e *OrbitEstimate) GetState() []float64

GetState gets the state.

func (*OrbitEstimate) PropagateUntil

func (e *OrbitEstimate) PropagateUntil(dt time.Time)

PropagateUntil propagates until the given time is reached.

func (*OrbitEstimate) SetState

func (e *OrbitEstimate) SetState(t float64, s []float64)

SetState sets the next state at time t.

func (*OrbitEstimate) State

func (e *OrbitEstimate) State() State

State returns the latest state

func (*OrbitEstimate) Stop

func (e *OrbitEstimate) Stop(t float64) bool

Stop returns whether we should stop the integration.

type OrbitNoise

type OrbitNoise struct {
	// contains filtered or unexported fields
}

OrbitNoise defines a new orbit noise applied as a perturbations. Use case is for generating datasets for filtering.

func NewOrbitNoise

func NewOrbitNoise(probability, sigmaPosition, sigmaVelocity float64) OrbitNoise

func (OrbitNoise) Generate

func (n OrbitNoise) Generate() (rtn []float64)

type OrbitTarget

type OrbitTarget struct {
	// contains filtered or unexported fields
}

OrbitTarget allows to target an orbit.

func NewOrbitTarget

func NewOrbitTarget(target Orbit, action *WaypointAction, meth ControlLawType, laws ...ControlLaw) *OrbitTarget

NewOrbitTarget defines a new orbit target.

func (*OrbitTarget) Action

func (wp *OrbitTarget) Action() *WaypointAction

Action implements the Waypoint interface.

func (*OrbitTarget) Cleared

func (wp *OrbitTarget) Cleared() bool

Cleared implements the Waypoint interface.

func (*OrbitTarget) SetEpsilons

func (wp *OrbitTarget) SetEpsilons(distanceε, eccentricityε, angleε float64)

SetEpsilons allows to set the epsilons of the control law

func (*OrbitTarget) SetExport

func (wp *OrbitTarget) SetExport(t *ThurstAngleExport)

ThrustDirection implements the optimal orbit target.

func (*OrbitTarget) String

func (wp *OrbitTarget) String() string

String implements the Waypoint interface.

func (*OrbitTarget) ThrustDirection

func (wp *OrbitTarget) ThrustDirection(o Orbit, dt time.Time) (ThrustControl, bool)

ThrustDirection implements the optimal orbit target.

type PPS1350

type PPS1350 struct{}

PPS1350 is the Snecma EPThruster used on SMART-1.

func (*PPS1350) Max

func (t *PPS1350) Max() (voltage, power uint)

Max implements the EPThruster interface.

func (*PPS1350) Min

func (t *PPS1350) Min() (voltage, power uint)

Min implements the EPThruster interface.

func (*PPS1350) Thrust

func (t *PPS1350) Thrust(voltage, power uint) (thrust, isp float64)

Thrust implements the EPThruster interface.

type PPS5000

type PPS5000 struct{}

PPS5000 is the latest Snecma EPThruster.

func (*PPS5000) Max

func (t *PPS5000) Max() (voltage, power uint)

Max implements the EPThruster interface.

func (*PPS5000) Min

func (t *PPS5000) Min() (voltage, power uint)

Min implements the EPThruster interface.

func (*PPS5000) Thrust

func (t *PPS5000) Thrust(voltage, power uint) (thrust, isp float64)

Thrust implements the EPThruster interface.

type Perturbations

type Perturbations struct {
	Jn             uint8            // Factors to be used (only up to 4 supported)
	PerturbingBody *CelestialObject // The 3rd body which is perturbating the spacecraft.
	AutoThirdBody  bool             // Automatically determine what is the 3rd body based on distance and mass
	Drag           bool             // Set to true to use the Spacecraft's Drag for everything including STM computation
	Noise          OrbitNoise
	Arbitrary      func(o Orbit) []float64 // Additional arbitrary pertubation.
}

Perturbations defines how to handle perturbations during the propagation.

func (Perturbations) Perturb

func (p Perturbations) Perturb(o Orbit, dt time.Time, sc Spacecraft) []float64

Perturb returns the perturbing state vector based on the kind of propagation being used. For example, if using Cartesian, it'll return the impact on the R vector. If Gaussian, it'll return the impact on Ω, ω, ν (later for ν...).

func (Perturbations) STMSize

func (p Perturbations) STMSize() (r, c int)

STMSize returns the size of the STM

type ReachDistance

type ReachDistance struct {
	// contains filtered or unexported fields
}

ReachDistance is a type of waypoint which thrusts until a given distance is reached from the central body.

func NewOutwardSpiral

func NewOutwardSpiral(body CelestialObject, action *WaypointAction) *ReachDistance

NewOutwardSpiral defines a new outward spiral from a celestial object.

func NewReachDistance

func NewReachDistance(distance float64, further bool, action *WaypointAction) *ReachDistance

NewReachDistance defines a new spiral until a given distance is reached.

func (*ReachDistance) Action

func (wp *ReachDistance) Action() *WaypointAction

Action implements the Waypoint interface.

func (*ReachDistance) Cleared

func (wp *ReachDistance) Cleared() bool

Cleared implements the Waypoint interface.

func (*ReachDistance) String

func (wp *ReachDistance) String() string

String implements the Waypoint interface.

func (*ReachDistance) ThrustDirection

func (wp *ReachDistance) ThrustDirection(o Orbit, dt time.Time) (ThrustControl, bool)

ThrustDirection implements the Waypoint interface.

type Spacecraft

type Spacecraft struct {
	Name        string                 // Name of spacecraft
	DryMass     float64                // DryMass of spacecraft (in kg)
	FuelMass    float64                // FuelMass of spacecraft (in kg) (will panic if runs out of fuel)
	EPS         EPS                    // EPS definition, needed for the EPThrusters.
	EPThrusters []EPThruster           // All available EP EPThrusters
	ChemProp    bool                   // Set to true to allow Hohmann Transfers.
	Cargo       []*Cargo               // All onboard cargo
	WayPoints   []Waypoint             // All waypoints of the tug
	Maneuvers   map[time.Time]Maneuver // List of maneuvers.
	FuncQ       []func()

	Drag float64
	// contains filtered or unexported fields
}

Spacecraft defines a new spacecraft.

func NewEmptySC

func NewEmptySC(name string, mass uint) *Spacecraft

NewEmptySC returns a spacecraft with no cargo and no EPThrusters.

func NewSpacecraft

func NewSpacecraft(name string, dryMass, fuelMass float64, eps EPS, prop []EPThruster, impulse bool, payload []*Cargo, wp []Waypoint) *Spacecraft

NewSpacecraft returns a spacecraft with initialized function queue and logger.

func (*Spacecraft) Accelerate

func (sc *Spacecraft) Accelerate(dt time.Time, o *Orbit) (Δv []float64, fuel float64)

Accelerate returns the applied velocity (in km/s) at a given orbital position and date time, and the fuel used. Keeps track of the thrust applied by all EPThrusters, with necessary optimizations based on next waypoint, *but* does not update the fuel available (as it needs to be integrated).

func (*Spacecraft) LogInfo

func (sc *Spacecraft) LogInfo()

LogInfo logs the information of this spacecraft.

func (*Spacecraft) Mass

func (sc *Spacecraft) Mass(dt time.Time) (m float64)

Mass returns the given vehicle mass based on the provided UTC date time.

func (*Spacecraft) ToXCentric

func (sc *Spacecraft) ToXCentric(body CelestialObject, dt time.Time, o *Orbit) func()

ToXCentric switches the propagation from the current origin to a new one and logs the change.

type State

type State struct {
	DT    time.Time
	SC    Spacecraft
	Orbit Orbit
	Φ     *mat64.Dense // STM
	// contains filtered or unexported fields
}

State stores propagated state.

func (State) Vector

func (s State) Vector() *mat64.Vector

Vector returns the orbit vector with position and velocity.

type Station

type Station struct {
	Name                       string
	R, V                       []float64 // position and velocity in ECEF
	LatΦ, Longθ                float64   // these are stored in radians!
	Altitude, Elevation        float64
	RangeNoise, RangeRateNoise *distmv.Normal // Station noise
	Planet                     CelestialObject
	// contains filtered or unexported fields
}

Station defines a ground station.

func BuiltinStationFromName

func BuiltinStationFromName(name string) Station

func NewSpecialStation

func NewSpecialStation(name string, altitude, elevation, latΦ, longθ, σρ, σρDot float64, rowsH int) Station

NewSpecialStation same as NewStation but can specify the rows of H.

func NewStation

func NewStation(name string, altitude, elevation, latΦ, longθ, σρ, σρDot float64) Station

NewStation returns a new station. Angles in degrees.

func (Station) PerformMeasurement

func (s Station) PerformMeasurement(θgst float64, state State) Measurement

PerformMeasurement returns whether the SC is visible, and if so, the measurement.

func (Station) RangeElAz

func (s Station) RangeElAz(rECEF []float64) (ρECEF []float64, ρ, el, az float64)

RangeElAz returns the range (in the SEZ frame), elevation and azimuth (in degrees) of a given R vector in ECEF.

func (Station) String

func (s Station) String() string

type Tangential

type Tangential struct {
	// contains filtered or unexported fields
}

Tangential defines a tangential thrust control law

func (Tangential) Control

func (cl Tangential) Control(o Orbit) []float64

Control implements the ThrustControl interface.

func (Tangential) Reason

func (cl Tangential) Reason() string

Reason implements the ThrustControl interface.

func (Tangential) Type

func (cl Tangential) Type() ControlLaw

Type implements the ThrustControl interface.

type ThrustControl

type ThrustControl interface {
	Control(o Orbit) []float64
	Type() ControlLaw
	Reason() string
}

ThrustControl defines a thrust control interface.

func NewOptimalThrust

func NewOptimalThrust(cl ControlLaw, reason string) ThrustControl

NewOptimalThrust returns a new optimal Δe.

type ThurstAngleExport

type ThurstAngleExport struct {
	// contains filtered or unexported fields
}

ThurstAngleExport configures the exporting of the simulation. Exports in CSV in the format of "datetime,alpha,beta"

func NewThurstAngleExport

func NewThurstAngleExport(filename string, timestamped bool) *ThurstAngleExport

func (*ThurstAngleExport) Store

func (t *ThurstAngleExport) Store(dt time.Time, alpha, beta float64)

Store appends a new tuple of values for the given time. Note that if the previous time is the same as the new one, the new entry is *ignored*.

type TimedEPS

type TimedEPS struct {
	// contains filtered or unexported fields
}

TimedEPS sets a hard limit on how long (time-wise) the EPS can deliver any power.

func NewTimedEPS

func NewTimedEPS(charge, discharge time.Duration) (t *TimedEPS)

NewTimedEPS creates a new TimedEPS.

func (*TimedEPS) Drain

func (t *TimedEPS) Drain(voltage, power uint, dt time.Time) error

Drain implements the EPS subsystem.

type ToElliptical

type ToElliptical struct {
	// contains filtered or unexported fields
}

ToElliptical decelerates the vehicle until its orbit is elliptical.

func NewToElliptical

func NewToElliptical(action *WaypointAction) *ToElliptical

NewToElliptical defines a ToElliptical waypoint.

func (*ToElliptical) Action

func (wp *ToElliptical) Action() *WaypointAction

Action implements the Waypoint interface.

func (*ToElliptical) Cleared

func (wp *ToElliptical) Cleared() bool

Cleared implements the Waypoint interface.

func (*ToElliptical) String

func (wp *ToElliptical) String() string

String implements the Waypoint interface.

func (*ToElliptical) ThrustDirection

func (wp *ToElliptical) ThrustDirection(o Orbit, dt time.Time) (ThrustControl, bool)

ThrustDirection implements the optimal orbit target.

type ToHyperbolic

type ToHyperbolic struct {
	// contains filtered or unexported fields
}

ToHyperbolic accelerates the vehicle until its orbit is elliptical.

func NewToHyperbolic

func NewToHyperbolic(action *WaypointAction) *ToHyperbolic

NewToHyperbolic defines a ToElliptical waypoint.

func (*ToHyperbolic) Action

func (wp *ToHyperbolic) Action() *WaypointAction

Action implements the Waypoint interface.

func (*ToHyperbolic) Cleared

func (wp *ToHyperbolic) Cleared() bool

Cleared implements the Waypoint interface.

func (*ToHyperbolic) String

func (wp *ToHyperbolic) String() string

String implements the Waypoint interface.

func (*ToHyperbolic) ThrustDirection

func (wp *ToHyperbolic) ThrustDirection(o Orbit, dt time.Time) (ThrustControl, bool)

ThrustDirection implements the optimal orbit target.

type TransferType

type TransferType uint8

TransferType defines the type of Lambert transfer

const (
	// TTypeAuto lets the Lambert solver determine the type
	TTypeAuto TransferType = iota + 1
	// TType1 is transfer of type 1 (zero revolution, short way)
	TType1
	// TType2 is transfer of type 2 (zero revolution, long way)
	TType2
	// TType3 is transfer of type 3 (one revolutions, short way)
	TType3
	// TType4 is transfer of type 4 (one revolutions, long way)
	TType4
)

func TransferTypeFromInt

func TransferTypeFromInt(ttype int) TransferType

func (TransferType) Longway

func (t TransferType) Longway() bool

Longway returns whether or not this is the long way.

func (TransferType) Revs

func (t TransferType) Revs() float64

Revs returns the number of revolutions given the type.

func (TransferType) String

func (t TransferType) String() string

type UnlimitedEPS

type UnlimitedEPS struct{}

UnlimitedEPS drain as much as you want, always.

func NewUnlimitedEPS

func NewUnlimitedEPS() (e *UnlimitedEPS)

NewUnlimitedEPS returns a dream-like EPS.

func (*UnlimitedEPS) Drain

func (e *UnlimitedEPS) Drain(voltage, power uint, dt time.Time) error

Drain implements the interface.

type VX200

type VX200 struct{}

VX200 is a VASIMR 200 kW EPThruster. Data from http://www.adastrarocket.com/Jared_IEPC11-154.pdf

func (*VX200) Max

func (t *VX200) Max() (voltage, power uint)

Max implements the EPThruster interface.

func (*VX200) Min

func (t *VX200) Min() (voltage, power uint)

Min implements the EPThruster interface.

func (*VX200) Thrust

func (t *VX200) Thrust(voltage, power uint) (thrust, isp float64)

Thrust implements the EPThruster interface.

type Waypoint

type Waypoint interface {
	Cleared() bool // returns whether waypoint has been reached
	Action() *WaypointAction
	ThrustDirection(Orbit, time.Time) (ThrustControl, bool)
	String() string
}

Waypoint defines the Waypoint interface.

type WaypointAction

type WaypointAction struct {
	Type  WaypointActionEnum
	Cargo *Cargo
}

WaypointAction defines what happens when a given waypoint is reached.

type WaypointActionEnum

type WaypointActionEnum uint8

WaypointActionEnum defines the possible waypoint actions.

const (
	// ADDCARGO is a waypoint action associated to a piece of cargo
	ADDCARGO WaypointActionEnum = iota + 1
	// DROPCARGO is the opposite of ADD
	DROPCARGO
	// REFEARTH switches the orbit reference to the Earth
	REFEARTH
	// REFMARS switches the orbit reference to Mars
	REFMARS
	//REFSUN switches the orbit reference to the Sun
	REFSUN
)

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
t or T : Toggle theme light dark auto
y or Y : Canonical URL