LunarSouthNode and Sidereal Cusps
- Refactors Coordinate for the addition of .tropical and sidereal properties - Adds .tropical and sidereal properties to Cusp - Updates README.md - Adds LunarSouthNode
This commit is contained in:
parent
27b5a1de31
commit
20a97e6c33
|
@ -56,6 +56,15 @@
|
|||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||
shouldUseLaunchSchemeArgsEnv = "YES"
|
||||
codeCoverageEnabled = "YES">
|
||||
<CodeCoverageTargets>
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "SwissEphemeris"
|
||||
BuildableName = "SwissEphemeris"
|
||||
BlueprintName = "SwissEphemeris"
|
||||
ReferencedContainer = "container:">
|
||||
</BuildableReference>
|
||||
</CodeCoverageTargets>
|
||||
<Testables>
|
||||
<TestableReference
|
||||
skipped = "NO">
|
||||
|
|
28
README.md
28
README.md
|
@ -59,7 +59,7 @@ moonCoordinate.speedLatitude
|
|||
// The speed in distance (AU/day).
|
||||
moonCoordinate.speedDistance
|
||||
```
|
||||
Astrological information about the tropical zodiacal location of a celestial body is also available from the same `Coordinate` type.
|
||||
Astrological information about the tropical and sidereal zodiacal locations of a celestial body is also available from the same `Coordinate` type through the `tropical` and `sidereal` properties.
|
||||
|
||||
```swift
|
||||
// Date for 12.14.2019 13:39 UT/GMT
|
||||
|
@ -67,11 +67,11 @@ let date = Date(timeIntervalSince1970: 598023482.487818)
|
|||
// Astronomical and astrological information for the sun on December 14th 2019.
|
||||
let sunCoordinate = Coordinate<Planet>(planet: .sun, date: date)
|
||||
// This will return 21 Degrees Sagittarius ♐︎ 46' 49''.
|
||||
sunCoordinate.formatted
|
||||
sunCoordinate.tropical.formatted
|
||||
// It is also possible to get the degree, minute and second as properties of the Coordinate.
|
||||
let degree = sunCoordinate.degree
|
||||
let minute = sunCoordinate.minute
|
||||
let second = sunCoordinate.second
|
||||
let degree = sunCoordinate.tropical.degree
|
||||
let minute = sunCoordinate.tropical.minute
|
||||
let second = sunCoordinate.tropical.second
|
||||
```
|
||||
#### Astrological Aspect
|
||||
|
||||
|
@ -79,30 +79,30 @@ To create an aspect between two `CelestialBody` types use `Aspect`. `Transit`
|
|||
|
||||
```swift
|
||||
// Create a pair of celestial bodies.
|
||||
let moonTrueNode = Pair<Planet, LunarNode>(a: .moon, b: .trueNode)
|
||||
let moonTrueNode = Pair<Planet, LunarNorthNode>(a: .moon, b: .trueNode)
|
||||
// Transit contains start and end date properties.
|
||||
let transit = Transit(pair: moonTrueNode, date: Date(), orb: 8.0)
|
||||
```
|
||||
#### Astrological Houses
|
||||
|
||||
To get the house layout for a date, location, and house system create a `HouseCusps`. The `HouseSystem` determines the type of astrological house system that is set. All house `Cusp` properties can be used to create an `Aspect` with a `CelestialBody`.
|
||||
To get the house layout for a date, location, and house system create a `HouseCusps`. The `HouseSystem` determines the type of astrological house system that is set. All house `Cusp` properties can be used to create an `Aspect` with a `CelestialBody`. Zodiacal information for each cusp is available through the `tropical` and `sidereal` properties.
|
||||
|
||||
```swift
|
||||
/// Create a date and location
|
||||
// Create a date and location
|
||||
let now = Date()
|
||||
let latitude: Double = 37.5081153
|
||||
let longitude: Double = -122.2854528
|
||||
/// All house cusps, Ascendent, and MC are properties on `houses`.
|
||||
// All house cusps, Ascendent, and MC are properties on `houses`.
|
||||
let houses = HouseCusps(date: date, latitude: latitude, longitude: longitude, houseSystem: .placidus)
|
||||
/// Get the formatted astrological position.
|
||||
let ascendentFormatted = houses.ascendent.formatted
|
||||
/// Or the precise degree
|
||||
let degree = houses.ascendent.degree
|
||||
// Get the formatted sidereal astrological position.
|
||||
let ascendentFormatted = houses.ascendent.sidereal.formatted
|
||||
// Or the precise degree for the sidereal zodiac.
|
||||
let degree = houses.ascendent.sidereal.degree
|
||||
```
|
||||
|
||||
#### IPL Numbering
|
||||
|
||||
The `enum` types for `Planet`, `Asteroid`, and `LunarNode` correspond to IPL numbers in the ephemeris. Other celestial bodies such as stars as fictitious points still need to be added. The type numbering is not comprehensive, but can be easily extended to match the celestial body that is not available in the package. All of the types conform to the `CelestialBody` protocol which makes it so different categories of celestial points can be mapped to the tropical zodiac both in aspect and position.
|
||||
The `enum` types for `Planet`, `Asteroid`, and `LunarNorthNode` correspond to IPL numbers in the ephemeris. Other celestial bodies such as stars as fictitious points still need to be added. The type numbering is not comprehensive, but can be easily extended to match the celestial body that is not available in the package. All of the types conform to the `CelestialBody` protocol which makes it so different categories of celestial points can be mapped to the tropical zodiac both in aspect and position.
|
||||
|
||||
#### Batch Calculations
|
||||
|
||||
|
|
|
@ -29,7 +29,7 @@ public enum Aspect: Equatable, Hashable, Codable {
|
|||
public init?<T, U>(pair: Pair<T, U>, date: Date, orb: Double = 10.0) {
|
||||
let degreeA = Coordinate(body: pair.a, date: date)
|
||||
let degreeB = Coordinate(body: pair.b, date: date)
|
||||
self.init(a: degreeA.value, b: degreeB.value, orb: orb)
|
||||
self.init(a: degreeA.longitude, b: degreeB.longitude, orb: orb)
|
||||
}
|
||||
|
||||
/// Creates an optional `Aspect` between two degrees. If there is no aspect within the orb, then this initializer will return `nil`.
|
||||
|
|
|
@ -10,7 +10,7 @@ import CSwissEphemeris
|
|||
|
||||
|
||||
/// Models a `CelestialBody` point in the sky.
|
||||
public struct Coordinate<T: CelestialBody> {
|
||||
public struct Coordinate<T: CelestialBody>: ZodiacMappable {
|
||||
|
||||
/// The type of `CelestialBody`.
|
||||
public let body: T
|
||||
|
@ -33,24 +33,31 @@ public struct Coordinate<T: CelestialBody> {
|
|||
/// The pointer for the fixed star name.
|
||||
private var charPointer = UnsafeMutablePointer<CChar>.allocate(capacity: 1)
|
||||
|
||||
// MARK: - ZodiacMappable Properties
|
||||
|
||||
public let tropical: ZodiacCoordinate
|
||||
public let sidereal: ZodiacCoordinate
|
||||
|
||||
// MARK: - Initializers
|
||||
|
||||
/// Creates a `Coordinate`.
|
||||
/// - Parameters:
|
||||
/// - body: The `CelestialBody` for the placement.
|
||||
/// - date: The date for the location of the coordinate.
|
||||
public init(body: T, date: Date) {
|
||||
defer {
|
||||
pointer.deinitialize(count: 6)
|
||||
pointer.deallocate()
|
||||
if let star = body as? FixedStar {
|
||||
charPointer.deinitialize(count: star.rawValue.count)
|
||||
charPointer.deallocate()
|
||||
}
|
||||
}
|
||||
defer {
|
||||
pointer.deinitialize(count: 6)
|
||||
pointer.deallocate()
|
||||
if let star = body as? FixedStar {
|
||||
charPointer.deinitialize(count: star.rawValue.count)
|
||||
charPointer.deallocate()
|
||||
}
|
||||
}
|
||||
self.body = body
|
||||
self.date = date
|
||||
switch body.value {
|
||||
case let value as Int32:
|
||||
pointer.initialize(repeating: 0, count: 6)
|
||||
pointer.initialize(repeating: 0, count: 6)
|
||||
swe_calc_ut(date.julianDate(), value, SEFLG_SPEED, pointer, nil)
|
||||
case let value as String:
|
||||
charPointer.initialize(from: value, count: value.count)
|
||||
|
@ -65,51 +72,53 @@ public struct Coordinate<T: CelestialBody> {
|
|||
speedLongitude = pointer[3]
|
||||
speedLatitude = pointer[4]
|
||||
speedDistance = pointer[5]
|
||||
tropical = ZodiacCoordinate(value: longitude)
|
||||
sidereal = ZodiacCoordinate(value: longitude, offset: Ayanamsha()(for: date))
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - ZodiacCoordinate Conformance
|
||||
|
||||
extension Coordinate: ZodiacCoordinate {
|
||||
public var value: Double { longitude }
|
||||
}
|
||||
|
||||
// MARK: - Codable Conformance
|
||||
|
||||
extension Coordinate: Codable {
|
||||
|
||||
public enum CodingKeys: CodingKey {
|
||||
case body
|
||||
case date
|
||||
case longitude
|
||||
case latitude
|
||||
case distance
|
||||
case speedLongitude
|
||||
case speedLatitude
|
||||
case speedDistance
|
||||
}
|
||||
|
||||
public init(from decoder: Decoder) throws {
|
||||
let container = try decoder.container(keyedBy: CodingKeys.self)
|
||||
body = try container.decode(T.self, forKey: .body)
|
||||
date = try container.decode(Date.self, forKey: .date)
|
||||
longitude = try container.decode(Double.self, forKey: .longitude)
|
||||
latitude = try container.decode(Double.self, forKey: .latitude)
|
||||
distance = try container.decode(Double.self, forKey: .distance)
|
||||
speedLongitude = try container.decode(Double.self, forKey: .speedLongitude)
|
||||
speedLatitude = try container.decode(Double.self, forKey: .speedLatitude)
|
||||
speedDistance = try container.decode(Double.self, forKey: .speedDistance)
|
||||
}
|
||||
|
||||
public func encode(to encoder: Encoder) throws {
|
||||
var container = encoder.container(keyedBy: CodingKeys.self)
|
||||
try container.encode(body, forKey: .body)
|
||||
try container.encode(date, forKey: .date)
|
||||
try container.encode(longitude, forKey: .longitude)
|
||||
try container.encode(latitude, forKey: .latitude)
|
||||
try container.encode(distance, forKey: .distance)
|
||||
try container.encode(speedLongitude, forKey: .speedLongitude)
|
||||
try container.encode(speedLatitude, forKey: .speedLatitude)
|
||||
try container.encode(speedDistance, forKey: .speedDistance)
|
||||
}
|
||||
|
||||
public enum CodingKeys: CodingKey {
|
||||
case body
|
||||
case date
|
||||
case longitude
|
||||
case latitude
|
||||
case distance
|
||||
case speedLongitude
|
||||
case speedLatitude
|
||||
case speedDistance
|
||||
case tropicalCoordinate
|
||||
case siderealCoordinate
|
||||
}
|
||||
|
||||
public init(from decoder: Decoder) throws {
|
||||
let container = try decoder.container(keyedBy: CodingKeys.self)
|
||||
body = try container.decode(T.self, forKey: .body)
|
||||
date = try container.decode(Date.self, forKey: .date)
|
||||
longitude = try container.decode(Double.self, forKey: .longitude)
|
||||
latitude = try container.decode(Double.self, forKey: .latitude)
|
||||
distance = try container.decode(Double.self, forKey: .distance)
|
||||
speedLongitude = try container.decode(Double.self, forKey: .speedLongitude)
|
||||
speedLatitude = try container.decode(Double.self, forKey: .speedLatitude)
|
||||
speedDistance = try container.decode(Double.self, forKey: .speedDistance)
|
||||
tropical = try container.decode(ZodiacCoordinate.self, forKey: .tropicalCoordinate)
|
||||
sidereal = try container.decode(ZodiacCoordinate.self, forKey: .siderealCoordinate)
|
||||
}
|
||||
|
||||
public func encode(to encoder: Encoder) throws {
|
||||
var container = encoder.container(keyedBy: CodingKeys.self)
|
||||
try container.encode(body, forKey: .body)
|
||||
try container.encode(date, forKey: .date)
|
||||
try container.encode(longitude, forKey: .longitude)
|
||||
try container.encode(latitude, forKey: .latitude)
|
||||
try container.encode(distance, forKey: .distance)
|
||||
try container.encode(speedLongitude, forKey: .speedLongitude)
|
||||
try container.encode(speedLatitude, forKey: .speedLatitude)
|
||||
try container.encode(speedDistance, forKey: .speedDistance)
|
||||
try container.encode(tropical, forKey: .tropicalCoordinate)
|
||||
try container.encode(sidereal, forKey: .siderealCoordinate)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -8,18 +8,16 @@
|
|||
import Foundation
|
||||
|
||||
/// Models the point between two houses
|
||||
public struct Cusp {
|
||||
public struct Cusp: ZodiacMappable {
|
||||
|
||||
/// The degree of the coordinate
|
||||
public let value: Double
|
||||
public let tropical: ZodiacCoordinate
|
||||
public let sidereal: ZodiacCoordinate
|
||||
|
||||
/// Creates a `Cusp`.
|
||||
/// - Parameter value: The latitudinal degree to set.
|
||||
public init(value: Double) {
|
||||
self.value = value
|
||||
/// - Parameter date: The `Date` needed to map to the zodiacs.
|
||||
public init(value: Double, date: Date) {
|
||||
tropical = ZodiacCoordinate(value: value)
|
||||
sidereal = ZodiacCoordinate(value: value, offset: Ayanamsha()(for: date))
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - ZodiacCoordinate Conformance
|
||||
|
||||
extension Cusp: ZodiacCoordinate {}
|
||||
|
|
|
@ -65,19 +65,19 @@ public struct HouseCusps {
|
|||
}
|
||||
self.date = date
|
||||
swe_houses(date.julianDate(), latitude, longitude, houseSystem.rawValue, cuspPointer, ascendentPointer);
|
||||
ascendent = Cusp(value: ascendentPointer[0])
|
||||
midHeaven = Cusp(value: ascendentPointer[1])
|
||||
first = Cusp(value: cuspPointer[1])
|
||||
second = Cusp(value: cuspPointer[2])
|
||||
third = Cusp(value: cuspPointer[3])
|
||||
fourth = Cusp(value: cuspPointer[4])
|
||||
fifth = Cusp(value: cuspPointer[5])
|
||||
sixth = Cusp(value: cuspPointer[6])
|
||||
seventh = Cusp(value: cuspPointer[7])
|
||||
eighth = Cusp(value: cuspPointer[8])
|
||||
ninth = Cusp(value: cuspPointer[9])
|
||||
tenth = Cusp(value: cuspPointer[10])
|
||||
eleventh = Cusp(value: cuspPointer[11])
|
||||
twelfth = Cusp(value: cuspPointer[12])
|
||||
ascendent = Cusp(value: ascendentPointer[0], date: date)
|
||||
midHeaven = Cusp(value: ascendentPointer[1], date: date)
|
||||
first = Cusp(value: cuspPointer[1], date: date)
|
||||
second = Cusp(value: cuspPointer[2], date: date)
|
||||
third = Cusp(value: cuspPointer[3], date: date)
|
||||
fourth = Cusp(value: cuspPointer[4], date: date)
|
||||
fifth = Cusp(value: cuspPointer[5], date: date)
|
||||
sixth = Cusp(value: cuspPointer[6], date: date)
|
||||
seventh = Cusp(value: cuspPointer[7], date: date)
|
||||
eighth = Cusp(value: cuspPointer[8], date: date)
|
||||
ninth = Cusp(value: cuspPointer[9], date: date)
|
||||
tenth = Cusp(value: cuspPointer[10], date: date)
|
||||
eleventh = Cusp(value: cuspPointer[11], date: date)
|
||||
twelfth = Cusp(value: cuspPointer[12], date: date)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
//
|
||||
// LunarNode.swift
|
||||
// LunarNorthNode.swift
|
||||
//
|
||||
//
|
||||
// Created by Vincent Smithers on 15.02.21.
|
||||
|
@ -9,14 +9,14 @@ import Foundation
|
|||
|
||||
/// Models the lunar nodes.
|
||||
/// The the raw `Int32` values map to the IPL bodies.
|
||||
public enum LunarNode: Int32 {
|
||||
public enum LunarNorthNode: Int32 {
|
||||
case meanNode = 10
|
||||
case trueNode
|
||||
}
|
||||
|
||||
// MARK: - CelestialBody Conformance
|
||||
|
||||
extension LunarNode: CelestialBody {
|
||||
extension LunarNorthNode: CelestialBody {
|
||||
public var value: Int32 {
|
||||
rawValue
|
||||
}
|
|
@ -0,0 +1,22 @@
|
|||
//
|
||||
// LunarSouthNode.swift
|
||||
//
|
||||
//
|
||||
// Created by Vincent Smithers on 6/4/22.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
/// Maps the Moon's south node to the zodiacs.
|
||||
public struct LunarSouthNode: ZodiacMappable {
|
||||
|
||||
public let tropical: ZodiacCoordinate
|
||||
public let sidereal: ZodiacCoordinate
|
||||
|
||||
/// Creates a `LunarSouthNode`.
|
||||
/// - Parameter nodeCoordinate: The north lunar node coordinate.
|
||||
public init(nodeCoordinate: Coordinate<LunarNorthNode>) {
|
||||
tropical = ZodiacCoordinate(value: nodeCoordinate.longitude, offset: 180.0)
|
||||
sidereal = ZodiacCoordinate(value: nodeCoordinate.longitude, offset: 180.0 + Ayanamsha()(for: nodeCoordinate.date))
|
||||
}
|
||||
}
|
|
@ -1,42 +0,0 @@
|
|||
//
|
||||
// SiderealCoordinate.swift
|
||||
//
|
||||
//
|
||||
// Created by Vincent Smithers on 16.03.21.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
/// Models a sidereal coordinate on the Zodiac.
|
||||
public struct SiderealCoordinate<T: CelestialBody> {
|
||||
|
||||
///
|
||||
public let date: Date
|
||||
/// The coordinate to set
|
||||
private let coordinate: Coordinate<T>
|
||||
/// The ayanamsha value that determines the sidereal position
|
||||
private let ayanamshaValue: Double
|
||||
|
||||
/// Creates a `SiderealCoordinate`.
|
||||
/// - Parameter coordinate: The coordinate to set.
|
||||
/// - Parameter ayanamshaValue: The ayanamsha value that determines the sidereal position.
|
||||
public init(coordinate: Coordinate<T>, ayanamshaValue: Double = Ayanamsha.current) {
|
||||
self.date = coordinate.date
|
||||
self.coordinate = coordinate
|
||||
self.ayanamshaValue = ayanamshaValue
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - ZodiacCoordinate Conformance
|
||||
|
||||
extension SiderealCoordinate: ZodiacCoordinate {
|
||||
|
||||
public var value: Double {
|
||||
// If in the first degrees of Aries
|
||||
if coordinate.longitude - ayanamshaValue < 0 {
|
||||
return 360 - abs(coordinate.longitude - ayanamshaValue)
|
||||
} else {
|
||||
return coordinate.longitude - ayanamshaValue
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,48 +0,0 @@
|
|||
//
|
||||
// ZodiacCoordinate.swift
|
||||
//
|
||||
//
|
||||
// Created by Vincent Smithers on 11.02.21.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
/// Models a degree along the twelve signs of the tropical zodiac.
|
||||
/// https://www.astro.com/astrowiki/en/Tropical_Zodiac
|
||||
public protocol ZodiacCoordinate {
|
||||
/// The degree to of zodiac. Between 0 - 360.
|
||||
var value: Double { get }
|
||||
/// The sign that houses the degree.
|
||||
var sign: Zodiac { get }
|
||||
///
|
||||
var lunarMansion: LunarMansion { get }
|
||||
/// The degree within the sign. Between 0 - 30.
|
||||
var degree: Double { get }
|
||||
/// The minute value for the degree.
|
||||
var minute: Double { get}
|
||||
/// The second value for the degree.
|
||||
var second: Double { get }
|
||||
}
|
||||
|
||||
public extension ZodiacCoordinate {
|
||||
|
||||
var sign: Zodiac { Zodiac(rawValue: Int(value / 30))! }
|
||||
|
||||
var lunarMansion: LunarMansion { LunarMansion(rawValue: Int(value / 12.857142857142857)) ?? .batnAlHut }
|
||||
|
||||
var degree: Double { value.truncatingRemainder(dividingBy: 30) }
|
||||
|
||||
var minute: Double { value.truncatingRemainder(dividingBy: 1) * 60 }
|
||||
|
||||
var second: Double { minute.truncatingRemainder(dividingBy: 1) * 60 }
|
||||
}
|
||||
|
||||
public extension ZodiacCoordinate {
|
||||
/// A readable `String` formatting the degree, sign, minute and second
|
||||
var formatted: String {
|
||||
"\(Int(degree)) Degrees " +
|
||||
"\(sign.formatted) " +
|
||||
"\(Int(minute))' " +
|
||||
"\(Int(second))''"
|
||||
}
|
||||
}
|
|
@ -0,0 +1,16 @@
|
|||
//
|
||||
// ZodiacMappable.swift
|
||||
//
|
||||
//
|
||||
// Created by Vincent Smithers on 6/5/22.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
/// Interface for both mapping both zodiacs to coordinates.
|
||||
public protocol ZodiacMappable {
|
||||
/// The coordinate for the tropical zodiac.
|
||||
var tropical: ZodiacCoordinate { get }
|
||||
/// The coordinate for the sidereal zodiac.
|
||||
var sidereal: ZodiacCoordinate { get }
|
||||
}
|
|
@ -23,7 +23,7 @@ public enum Station<T: CelestialBody>: Equatable {
|
|||
/// - Parameter timeInterval: The interval of time to compare the coordinate longitude. The default is 24 hours.
|
||||
public init(coordinate: Coordinate<T>, timeInterval: Double = 60 * 60 * 24) {
|
||||
let modDate = coordinate.date.addingTimeInterval(timeInterval)
|
||||
switch coordinate.value - Coordinate(body: coordinate.body, date: modDate).value {
|
||||
switch coordinate.longitude - Coordinate(body: coordinate.body, date: modDate).longitude {
|
||||
case let x where x < 0:
|
||||
self = .direct
|
||||
case let x where x > 0:
|
||||
|
|
|
@ -0,0 +1,48 @@
|
|||
//
|
||||
// ZodiacCoordinate.swift
|
||||
//
|
||||
//
|
||||
// Created by Vincent Smithers on 11.02.21.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
/// Models a degree along the twelve signs of the zodiac.
|
||||
public struct ZodiacCoordinate: Codable {
|
||||
/// The degree to of zodiac. Between 0 - 360.
|
||||
public let value: Double
|
||||
/// The sign that houses the degree.
|
||||
public var sign: Zodiac { Zodiac(rawValue: Int(value / 30))! }
|
||||
///
|
||||
public var lunarMansion: LunarMansion { LunarMansion(rawValue: Int(value / 12.857142857142857)) ?? .batnAlHut }
|
||||
/// The degree within the sign. Between 0 - 30.
|
||||
public var degree: Double { value.truncatingRemainder(dividingBy: 30) }
|
||||
/// The minute value for the degree.
|
||||
public var minute: Double { value.truncatingRemainder(dividingBy: 1) * 60 }
|
||||
/// The second value for the degree.
|
||||
public var second: Double { minute.truncatingRemainder(dividingBy: 1) * 60 }
|
||||
|
||||
/// An internal initializer for creating a `ZodiacCoordinate`.
|
||||
/// - Parameter value: Must be between 0-360.
|
||||
init(value: Double) {
|
||||
self.value = value
|
||||
}
|
||||
|
||||
/// An internal initializer for creating a `ZodiacCoordinate` with an offset.
|
||||
/// - Parameters:
|
||||
/// - value: Must be between 0-360.
|
||||
/// - offset: The offset to set.
|
||||
init(value: Double, offset: Double) {
|
||||
self.value = value - offset >= 0 ? value - offset : 360.0 + value - offset
|
||||
}
|
||||
}
|
||||
|
||||
public extension ZodiacCoordinate {
|
||||
/// A readable `String` formatting the degree, sign, minute and second
|
||||
var formatted: String {
|
||||
"\(Int(degree)) Degrees " +
|
||||
"\(sign.formatted) " +
|
||||
"\(Int(minute))' " +
|
||||
"\(Int(second))''"
|
||||
}
|
||||
}
|
|
@ -6,209 +6,228 @@ final class CelestialBodyTests: XCTestCase {
|
|||
override func setUpWithError() throws {
|
||||
JPLFileManager.setEphemerisPath()
|
||||
}
|
||||
|
||||
func testSunZodiacCoordinate() {
|
||||
// 12.14.2019 13:39 UT/GMT
|
||||
let date = Date(timeIntervalSince1970: 598023482.487818)
|
||||
let sunCoordinate = Coordinate<Planet>(body: .sun, date: date)
|
||||
XCTAssertEqual(sunCoordinate.value, 261.7804948994796)
|
||||
XCTAssertEqual(sunCoordinate.sign, .sagittarius)
|
||||
XCTAssertEqual(sunCoordinate.formatted, "21 Degrees Sagittarius ♐︎ 46' 49''")
|
||||
XCTAssertEqual(Int(sunCoordinate.degree), 21)
|
||||
XCTAssertEqual(Int(sunCoordinate.minute), 46)
|
||||
XCTAssertEqual(Int(sunCoordinate.second), 49)
|
||||
}
|
||||
|
||||
func testSunZodiacCoordinate() {
|
||||
// 12.14.2019 13:39 UT/GMT
|
||||
let date = Date(timeIntervalSince1970: 598023482.487818)
|
||||
let sunCoordinate = Coordinate<Planet>(body: .sun, date: date)
|
||||
XCTAssertEqual(sunCoordinate.longitude, 261.7804948994796)
|
||||
XCTAssertEqual(sunCoordinate.tropical.sign, .sagittarius)
|
||||
XCTAssertEqual(sunCoordinate.tropical.formatted, "21 Degrees Sagittarius ♐︎ 46' 49''")
|
||||
XCTAssertEqual(Int(sunCoordinate.tropical.degree), 21)
|
||||
XCTAssertEqual(Int(sunCoordinate.tropical.minute), 46)
|
||||
XCTAssertEqual(Int(sunCoordinate.tropical.second), 49)
|
||||
}
|
||||
|
||||
func testMoonSiderealCoordinate() {
|
||||
let moonCoordinate = SiderealCoordinate(coordinate: Coordinate<Planet>(body: .moon, date: Mock.date),
|
||||
ayanamshaValue: Ayanamsha()(for: Mock.date))
|
||||
XCTAssertEqual(Int(moonCoordinate.value), 295)
|
||||
XCTAssertEqual(moonCoordinate.sign, .capricorn)
|
||||
XCTAssertEqual(moonCoordinate.formatted, "25 Degrees Capricorn ♑︎ 3' 16''")
|
||||
XCTAssertEqual(Int(moonCoordinate.degree), 25)
|
||||
XCTAssertEqual(Int(moonCoordinate.minute), 3)
|
||||
XCTAssertEqual(Int(moonCoordinate.second), 16)
|
||||
let moonCoordinate = Coordinate<Planet>(body: .moon, date: Mock.date)
|
||||
XCTAssertEqual(Int(moonCoordinate.sidereal.value), 295)
|
||||
XCTAssertEqual(moonCoordinate.sidereal.sign, .capricorn)
|
||||
XCTAssertEqual(moonCoordinate.sidereal.formatted, "25 Degrees Capricorn ♑︎ 3' 16''")
|
||||
XCTAssertEqual(Int(moonCoordinate.sidereal.degree), 25)
|
||||
XCTAssertEqual(Int(moonCoordinate.sidereal.minute), 3)
|
||||
XCTAssertEqual(Int(moonCoordinate.sidereal.second), 16)
|
||||
}
|
||||
|
||||
func testAsteroids() throws {
|
||||
let date = try XCTUnwrap(Mock.date(from: "2021-03-01T12:31:00-0800"))
|
||||
let chiron = Coordinate<Asteroid>(body: .chiron, date: date)
|
||||
XCTAssertEqual(Int(chiron.degree), 7)
|
||||
XCTAssertEqual(chiron.sign, .aries)
|
||||
XCTAssertEqual(Int(chiron.tropical.degree), 7)
|
||||
XCTAssertEqual(chiron.tropical.sign, .aries)
|
||||
let pholus = Coordinate<Asteroid>(body: .pholus, date: date)
|
||||
XCTAssertEqual(Int(pholus.degree), 5)
|
||||
XCTAssertEqual(pholus.sign, .capricorn)
|
||||
XCTAssertEqual(Int(pholus.tropical.degree), 5)
|
||||
XCTAssertEqual(pholus.tropical.sign, .capricorn)
|
||||
let ceres = Coordinate<Asteroid>(body: .ceres, date: date)
|
||||
XCTAssertEqual(Int(ceres.degree), 3)
|
||||
XCTAssertEqual(ceres.sign, .aries)
|
||||
XCTAssertEqual(Int(ceres.tropical.degree), 3)
|
||||
XCTAssertEqual(ceres.tropical.sign, .aries)
|
||||
let pallas = Coordinate<Asteroid>(body: .pallas, date: date)
|
||||
XCTAssertEqual(Int(pallas.degree), 28)
|
||||
XCTAssertEqual(pallas.sign, .aquarius)
|
||||
XCTAssertEqual(Int(pallas.tropical.degree), 28)
|
||||
XCTAssertEqual(pallas.tropical.sign, .aquarius)
|
||||
let juno = Coordinate<Asteroid>(body: .juno, date: date)
|
||||
XCTAssertEqual(Int(juno.degree), 19)
|
||||
XCTAssertEqual(juno.sign, .sagittarius)
|
||||
XCTAssertEqual(Int(juno.tropical.degree), 19)
|
||||
XCTAssertEqual(juno.tropical.sign, .sagittarius)
|
||||
let vesta = Coordinate<Asteroid>(body: .vesta, date: date)
|
||||
XCTAssertEqual(Int(vesta.degree), 15)
|
||||
XCTAssertEqual(vesta.sign, .virgo)
|
||||
XCTAssertEqual(Int(vesta.tropical.degree), 15)
|
||||
XCTAssertEqual(vesta.tropical.sign, .virgo)
|
||||
}
|
||||
|
||||
func testLunarNodes() throws {
|
||||
func testLunarNorthNodes() throws {
|
||||
let date = try XCTUnwrap(Mock.date(year: 2121, month: 1, day: 1, hour: 1, minute: 1, second: 1))
|
||||
let trueNode = Coordinate<LunarNode>(body: .trueNode, date: date)
|
||||
XCTAssertEqual(Int(trueNode.degree), 3)
|
||||
XCTAssertEqual(trueNode.sign, .aquarius)
|
||||
let meanNode = Coordinate<LunarNode>(body: .meanNode, date: date)
|
||||
XCTAssertEqual(Int(meanNode.degree), 4)
|
||||
XCTAssertEqual(meanNode.sign, .aquarius)
|
||||
let trueNode = Coordinate<LunarNorthNode>(body: .trueNode, date: date)
|
||||
XCTAssertEqual(Int(trueNode.tropical.degree), 3)
|
||||
XCTAssertEqual(trueNode.tropical.sign, .aquarius)
|
||||
let meanNode = Coordinate<LunarNorthNode>(body: .meanNode, date: date)
|
||||
XCTAssertEqual(Int(meanNode.tropical.degree), 4)
|
||||
XCTAssertEqual(meanNode.tropical.sign, .aquarius)
|
||||
}
|
||||
|
||||
func testPlanets() {
|
||||
for (index, planet) in Planet.allCases.enumerated() {
|
||||
switch index {
|
||||
case 0:
|
||||
XCTAssertEqual(planet.formatted, "☉ Sun")
|
||||
|
||||
func testLunarSouthNodes() throws {
|
||||
let date = try XCTUnwrap(Mock.date(year: 2121, month: 1, day: 1, hour: 1, minute: 1, second: 1))
|
||||
let trueNode = Coordinate<LunarNorthNode>(body: .trueNode, date: date)
|
||||
let trueSouthNode = LunarSouthNode(nodeCoordinate: trueNode)
|
||||
XCTAssertEqual(trueSouthNode.tropical.value + 180, trueNode.tropical.value)
|
||||
XCTAssertEqual(trueSouthNode.tropical.value - Ayanamsha().callAsFunction(for: date), trueSouthNode.sidereal.value)
|
||||
let meanNode = Coordinate<LunarNorthNode>(body: .meanNode, date: date)
|
||||
let meanSouthNode = LunarSouthNode(nodeCoordinate: meanNode)
|
||||
XCTAssertEqual(meanSouthNode.tropical.value + 180, meanNode.tropical.value)
|
||||
XCTAssertEqual(meanSouthNode.tropical.value - Ayanamsha().callAsFunction(for: date), meanSouthNode.sidereal.value)
|
||||
}
|
||||
|
||||
func testLunarSouthNodeOffset() {
|
||||
for n in 0...360 {
|
||||
let north = ZodiacCoordinate(value:Double(n))
|
||||
let south = ZodiacCoordinate(value: north.value, offset: 180.0)
|
||||
XCTAssertEqual(abs(north.value - south.value), 180.0)
|
||||
}
|
||||
}
|
||||
|
||||
func testPlanets() {
|
||||
for (index, planet) in Planet.allCases.enumerated() {
|
||||
switch index {
|
||||
case 0:
|
||||
XCTAssertEqual(planet.formatted, "☉ Sun")
|
||||
XCTAssertEqual(planet.symbol, "☉")
|
||||
case 1:
|
||||
XCTAssertEqual(planet.formatted, "☾ Moon")
|
||||
case 1:
|
||||
XCTAssertEqual(planet.formatted, "☾ Moon")
|
||||
XCTAssertEqual(planet.symbol, "☾")
|
||||
case 2:
|
||||
XCTAssertEqual(planet.formatted, "☿ Mercury")
|
||||
case 2:
|
||||
XCTAssertEqual(planet.formatted, "☿ Mercury")
|
||||
XCTAssertEqual(planet.symbol, "☿")
|
||||
case 3:
|
||||
XCTAssertEqual(planet.formatted, "♀ Venus")
|
||||
case 3:
|
||||
XCTAssertEqual(planet.formatted, "♀ Venus")
|
||||
XCTAssertEqual(planet.symbol, "♀")
|
||||
case 4:
|
||||
XCTAssertEqual(planet.formatted, "♂️Mars")
|
||||
case 4:
|
||||
XCTAssertEqual(planet.formatted, "♂️Mars")
|
||||
XCTAssertEqual(planet.symbol, "♂︎")
|
||||
case 5:
|
||||
XCTAssertEqual(planet.formatted, "♃ Jupiter")
|
||||
case 5:
|
||||
XCTAssertEqual(planet.formatted, "♃ Jupiter")
|
||||
XCTAssertEqual(planet.symbol, "♃")
|
||||
case 6:
|
||||
XCTAssertEqual(planet.formatted, "♄ Saturn")
|
||||
case 6:
|
||||
XCTAssertEqual(planet.formatted, "♄ Saturn")
|
||||
XCTAssertEqual(planet.symbol, "♄")
|
||||
case 7:
|
||||
XCTAssertEqual(planet.formatted, "♅ Uranus")
|
||||
case 7:
|
||||
XCTAssertEqual(planet.formatted, "♅ Uranus")
|
||||
XCTAssertEqual(planet.symbol, "♅")
|
||||
case 8:
|
||||
XCTAssertEqual(planet.formatted, "♆ Neptune")
|
||||
case 8:
|
||||
XCTAssertEqual(planet.formatted, "♆ Neptune")
|
||||
XCTAssertEqual(planet.symbol, "♆")
|
||||
case 9:
|
||||
XCTAssertEqual(planet.formatted, "♇ Pluto")
|
||||
case 9:
|
||||
XCTAssertEqual(planet.formatted, "♇ Pluto")
|
||||
XCTAssertEqual(planet.symbol, "♇")
|
||||
default:
|
||||
XCTFail("Failed because there are planets that not tested")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func testZodiac() {
|
||||
for (index, sign) in Zodiac.allCases.enumerated() {
|
||||
switch index {
|
||||
case 0:
|
||||
XCTAssertEqual(sign.formatted, "Aries ♈︎")
|
||||
default:
|
||||
XCTFail("Failed because there are planets that not tested")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func testZodiac() {
|
||||
for (index, sign) in Zodiac.allCases.enumerated() {
|
||||
switch index {
|
||||
case 0:
|
||||
XCTAssertEqual(sign.formatted, "Aries ♈︎")
|
||||
XCTAssertEqual(sign.symbol, "♈︎")
|
||||
case 1:
|
||||
XCTAssertEqual(sign.formatted, "Taurus ♉︎")
|
||||
case 1:
|
||||
XCTAssertEqual(sign.formatted, "Taurus ♉︎")
|
||||
XCTAssertEqual(sign.symbol, "♉︎")
|
||||
case 2:
|
||||
XCTAssertEqual(sign.formatted, "Gemini ♊︎")
|
||||
case 2:
|
||||
XCTAssertEqual(sign.formatted, "Gemini ♊︎")
|
||||
XCTAssertEqual(sign.symbol, "♊︎")
|
||||
case 3:
|
||||
XCTAssertEqual(sign.formatted, "Cancer ♋︎")
|
||||
case 3:
|
||||
XCTAssertEqual(sign.formatted, "Cancer ♋︎")
|
||||
XCTAssertEqual(sign.symbol, "♋︎")
|
||||
case 4:
|
||||
XCTAssertEqual(sign.formatted, "Leo ♌︎")
|
||||
case 4:
|
||||
XCTAssertEqual(sign.formatted, "Leo ♌︎")
|
||||
XCTAssertEqual(sign.symbol, "♌︎")
|
||||
case 5:
|
||||
XCTAssertEqual(sign.formatted, "Virgo ♍︎")
|
||||
case 5:
|
||||
XCTAssertEqual(sign.formatted, "Virgo ♍︎")
|
||||
XCTAssertEqual(sign.symbol, "♍︎")
|
||||
case 6:
|
||||
XCTAssertEqual(sign.formatted, "Libra ♎︎")
|
||||
case 6:
|
||||
XCTAssertEqual(sign.formatted, "Libra ♎︎")
|
||||
XCTAssertEqual(sign.symbol, "♎︎")
|
||||
case 7:
|
||||
XCTAssertEqual(sign.formatted, "Scorpio ♏︎")
|
||||
case 7:
|
||||
XCTAssertEqual(sign.formatted, "Scorpio ♏︎")
|
||||
XCTAssertEqual(sign.symbol, "♏︎")
|
||||
case 8:
|
||||
XCTAssertEqual(sign.formatted, "Sagittarius ♐︎")
|
||||
case 8:
|
||||
XCTAssertEqual(sign.formatted, "Sagittarius ♐︎")
|
||||
XCTAssertEqual(sign.symbol, "♐︎")
|
||||
case 9:
|
||||
XCTAssertEqual(sign.formatted, "Capricorn ♑︎")
|
||||
case 9:
|
||||
XCTAssertEqual(sign.formatted, "Capricorn ♑︎")
|
||||
XCTAssertEqual(sign.symbol, "♑︎")
|
||||
case 10:
|
||||
XCTAssertEqual(sign.formatted, "Aquarius ♒︎")
|
||||
case 10:
|
||||
XCTAssertEqual(sign.formatted, "Aquarius ♒︎")
|
||||
XCTAssertEqual(sign.symbol, "♒︎")
|
||||
case 11:
|
||||
XCTAssertEqual(sign.formatted, "Pisces ♓︎")
|
||||
case 11:
|
||||
XCTAssertEqual(sign.formatted, "Pisces ♓︎")
|
||||
XCTAssertEqual(sign.symbol, "♓︎")
|
||||
default:
|
||||
XCTFail("Failed because there are signs that are not tested")
|
||||
}
|
||||
}
|
||||
XCTAssertNil(Zodiac(rawValue: 12))
|
||||
}
|
||||
|
||||
func testAscendent() {
|
||||
let houseSystem = Mock.makeHouses()
|
||||
// Ascendent
|
||||
let ascCoordinate = houseSystem.ascendent
|
||||
XCTAssertEqual(ascCoordinate.sign, Zodiac.sagittarius)
|
||||
XCTAssertEqual(ascCoordinate.formatted, "2 Degrees Sagittarius ♐︎ 1' 49''")
|
||||
// MC
|
||||
let mc = houseSystem.midHeaven
|
||||
XCTAssertEqual(mc.sign, Zodiac.virgo)
|
||||
}
|
||||
|
||||
func testHouses() {
|
||||
let houseSystem = Mock.makeHouses()
|
||||
/// House Cusps
|
||||
XCTAssertEqual(houseSystem.first.sign, Zodiac.sagittarius)
|
||||
XCTAssertEqual(houseSystem.second.sign, Zodiac.capricorn)
|
||||
XCTAssertEqual(houseSystem.third.sign, Zodiac.aquarius)
|
||||
XCTAssertEqual(houseSystem.fourth.sign, Zodiac.pisces)
|
||||
XCTAssertEqual(houseSystem.fifth.sign, Zodiac.aries)
|
||||
XCTAssertEqual(houseSystem.sixth.sign, Zodiac.taurus)
|
||||
XCTAssertEqual(houseSystem.seventh.sign, Zodiac.gemini)
|
||||
XCTAssertEqual(houseSystem.eighth.sign, Zodiac.cancer)
|
||||
XCTAssertEqual(houseSystem.ninth.sign, Zodiac.leo)
|
||||
XCTAssertEqual(houseSystem.tenth.sign, Zodiac.virgo)
|
||||
XCTAssertEqual(houseSystem.eleventh.sign, Zodiac.libra)
|
||||
XCTAssertEqual(houseSystem.twelfth.sign, Zodiac.scorpio)
|
||||
}
|
||||
|
||||
func testAspects() {
|
||||
// Expect 64.0 relationship sextile
|
||||
default:
|
||||
XCTFail("Failed because there are signs that are not tested")
|
||||
}
|
||||
}
|
||||
XCTAssertNil(Zodiac(rawValue: 12))
|
||||
}
|
||||
|
||||
func testAscendent() {
|
||||
let houseSystem = Mock.makeHouses()
|
||||
// Ascendent
|
||||
let ascCoordinate = houseSystem.ascendent
|
||||
XCTAssertEqual(ascCoordinate.tropical.sign, Zodiac.sagittarius)
|
||||
XCTAssertEqual(ascCoordinate.tropical.formatted, "2 Degrees Sagittarius ♐︎ 1' 49''")
|
||||
// MC
|
||||
let mc = houseSystem.midHeaven
|
||||
XCTAssertEqual(mc.tropical.sign, Zodiac.virgo)
|
||||
}
|
||||
|
||||
func testHouses() {
|
||||
let houseSystem = Mock.makeHouses()
|
||||
/// House Cusps
|
||||
XCTAssertEqual(houseSystem.first.tropical.sign, Zodiac.sagittarius)
|
||||
XCTAssertEqual(houseSystem.second.tropical.sign, Zodiac.capricorn)
|
||||
XCTAssertEqual(houseSystem.third.tropical.sign, Zodiac.aquarius)
|
||||
XCTAssertEqual(houseSystem.fourth.tropical.sign, Zodiac.pisces)
|
||||
XCTAssertEqual(houseSystem.fifth.tropical.sign, Zodiac.aries)
|
||||
XCTAssertEqual(houseSystem.sixth.tropical.sign, Zodiac.taurus)
|
||||
XCTAssertEqual(houseSystem.seventh.tropical.sign, Zodiac.gemini)
|
||||
XCTAssertEqual(houseSystem.eighth.tropical.sign, Zodiac.cancer)
|
||||
XCTAssertEqual(houseSystem.ninth.tropical.sign, Zodiac.leo)
|
||||
XCTAssertEqual(houseSystem.tenth.tropical.sign, Zodiac.virgo)
|
||||
XCTAssertEqual(houseSystem.eleventh.tropical.sign, Zodiac.libra)
|
||||
XCTAssertEqual(houseSystem.twelfth.tropical.sign, Zodiac.scorpio)
|
||||
}
|
||||
|
||||
func testAspects() {
|
||||
// Expect 64.0 relationship sextile
|
||||
var aspect = Aspect(pair: Pair<Planet, Planet>(a: .jupiter, b: .moon), date: Mock.date, orb: 8.0)
|
||||
XCTAssertEqual(aspect?.remainder, 3.45)
|
||||
XCTAssertEqual(aspect, .sextile(3.45))
|
||||
XCTAssertEqual(aspect?.symbol, "﹡")
|
||||
// Expect reverse to be 64.0 sextile
|
||||
XCTAssertEqual(aspect, .sextile(3.45))
|
||||
XCTAssertEqual(aspect?.symbol, "﹡")
|
||||
// Expect reverse to be 64.0 sextile
|
||||
aspect = Aspect(pair: Pair<Planet, Planet>(a: .moon, b: .jupiter), date: Mock.date, orb: 8.0)
|
||||
XCTAssertEqual(aspect?.remainder, 3.45)
|
||||
XCTAssertEqual(aspect, .sextile(3.45))
|
||||
// Expect 3.0 relationship conjunction
|
||||
XCTAssertEqual(aspect?.remainder, 3.45)
|
||||
XCTAssertEqual(aspect, .sextile(3.45))
|
||||
// Expect 3.0 relationship conjunction
|
||||
aspect = Aspect(pair: Pair<Planet, Planet>(a: .sun, b: .pluto), date: Mock.date, orb: 8.0)
|
||||
XCTAssertEqual(aspect?.remainder, 3.05)
|
||||
XCTAssertEqual(aspect, .conjunction(3.05))
|
||||
XCTAssertEqual(aspect?.symbol, "☌")
|
||||
// Expect 171.0 relationship opposition
|
||||
XCTAssertEqual(aspect?.remainder, 3.05)
|
||||
XCTAssertEqual(aspect, .conjunction(3.05))
|
||||
XCTAssertEqual(aspect?.symbol, "☌")
|
||||
// Expect 171.0 relationship opposition
|
||||
aspect = Aspect(pair: Pair<Planet, Planet>(a: .mars, b: .jupiter), date: Mock.date, orb: 10)
|
||||
XCTAssertEqual(aspect?.remainder, -8.96)
|
||||
XCTAssertEqual(aspect, .opposition(-8.96))
|
||||
XCTAssertEqual(aspect?.symbol, "☍")
|
||||
// Expect 124.0 relationship trine
|
||||
XCTAssertEqual(aspect?.remainder, -8.96)
|
||||
XCTAssertEqual(aspect, .opposition(-8.96))
|
||||
XCTAssertEqual(aspect?.symbol, "☍")
|
||||
// Expect 124.0 relationship trine
|
||||
aspect = Aspect(pair: Pair<Planet, Planet>(a: .saturn, b: .jupiter), date: Mock.date, orb: 10)
|
||||
XCTAssertEqual(aspect?.remainder, 4.65)
|
||||
XCTAssertEqual(aspect, .trine(4.65))
|
||||
XCTAssertEqual(aspect?.symbol, "▵")
|
||||
// Expect 84.0 relationship square
|
||||
XCTAssertEqual(aspect?.remainder, 4.65)
|
||||
XCTAssertEqual(aspect, .trine(4.65))
|
||||
XCTAssertEqual(aspect?.symbol, "▵")
|
||||
// Expect 84.0 relationship square
|
||||
aspect = Aspect(pair: Pair<Planet, Planet>(a: .venus, b: .moon), date: Mock.date, orb: 10)
|
||||
XCTAssertEqual(aspect?.remainder, -5.07)
|
||||
XCTAssertEqual(aspect, .square(-5.07))
|
||||
XCTAssertEqual(aspect?.symbol, "◾️")
|
||||
// Expect no aspect
|
||||
XCTAssertNil(Aspect(pair: Pair<Planet, Planet>(a: .venus, b: .mars), date: Mock.date, orb: 5))
|
||||
}
|
||||
|
||||
XCTAssertEqual(aspect?.remainder, -5.07)
|
||||
XCTAssertEqual(aspect, .square(-5.07))
|
||||
XCTAssertEqual(aspect?.symbol, "◾️")
|
||||
// Expect no aspect
|
||||
XCTAssertNil(Aspect(pair: Pair<Planet, Planet>(a: .venus, b: .mars), date: Mock.date, orb: 5))
|
||||
}
|
||||
|
||||
func testAspectCount() {
|
||||
let sunAspects = Planet.allCases.compactMap {
|
||||
Aspect(pair: Pair<Planet, Planet>(a: .sun, b: $0),date: Mock.date, orb: 10)
|
||||
|
@ -224,23 +243,23 @@ final class CelestialBodyTests: XCTestCase {
|
|||
XCTAssertEqual(mercuryAspects.count, 4)
|
||||
}
|
||||
|
||||
func testPlanetsOnAscendent() {
|
||||
let houseSystem = Mock.makeHouses()
|
||||
let ascendent = houseSystem.ascendent
|
||||
let aspects = Planet.allCases.compactMap {
|
||||
Aspect(a: ascendent.value,
|
||||
b: Coordinate<Planet>(body: $0, date: Mock.date).value,
|
||||
func testPlanetsOnAscendent() {
|
||||
let houseSystem = Mock.makeHouses()
|
||||
let ascendent = houseSystem.ascendent
|
||||
let aspects = Planet.allCases.compactMap {
|
||||
Aspect(a: ascendent.tropical.value,
|
||||
b: Coordinate<Planet>(body: $0, date: Mock.date).tropical.value,
|
||||
orb: 10)
|
||||
}
|
||||
XCTAssertEqual(aspects.compactMap { $0 }.count, 1)
|
||||
}
|
||||
|
||||
func testPlanetaryStation() {
|
||||
XCTAssertEqual(Station<Planet>(coordinate: Coordinate(body: .sun, date: Mock.date)), .direct)
|
||||
XCTAssertEqual(Station<Planet>(coordinate: Coordinate(body: .mars, date: Mock.date)), .direct)
|
||||
XCTAssertEqual(Station<Planet>(coordinate: Coordinate(body: .mercury, date: Mock.date)), .retrograde)
|
||||
XCTAssertEqual(Station<Planet>(coordinate: Coordinate(body: .jupiter, date: Mock.date)), .retrograde)
|
||||
}
|
||||
}
|
||||
XCTAssertEqual(aspects.compactMap { $0 }.count, 1)
|
||||
}
|
||||
|
||||
func testPlanetaryStation() {
|
||||
XCTAssertEqual(Station<Planet>(coordinate: Coordinate(body: .sun, date: Mock.date)), .direct)
|
||||
XCTAssertEqual(Station<Planet>(coordinate: Coordinate(body: .mars, date: Mock.date)), .direct)
|
||||
XCTAssertEqual(Station<Planet>(coordinate: Coordinate(body: .mercury, date: Mock.date)), .retrograde)
|
||||
XCTAssertEqual(Station<Planet>(coordinate: Coordinate(body: .jupiter, date: Mock.date)), .retrograde)
|
||||
}
|
||||
|
||||
func testAyanamsha() throws {
|
||||
let date = try XCTUnwrap(Mock.date(from: "2021-03-09T12:31:00-0800"))
|
||||
|
@ -268,11 +287,11 @@ final class CelestialBodyTests: XCTestCase {
|
|||
altitude: 0)
|
||||
XCTAssertEqual(moonRiseNYC.date?.description, "2021-03-15 12:25:55 +0000")
|
||||
}
|
||||
|
||||
func testPlanetSettingTime() throws {
|
||||
|
||||
func testPlanetSettingTime() throws {
|
||||
let timestamp = "2021-03-15"
|
||||
let dateFormatter = DateFormatter()
|
||||
dateFormatter.dateFormat = "yyyy-MM-dd"
|
||||
let dateFormatter = DateFormatter()
|
||||
dateFormatter.dateFormat = "yyyy-MM-dd"
|
||||
let date = try XCTUnwrap(dateFormatter.date(from: timestamp))
|
||||
let moonSet = SetTime<Planet>(date: date,
|
||||
body: .moon,
|
||||
|
@ -280,12 +299,12 @@ final class CelestialBodyTests: XCTestCase {
|
|||
latitude: 52.52437)
|
||||
XCTAssertEqual(moonSet.date?.description, "2021-03-15 19:25:58 +0000")
|
||||
let dateB = try XCTUnwrap(dateFormatter.date(from: "2021-03-16"))
|
||||
let sunsetTokyo = SetTime<Planet>(date: dateB,
|
||||
body: .sun,
|
||||
longitude: 139.69171,
|
||||
latitude: 35.6895)
|
||||
XCTAssertEqual(sunsetTokyo.date?.description, "2021-03-16 08:49:34 +0000")
|
||||
}
|
||||
let sunsetTokyo = SetTime<Planet>(date: dateB,
|
||||
body: .sun,
|
||||
longitude: 139.69171,
|
||||
latitude: 35.6895)
|
||||
XCTAssertEqual(sunsetTokyo.date?.description, "2021-03-16 08:49:34 +0000")
|
||||
}
|
||||
|
||||
func testLunarPhase() throws {
|
||||
let date = try Mock.date(from: "2021-03-21T11:11:00-0000")
|
||||
|
@ -310,37 +329,39 @@ final class CelestialBodyTests: XCTestCase {
|
|||
// Count from full to new
|
||||
XCTAssertEqual(count, 29)
|
||||
}
|
||||
|
||||
|
||||
func testLunarMansion() throws {
|
||||
var date = try Mock.date(from: "2021-04-12T01:11:00-0001")
|
||||
let interval: TimeInterval = 60 * 60 * 12
|
||||
var formatted = Set<String>()
|
||||
for _ in 0...56 {
|
||||
let moon = Coordinate<Planet>(body: .moon, date: date)
|
||||
formatted.insert(moon.lunarMansion.formatted)
|
||||
if #available(iOS 13.0, *) {
|
||||
date = date.advanced(by: interval)
|
||||
}
|
||||
formatted.insert(moon.tropical.lunarMansion.formatted)
|
||||
if #available(iOS 13.0, *) {
|
||||
date = date.advanced(by: interval)
|
||||
}
|
||||
}
|
||||
// Expect a unique description for each mansion.
|
||||
XCTAssertEqual(formatted.count, 28)
|
||||
}
|
||||
|
||||
|
||||
func testSiderealCoordinateEarlyAries() throws {
|
||||
let date = try Mock.date(from: "2021-03-25T01:11:00-0001")
|
||||
let sun = Coordinate<Planet>(body: .sun, date: date)
|
||||
XCTAssertEqual(sun.sign, .aries)
|
||||
let siderealSun = SiderealCoordinate(coordinate: sun)
|
||||
XCTAssertEqual(sun.tropical.sign, .aries)
|
||||
let siderealSun = sun.sidereal
|
||||
XCTAssertEqual(siderealSun.sign, .pisces)
|
||||
}
|
||||
|
||||
|
||||
static var allTests = [
|
||||
("testSunZodiacCoordinate",testSunZodiacCoordinate,
|
||||
"testMoonSiderealCoordinate", testMoonSiderealCoordinate,
|
||||
"testPlanets", testPlanets,
|
||||
"testAsteroids", testAsteroids,
|
||||
"testZodiac", testZodiac,
|
||||
"testLunarNodes", testLunarNodes,
|
||||
"testLunarNorthNodes", testLunarNorthNodes,
|
||||
"testLunarSouthNodes", testLunarSouthNodes,
|
||||
"testLunarSouthNodeOffset", testLunarSouthNodeOffset,
|
||||
"testAscendent", testAscendent,
|
||||
"testHouses", testHouses,
|
||||
"testAspects", testAspects,
|
||||
|
|
|
@ -9,77 +9,77 @@ final class FixedStarsTests: XCTestCase {
|
|||
|
||||
func testGalacticCenter() {
|
||||
let galacticCenter = Coordinate<FixedStar>(body: .galacticCenter, date: Mock.date)
|
||||
XCTAssertEqual(galacticCenter.formatted, "26 Degrees Sagittarius ♐︎ 40' 39''")
|
||||
XCTAssertEqual(galacticCenter.tropical.formatted, "26 Degrees Sagittarius ♐︎ 40' 39''")
|
||||
}
|
||||
|
||||
func testAldebaran() {
|
||||
let aldebaran = Coordinate<FixedStar>(body: .aldebaran, date: Mock.date)
|
||||
XCTAssertEqual(aldebaran.formatted, "9 Degrees Gemini ♊︎ 37' 24''")
|
||||
XCTAssertEqual(aldebaran.tropical.formatted, "9 Degrees Gemini ♊︎ 37' 24''")
|
||||
}
|
||||
|
||||
func testAntares() {
|
||||
let antares = Coordinate<FixedStar>(body: .antares, date: Mock.date)
|
||||
XCTAssertEqual(antares.formatted, "9 Degrees Sagittarius ♐︎ 35' 13''")
|
||||
XCTAssertEqual(antares.tropical.formatted, "9 Degrees Sagittarius ♐︎ 35' 13''")
|
||||
}
|
||||
|
||||
func testRegulus() {
|
||||
let regulus = Coordinate<FixedStar>(body: .regulus, date: Mock.date)
|
||||
XCTAssertEqual(regulus.formatted, "29 Degrees Leo ♌︎ 39' 26''")
|
||||
XCTAssertEqual(regulus.tropical.formatted, "29 Degrees Leo ♌︎ 39' 26''")
|
||||
}
|
||||
|
||||
func testSirius() {
|
||||
let sirius = Coordinate<FixedStar>(body: .sirius, date: Mock.date)
|
||||
XCTAssertEqual(sirius.formatted, "13 Degrees Cancer ♋︎ 55' 0''")
|
||||
XCTAssertEqual(sirius.tropical.formatted, "13 Degrees Cancer ♋︎ 55' 0''")
|
||||
}
|
||||
|
||||
func testSpica() {
|
||||
let spica = Coordinate<FixedStar>(body: .spica, date: Mock.date)
|
||||
XCTAssertEqual(spica.formatted, "23 Degrees Libra ♎︎ 39' 56''")
|
||||
XCTAssertEqual(spica.tropical.formatted, "23 Degrees Libra ♎︎ 39' 56''")
|
||||
}
|
||||
|
||||
func testAlgol() {
|
||||
let algol = Coordinate<FixedStar>(body: .algol, date: Mock.date)
|
||||
XCTAssertEqual(algol.formatted, "26 Degrees Taurus ♉︎ 0' 12''")
|
||||
XCTAssertEqual(algol.tropical.formatted, "26 Degrees Taurus ♉︎ 0' 12''")
|
||||
}
|
||||
|
||||
func testRigel() {
|
||||
let rigel = Coordinate<FixedStar>(body: .rigel, date: Mock.date)
|
||||
XCTAssertEqual(rigel.formatted, "16 Degrees Gemini ♊︎ 39' 51''")
|
||||
XCTAssertEqual(rigel.tropical.formatted, "16 Degrees Gemini ♊︎ 39' 51''")
|
||||
}
|
||||
|
||||
func testAltair() {
|
||||
let altair = Coordinate<FixedStar>(body: .altair, date: Mock.date)
|
||||
XCTAssertEqual(altair.formatted, "1 Degrees Aquarius ♒︎ 36' 12''")
|
||||
XCTAssertEqual(altair.tropical.formatted, "1 Degrees Aquarius ♒︎ 36' 12''")
|
||||
}
|
||||
|
||||
func testCapella() {
|
||||
let capella = Coordinate<FixedStar>(body: .capella, date: Mock.date)
|
||||
XCTAssertEqual(capella.formatted, "21 Degrees Gemini ♊︎ 41' 30''")
|
||||
XCTAssertEqual(capella.tropical.formatted, "21 Degrees Gemini ♊︎ 41' 30''")
|
||||
}
|
||||
|
||||
func testArcturus() {
|
||||
let arcturus = Coordinate<FixedStar>(body: .arcturus, date: Mock.date)
|
||||
XCTAssertEqual(arcturus.formatted, "24 Degrees Libra ♎︎ 3' 24''")
|
||||
XCTAssertEqual(arcturus.tropical.formatted, "24 Degrees Libra ♎︎ 3' 24''")
|
||||
}
|
||||
|
||||
func testProcyon() {
|
||||
let procyon = Coordinate<FixedStar>(body: .procyon, date: Mock.date)
|
||||
XCTAssertEqual(procyon.formatted, "25 Degrees Cancer ♋︎ 37' 6''")
|
||||
XCTAssertEqual(procyon.tropical.formatted, "25 Degrees Cancer ♋︎ 37' 6''")
|
||||
}
|
||||
|
||||
func testCastor() {
|
||||
let castor = Coordinate<FixedStar>(body: .castor, date: Mock.date)
|
||||
XCTAssertEqual(castor.formatted, "20 Degrees Cancer ♋︎ 4' 19''")
|
||||
XCTAssertEqual(castor.tropical.formatted, "20 Degrees Cancer ♋︎ 4' 19''")
|
||||
}
|
||||
|
||||
func testPollux() {
|
||||
let pollux = Coordinate<FixedStar>(body: .pollux, date: Mock.date)
|
||||
XCTAssertEqual(pollux.formatted, "23 Degrees Cancer ♋︎ 2' 55''")
|
||||
XCTAssertEqual(pollux.tropical.formatted, "23 Degrees Cancer ♋︎ 2' 55''")
|
||||
}
|
||||
|
||||
func testBetelgeuse() {
|
||||
let betelgeuse = Coordinate<FixedStar>(body: .betelgeuse, date: Mock.date)
|
||||
XCTAssertEqual(betelgeuse.formatted, "28 Degrees Gemini ♊︎ 35' 16''")
|
||||
XCTAssertEqual(betelgeuse.tropical.formatted, "28 Degrees Gemini ♊︎ 35' 16''")
|
||||
}
|
||||
|
||||
static var allTests = [
|
||||
|
|
|
@ -2,126 +2,126 @@ import XCTest
|
|||
@testable import SwissEphemeris
|
||||
|
||||
final class PerformanceTests: XCTestCase {
|
||||
|
||||
private var date: Date!
|
||||
private let latitude: Double = 37.5081153
|
||||
private let longitude: Double = -122.2854528
|
||||
|
||||
override func setUpWithError() throws {
|
||||
date = try Mock.date(from: "2021-06-21T01:11:00-0001")
|
||||
JPLFileManager.setEphemerisPath()
|
||||
}
|
||||
|
||||
func testCoordinatePerformance() throws {
|
||||
measure {
|
||||
for day in 0...1065 {
|
||||
Planet.allCases.forEach {
|
||||
XCTAssertNotNil(Coordinate<Planet>(body: $0, date: date))
|
||||
if #available(iOS 13.0, *) {
|
||||
date = date.advanced(by: (60 * 60 * 24) * TimeInterval(day))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func testHouseCuspsPerformance() {
|
||||
measure {
|
||||
for day in 0...365 {
|
||||
HouseSystem.allCases.forEach {
|
||||
XCTAssertNotNil(HouseCusps(date: date,
|
||||
latitude: latitude,
|
||||
longitude: longitude,
|
||||
houseSystem: $0))
|
||||
if #available(iOS 13.0, *) {
|
||||
date = date.advanced(by: (60 * 60 * 24) * TimeInterval(day))
|
||||
} else {
|
||||
// Fallback on earlier versions
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func testRiseTimePerfomance() {
|
||||
measure {
|
||||
for day in 0...730 {
|
||||
XCTAssertNotNil(
|
||||
RiseTime<Planet>(date: date,
|
||||
body: .moon,
|
||||
longitude: longitude,
|
||||
latitude: latitude)
|
||||
)
|
||||
if #available(iOS 13.0, *) {
|
||||
date = date.advanced(by: (60 * 60 * 24) * TimeInterval(day))
|
||||
} else {
|
||||
// Fallback on earlier versions
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func testSetTimePerformance() {
|
||||
measure {
|
||||
for day in 0...730 {
|
||||
XCTAssertNotNil(
|
||||
SetTime<Planet>(date: date,
|
||||
body: .moon,
|
||||
longitude: longitude,
|
||||
latitude: latitude)
|
||||
)
|
||||
if #available(iOS 13.0, *) {
|
||||
date = date.advanced(by: (60 * 60 * 24) * TimeInterval(day))
|
||||
} else {
|
||||
// Fallback on earlier versions
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func testLunationPerformance() {
|
||||
measure {
|
||||
for day in 0...730 {
|
||||
XCTAssertNotNil(Lunation(date: date))
|
||||
if #available(iOS 13.0, *) {
|
||||
date = date.advanced(by: (60 * 60 * 24) * TimeInterval(day))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func testAspectPerformance() {
|
||||
let PlanetPairs = PlanetPairs()
|
||||
measure {
|
||||
for day in 0...1365 {
|
||||
PlanetPairs.pairs.forEach {
|
||||
let _ = Aspect(pair: $0, date: date)
|
||||
}
|
||||
if #available(iOS 13.0, *) {
|
||||
date = date.advanced(by: (60 * 60 * 24) * TimeInterval(day))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func testSpringEquinoxDatePerformance() {
|
||||
measure {
|
||||
var coordinate = Coordinate<Planet>(body: .sun, date: date)
|
||||
while coordinate.value > 1.0 {
|
||||
coordinate = Coordinate(body: .sun, date: coordinate.date.addingTimeInterval(60 * 60 * 12))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func testAutumnalEquinoxDatePerformance() {
|
||||
measure {
|
||||
var coordinate = Coordinate<Planet>(body: .sun, date: date)
|
||||
while Int(coordinate.value) != 180 {
|
||||
coordinate = Coordinate(body: .sun, date: coordinate.date.addingTimeInterval(60 * 60 * 12))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private var date: Date!
|
||||
private let latitude: Double = 37.5081153
|
||||
private let longitude: Double = -122.2854528
|
||||
|
||||
override func setUpWithError() throws {
|
||||
date = try Mock.date(from: "2021-06-21T01:11:00-0001")
|
||||
JPLFileManager.setEphemerisPath()
|
||||
}
|
||||
|
||||
func testCoordinatePerformance() throws {
|
||||
measure {
|
||||
for day in 0...1065 {
|
||||
Planet.allCases.forEach {
|
||||
XCTAssertNotNil(Coordinate<Planet>(body: $0, date: date))
|
||||
if #available(iOS 13.0, *) {
|
||||
date = date.advanced(by: (60 * 60 * 24) * TimeInterval(day))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func testHouseCuspsPerformance() {
|
||||
measure {
|
||||
for day in 0...365 {
|
||||
HouseSystem.allCases.forEach {
|
||||
XCTAssertNotNil(HouseCusps(date: date,
|
||||
latitude: latitude,
|
||||
longitude: longitude,
|
||||
houseSystem: $0))
|
||||
if #available(iOS 13.0, *) {
|
||||
date = date.advanced(by: (60 * 60 * 24) * TimeInterval(day))
|
||||
} else {
|
||||
// Fallback on earlier versions
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func testRiseTimePerfomance() {
|
||||
measure {
|
||||
for day in 0...730 {
|
||||
XCTAssertNotNil(
|
||||
RiseTime<Planet>(date: date,
|
||||
body: .moon,
|
||||
longitude: longitude,
|
||||
latitude: latitude)
|
||||
)
|
||||
if #available(iOS 13.0, *) {
|
||||
date = date.advanced(by: (60 * 60 * 24) * TimeInterval(day))
|
||||
} else {
|
||||
// Fallback on earlier versions
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func testSetTimePerformance() {
|
||||
measure {
|
||||
for day in 0...730 {
|
||||
XCTAssertNotNil(
|
||||
SetTime<Planet>(date: date,
|
||||
body: .moon,
|
||||
longitude: longitude,
|
||||
latitude: latitude)
|
||||
)
|
||||
if #available(iOS 13.0, *) {
|
||||
date = date.advanced(by: (60 * 60 * 24) * TimeInterval(day))
|
||||
} else {
|
||||
// Fallback on earlier versions
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func testLunationPerformance() {
|
||||
measure {
|
||||
for day in 0...730 {
|
||||
XCTAssertNotNil(Lunation(date: date))
|
||||
if #available(iOS 13.0, *) {
|
||||
date = date.advanced(by: (60 * 60 * 24) * TimeInterval(day))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func testAspectPerformance() {
|
||||
let PlanetPairs = PlanetPairs()
|
||||
measure {
|
||||
for day in 0...1365 {
|
||||
PlanetPairs.pairs.forEach {
|
||||
let _ = Aspect(pair: $0, date: date)
|
||||
}
|
||||
if #available(iOS 13.0, *) {
|
||||
date = date.advanced(by: (60 * 60 * 24) * TimeInterval(day))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func testSpringEquinoxDatePerformance() {
|
||||
measure {
|
||||
var coordinate = Coordinate<Planet>(body: .sun, date: date)
|
||||
while coordinate.longitude > 1.0 {
|
||||
coordinate = Coordinate(body: .sun, date: coordinate.date.addingTimeInterval(60 * 60 * 12))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func testAutumnalEquinoxDatePerformance() {
|
||||
measure {
|
||||
var coordinate = Coordinate<Planet>(body: .sun, date: date)
|
||||
while Int(coordinate.tropical.value) != 180 {
|
||||
coordinate = Coordinate(body: .sun, date: coordinate.date.addingTimeInterval(60 * 60 * 12))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func testBatchRequestPlanetCoordinatesDeprecated() {
|
||||
measure {
|
||||
PlanetsRequest(body: .moon).fetch(start: date, end: date.addingTimeInterval(60 * 60 * 24 * 30)) {
|
||||
|
@ -135,7 +135,7 @@ final class PerformanceTests: XCTestCase {
|
|||
let batch = await PlanetsRequest(body: .moon).fetch(start: date, end: date.addingTimeInterval(60 * 60 * 24 * 30))
|
||||
XCTAssertEqual(batch.count, 43200)
|
||||
}
|
||||
|
||||
|
||||
static var allTests = [
|
||||
("testCoordinatePerformance",testCoordinatePerformance,
|
||||
"testHouseCuspsPerformance", testHouseCuspsPerformance,
|
||||
|
|
Loading…
Reference in New Issue