From ee8fc7b5a498db39b4e8e83907816c9507896d1c Mon Sep 17 00:00:00 2001 From: Ignas Sireikis Date: Thu, 28 Aug 2025 15:34:03 -0500 Subject: [PATCH 01/91] Fixed formatting and style in Utils --- Sources/SunKit/Utils.swift | 126 ++++++++++++++++++++----------------- 1 file changed, 67 insertions(+), 59 deletions(-) diff --git a/Sources/SunKit/Utils.swift b/Sources/SunKit/Utils.swift index de30e34..30649b7 100644 --- a/Sources/SunKit/Utils.swift +++ b/Sources/SunKit/Utils.swift @@ -1,6 +1,6 @@ // // Utils.swift -// +// // // Copyright 2024 Leonardo Bertinelli, Davide Biancardi, Raffaele Fulgente, Clelia Iovine, Nicolas Mariniello, Fabio Pizzano // @@ -18,6 +18,7 @@ import Foundation + let SECONDS_IN_ONE_DAY = 86399 let TWELVE_HOUR_IN_SECONDS: Double = 43200 let TWO_HOURS_IN_SECONDS: Double = 7200 @@ -29,38 +30,32 @@ let SECONDS_IN_ONE_HOUR: Double = 3600 /// - a: first operand /// - n: second operand /// - Returns: a % n if a is positive. If a isn't positive, it will add to the result of the modulo operation the value of the n operand until the result is positive.Please note that this function only works when both operands are integers. - public func mod(_ a: Int, _ n: Int) -> Int { - let r = a % n - return r >= 0 ? r : r + n - } - - - /// Same as mod function described above, but this function can accept as first operand a Double and it handle the edge case where a is included between -1 and 0. - /// - Parameters: - /// - a: first operand - /// - n: second operand - /// - Returns: a % n if a is positive. If a isn't positive, it will add to the result of the modulo operation the value of the n operand until the result is positive. - public func extendedMod(_ a: Double, _ n: Int) -> Double { - - let remainder: Double = a.truncatingRemainder(dividingBy: 1) - - if (a < 0 && a > -1){ - - return Double(n) + remainder - } - - let x = Double(mod(Int(a),n)) - - return x + remainder - } - - +public func mod(_ a: Int, _ n: Int) -> Int { + let r = a % n + + return r >= 0 ? r : r + n +} -public func clamp(lower: Double, upper: Double, number: Double) -> Double { +/// Same as mod function described above, but this function can accept as first operand a Double and it handle the edge case where a is included between -1 and 0. +/// - Parameters: +/// - a: first operand +/// - n: second operand +/// - Returns: a % n if a is positive. If a isn't positive, it will add to the result of the modulo operation the value of the n operand until the result is positive. +public func extendedMod(_ a: Double, _ n: Int) -> Double { + let remainder: Double = a.truncatingRemainder(dividingBy: 1) - return min(upper,max(lower,number)) - } + if (a < 0 && a > -1) { + return Double(n) + remainder + } + + let x = Double(mod(Int(a), n)) + + return x + remainder +} +public func clamp(lower: Double, upper: Double, number: Double) -> Double { + return min(upper, max(lower, number)) +} /// Creates a date with the UTC timezone /// - Parameters: @@ -72,8 +67,15 @@ public func clamp(lower: Double, upper: Double, number: Double) -> Double { /// - seconds: second of the date you want to create /// - nanosecond: nanosecond of the date you want to create /// - Returns: A date with the parameters given in input. Used in combination with function jdFromDate, that accepts dates in UTC format -public func createDateUTC(day: Int,month: Int,year: Int,hour: Int,minute: Int,seconds: Int, nanosecond: Int = 0) -> Date{ - +public func createDateUTC( + day: Int, + month: Int, + year: Int, + hour: Int, + minute: Int, + seconds: Int, + nanosecond: Int = 0 +) -> Date{ var calendarUTC:Calendar = .init(identifier: .gregorian) calendarUTC.timeZone = .init(secondsFromGMT: 0)! var dateComponents = DateComponents() @@ -88,7 +90,6 @@ public func createDateUTC(day: Int,month: Int,year: Int,hour: Int,minute: Int,se return calendarUTC.date(from: dateComponents) ?? Date() } - /// Creates a date with the timezone used in your current device /// - Parameters: /// - day: day of the date you want to create @@ -99,8 +100,15 @@ public func createDateUTC(day: Int,month: Int,year: Int,hour: Int,minute: Int,se /// - seconds: second of the date you want to create /// - nanosecond: nanosecond of the date you want to create /// - Returns: A date with the parameters given in input -public func createDateCurrentTimeZone(day: Int,month: Int,year: Int,hour: Int,minute: Int,seconds: Int, nanosecond: Int = 0) -> Date{ - +public func createDateCurrentTimeZone( + day: Int, + month: Int, + year: Int, + hour: Int, + minute: Int, + seconds: Int, + nanosecond: Int = 0 +) -> Date{ var calendar: Calendar = .init(identifier: .gregorian) calendar.timeZone = .current var dateComponents = DateComponents() @@ -126,8 +134,16 @@ public func createDateCurrentTimeZone(day: Int,month: Int,year: Int,hour: Int,mi /// - nanosecond: nanosecond of the date you want to create /// - timeZone: timezone of the date /// - Returns: A date with the parameters given in input -public func createDateCustomTimeZone(day: Int,month: Int,year: Int,hour: Int,minute: Int,seconds: Int, nanosecond: Int = 0, timeZone: TimeZone) -> Date{ - +public func createDateCustomTimeZone( + day: Int, + month: Int, + year: Int, + hour: Int, + minute: Int, + seconds: Int, + nanosecond: Int = 0, + timeZone: TimeZone +) -> Date{ var calendar: Calendar = .init(identifier: .gregorian) calendar.timeZone = timeZone var dateComponents = DateComponents() @@ -142,7 +158,6 @@ public func createDateCustomTimeZone(day: Int,month: Int,year: Int,hour: Int,min return calendar.date(from: dateComponents) ?? Date() } - /// Converts Julian Number in a date /// - Parameter jd: Julian number to convert in an UTC date /// - Returns: The date corresponding to the julian number in input @@ -151,60 +166,56 @@ public func dateFromJd(jd : Double) -> Date { return Date(timeIntervalSince1970: (jd - JD_JAN_1_1970_0000GMT) * 86400) } - /// Converts date in his Julian Number /// - Parameter date: UTC date to convert in julian number. TimeZone of the given date shall be equals to +0000. /// - Returns: The julian day number corresponding to date in input public func jdFromDate(date : Date) -> Double { let JD_JAN_1_1970_0000GMT = 2440587.5 + return JD_JAN_1_1970_0000GMT + date.timeIntervalSince1970 / 86400 } - /// Converts UT time to Greenwich Sidereal Time /// - Parameter ut: UT time to convert in GST /// - Parameter timeZoneInSeconds: time zone expressed in seconds of your local civil time /// - Returns: GST equivalent of the UT given in input public func uT2GST(_ ut:Date) -> HMS{ - var calendarUTC: Calendar = .init(identifier: .gregorian) calendarUTC.timeZone = TimeZone(identifier: "GMT")! - //Step1: + // Step1: let jd = jdFromDate(date: calendarUTC.startOfDay(for: ut)) - //Step2: + // Step2: let year = calendarUTC.component(.year, from: ut) let firstDayOfTheYear = calendarUTC.date(from: DateComponents(year: year , month: 1, day: 1)) ?? Date() let jdZero = jdFromDate(date: firstDayOfTheYear) - //Step3: + // Step3: let days = jd - jdZero - //Step4: + // Step4: let T = (jdZero - 2415020.0) / 36525.0 - //Step5: + // Step5: let R = 6.6460656 + 2400.051262 * T + 0.00002581*(T*T) - //Step6: + // Step6: let B :Double = 24 - R + Double(24 * (year - 1900)) - //Step7: + // Step7: let TZero = 0.0657098 * days - B - //Step8: + // Step8: let utDecimal = HMS.init(from: ut).hMS2Decimal() - //Step9: + // Step9: var gstDecimal = TZero + 1.002738 * utDecimal if gstDecimal < 0 { - gstDecimal += 24 - - }else if gstDecimal >= 24 { + } else if gstDecimal >= 24 { gstDecimal -= 24 } @@ -221,7 +232,6 @@ public func uT2GST(_ ut:Date) -> HMS{ /// - Parameter timeZoneInSeconds: time zone expressed in seconds of your local civil time /// - Returns: LST equivalent for the GST given in input public func gST2LST(_ gst: HMS, longitude: Angle) -> HMS{ - //Step1: let gstDecimal = gst.hMS2Decimal() @@ -230,25 +240,23 @@ public func gST2LST(_ gst: HMS, longitude: Angle) -> HMS{ //Step3: var lstDecimal = gstDecimal + adjustment - + if lstDecimal < 0 { lstDecimal += 24 - }else if lstDecimal >= 24 { + } else if lstDecimal >= 24 { lstDecimal -= 24 } - + let lstHMS = HMS.init(decimal: lstDecimal) return lstHMS } - /// Converts the number of seconds in HH:MM:ss /// - Parameter seconds: Number of seconds that have to be converted /// - Returns: From value in input the equivalent in (hours,minute,seconds) public func secondsToHoursMinutesSeconds(_ seconds : Int) -> (Int,Int,Int) { - return (seconds / 3600, (seconds % 3600) / 60, (seconds % 3600) % 60) } From 9fbc0933ae4a0e8b6a2d71edb7d017416050fb41 Mon Sep 17 00:00:00 2001 From: Ignas Sireikis Date: Thu, 28 Aug 2025 15:36:04 -0500 Subject: [PATCH 02/91] Fixed formatting and style in SunElevationEvents and Utils. --- Sources/SunKit/SunElevationEvents.swift | 5 ++--- Sources/SunKit/Utils.swift | 16 +++++++--------- 2 files changed, 9 insertions(+), 12 deletions(-) diff --git a/Sources/SunKit/SunElevationEvents.swift b/Sources/SunKit/SunElevationEvents.swift index fcaf6d3..dc002fb 100644 --- a/Sources/SunKit/SunElevationEvents.swift +++ b/Sources/SunKit/SunElevationEvents.swift @@ -18,8 +18,8 @@ import Foundation -enum SunElevationEvents: Double{ - + +enum SunElevationEvents: Double { case civil = -6 case nautical = -12 case astronomical = -18 @@ -28,5 +28,4 @@ enum SunElevationEvents: Double{ static var morningGoldenHourStart: SunElevationEvents { .eveningGoldenHourEnd } static var morningGoldenHourEnd: SunElevationEvents { .eveningGoldenHourStart } - } diff --git a/Sources/SunKit/Utils.swift b/Sources/SunKit/Utils.swift index 30649b7..c7f4f49 100644 --- a/Sources/SunKit/Utils.swift +++ b/Sources/SunKit/Utils.swift @@ -75,7 +75,7 @@ public func createDateUTC( minute: Int, seconds: Int, nanosecond: Int = 0 -) -> Date{ +) -> Date { var calendarUTC:Calendar = .init(identifier: .gregorian) calendarUTC.timeZone = .init(secondsFromGMT: 0)! var dateComponents = DateComponents() @@ -108,7 +108,7 @@ public func createDateCurrentTimeZone( minute: Int, seconds: Int, nanosecond: Int = 0 -) -> Date{ +) -> Date { var calendar: Calendar = .init(identifier: .gregorian) calendar.timeZone = .current var dateComponents = DateComponents() @@ -143,7 +143,7 @@ public func createDateCustomTimeZone( seconds: Int, nanosecond: Int = 0, timeZone: TimeZone -) -> Date{ +) -> Date { var calendar: Calendar = .init(identifier: .gregorian) calendar.timeZone = timeZone var dateComponents = DateComponents() @@ -172,15 +172,14 @@ public func dateFromJd(jd : Double) -> Date { public func jdFromDate(date : Date) -> Double { let JD_JAN_1_1970_0000GMT = 2440587.5 - return JD_JAN_1_1970_0000GMT + date.timeIntervalSince1970 - / 86400 + return JD_JAN_1_1970_0000GMT + date.timeIntervalSince1970 / 86400 } /// Converts UT time to Greenwich Sidereal Time /// - Parameter ut: UT time to convert in GST /// - Parameter timeZoneInSeconds: time zone expressed in seconds of your local civil time /// - Returns: GST equivalent of the UT given in input -public func uT2GST(_ ut:Date) -> HMS{ +public func uT2GST(_ ut:Date) -> HMS { var calendarUTC: Calendar = .init(identifier: .gregorian) calendarUTC.timeZone = TimeZone(identifier: "GMT")! @@ -216,7 +215,6 @@ public func uT2GST(_ ut:Date) -> HMS{ if gstDecimal < 0 { gstDecimal += 24 } else if gstDecimal >= 24 { - gstDecimal -= 24 } @@ -231,7 +229,7 @@ public func uT2GST(_ ut:Date) -> HMS{ /// - longitude: longitude of the observer /// - Parameter timeZoneInSeconds: time zone expressed in seconds of your local civil time /// - Returns: LST equivalent for the GST given in input -public func gST2LST(_ gst: HMS, longitude: Angle) -> HMS{ +public func gST2LST(_ gst: HMS, longitude: Angle) -> HMS { //Step1: let gstDecimal = gst.hMS2Decimal() @@ -257,6 +255,6 @@ public func gST2LST(_ gst: HMS, longitude: Angle) -> HMS{ /// Converts the number of seconds in HH:MM:ss /// - Parameter seconds: Number of seconds that have to be converted /// - Returns: From value in input the equivalent in (hours,minute,seconds) -public func secondsToHoursMinutesSeconds(_ seconds : Int) -> (Int,Int,Int) { +public func secondsToHoursMinutesSeconds(_ seconds : Int) -> (Int, Int, Int) { return (seconds / 3600, (seconds % 3600) / 60, (seconds % 3600) % 60) } From 36302de6b0e7f34ae4146afa6719f03bc6926310 Mon Sep 17 00:00:00 2001 From: Ignas Sireikis Date: Thu, 28 Aug 2025 15:41:20 -0500 Subject: [PATCH 03/91] Fixed formatting and style in Sun. --- Sources/SunKit/Sun.swift | 45 ++++++++++++++++++---------------------- 1 file changed, 20 insertions(+), 25 deletions(-) diff --git a/Sources/SunKit/Sun.swift b/Sources/SunKit/Sun.swift index 4b7e79f..2da58da 100644 --- a/Sources/SunKit/Sun.swift +++ b/Sources/SunKit/Sun.swift @@ -19,6 +19,7 @@ import Foundation import CoreLocation + public struct Sun: Identifiable, Sendable { public let id: UUID = UUID() @@ -284,7 +285,6 @@ public struct Sun: Identifiable, Sendable { refresh() } - /// Is highly recommanded to use the other method to change both location and timezone. This will be kept only for backwards retrocompatibility. /// - Parameters: /// - newLocation: New Location @@ -296,7 +296,6 @@ public struct Sun: Identifiable, Sendable { refresh() } - /*-------------------------------------------------------------------- Changing Timezone *-------------------------------------------------------------------*/ @@ -322,7 +321,6 @@ public struct Sun: Identifiable, Sendable { /// Dumps all the Sun Events dates public func dumpDateInfos(){ - print("Current Date -> \(dateFormatter.string(from: date))") print("Sunrise -> \(dateFormatter.string(from: sunrise))") print("Sunset -> \(dateFormatter.string(from: sunset))") @@ -373,8 +371,7 @@ public struct Sun: Identifiable, Sendable { timeZone.secondsFromGMT(for: self.date) } - private var sunHorizonCoordinates: HorizonCoordinates = .init(altitude: .zero, azimuth: .zero) - + private var sunHorizonCoordinates: HorizonCoordinates = .init(altitude: .zero, azimuth: .zero) //Sun constants private let sunEclipticLongitudeAtTheEpoch: Angle = .init(degrees: 280.466069) @@ -424,7 +421,6 @@ public struct Sun: Identifiable, Sendable { return timeCorrectionFactorInSeconds } - /*-------------------------------------------------------------------- Private methods *-------------------------------------------------------------------*/ @@ -439,7 +435,7 @@ public struct Sun: Identifiable, Sendable { private mutating func refresh(needToComputeSunEvents: Bool = true) { updateSunCoordinates() - if(needToComputeSunEvents){ + if (needToComputeSunEvents){ self.sunrise = getSunrise() ?? Date() self.sunriseAzimuth = getSunHorizonCoordinatesFrom(date: sunrise).azimuth.degrees self.sunset = getSunset() ?? Date() @@ -457,7 +453,6 @@ public struct Sun: Identifiable, Sendable { self.astronomicalDawn = getAstronomicalDawn() ?? Date() self.morningGoldenHourStart = getMorningGoldenHourStart() ?? Date() self.morningGoldenHourEnd = getMorningGoldenHourEnd() ?? Date() - } self.marchEquinox = getMarchEquinox() ?? Date() @@ -487,7 +482,6 @@ public struct Sun: Identifiable, Sendable { return eclipticLatitude } - /// Updates Horizon coordinates, Ecliptic coordinates and Equatorial coordinates of the Sun private mutating func updateSunCoordinates() { //Step1: @@ -537,7 +531,6 @@ public struct Sun: Identifiable, Sendable { //Step13: Equatorial to Horizon sunHorizonCoordinates = sunEquatorialCoordinates.equatorial2Horizon(lstDecimal: lstDecimal,latitude: latitude) ?? .init(altitude: .zero, azimuth: .zero) - } public func getSunHorizonCoordinatesFrom(date: Date) -> HorizonCoordinates { @@ -608,18 +601,15 @@ public struct Sun: Identifiable, Sendable { /// Computes the solar midnight for self.date. /// - Returns: Solar midnight time private func getSolarMidnight() -> Date? { - let secondsForUTCSolarMidnight = (0 - 4 * location.coordinate.longitude - equationOfTime) * 60 let secondsForSolarMidnight = secondsForUTCSolarMidnight + Double(timeZoneInSeconds) let startOfTheDay = calendar.startOfDay(for: date) let solarMidnight = calendar.date(byAdding: .second, value: Int(secondsForSolarMidnight) , to: startOfTheDay) - + return solarMidnight } - - /// Computes the Sunrise time for self.date /// - Returns: Sunrise time private func getSunrise() -> Date? { @@ -669,8 +659,10 @@ public struct Sun: Identifiable, Sendable { /// - elevation: Elevation /// - morning: Sun reaches a specific elevation twice, this boolean variable is needed to find out which one need to be considered. The one reached in the morning or not. /// - Returns: Time at which the Sun reaches that elevation. Nil if it didn't find it. - private func getDateFrom(sunEvent : SunElevationEvents, morning: Bool = false) -> Date? { - + private func getDateFrom( + sunEvent : SunElevationEvents, + morning: Bool = false + ) -> Date? { let elevationSun: Angle = .degrees(sunEvent.rawValue) var cosHra = (sin(elevationSun.radians) - sin(sunEquatorialCoordinates.declination.radians) * sin(latitude.radians)) / (cos(sunEquatorialCoordinates.declination.radians) * cos(latitude.radians)) cosHra = clamp(lower: -1, upper: 1, number: cosHra) @@ -690,12 +682,9 @@ public struct Sun: Identifiable, Sendable { let newDate = calendar.date(bySettingHour: hoursMinutesSeconds.0 , minute: hoursMinutesSeconds.1, second: hoursMinutesSeconds.2, of: startOfTheDay) - return newDate } - - /// Golden Hour in the evening begins when the sun reaches elevation equals to 6 degrees /// - Returns: Time at which the GoldenHour starts private func getEveningGoldenHourStart() -> Date? { @@ -722,6 +711,7 @@ public struct Sun: Identifiable, Sendable { guard let civilDawn = getDateFrom(sunEvent: .civil,morning: true) else { return nil } + return civilDawn } @@ -731,6 +721,7 @@ public struct Sun: Identifiable, Sendable { guard let civilDusk = getDateFrom(sunEvent: .civil, morning: false) else { return nil } + return civilDusk } @@ -740,6 +731,7 @@ public struct Sun: Identifiable, Sendable { guard let nauticalDusk = getDateFrom(sunEvent: .nautical, morning: false) else { return nil } + return nauticalDusk } @@ -749,6 +741,7 @@ public struct Sun: Identifiable, Sendable { guard let nauticalDawn = getDateFrom(sunEvent: .nautical, morning: true) else { return nil } + return nauticalDawn } @@ -758,6 +751,7 @@ public struct Sun: Identifiable, Sendable { guard let astronomicalDusk = getDateFrom(sunEvent: .astronomical, morning: false) else { return nil } + return astronomicalDusk } @@ -767,6 +761,7 @@ public struct Sun: Identifiable, Sendable { guard let astronomicalDawn = getDateFrom(sunEvent: .astronomical, morning: true) else { return nil } + return astronomicalDawn } @@ -776,6 +771,7 @@ public struct Sun: Identifiable, Sendable { guard let morningGoldenHourStart = getDateFrom(sunEvent: .morningGoldenHourStart , morning: true) else { return nil } + return morningGoldenHourStart } @@ -785,11 +781,11 @@ public struct Sun: Identifiable, Sendable { guard let morningGoldenHourEnd = getDateFrom(sunEvent: .morningGoldenHourEnd , morning: true) else { return nil } + return morningGoldenHourEnd } private func getMarchEquinox() -> Date? { - let year = Double(calendar.component(.year, from: self.date)) let t: Double = year / 1000 let julianDayMarchEquinox: Double = 1721139.2855 + 365.2421376 * year + 0.0679190 * pow(t, 2) - 0.0027879 * pow(t, 3) @@ -800,7 +796,6 @@ public struct Sun: Identifiable, Sendable { } private func getJuneSolstice() -> Date? { - let year = Double(calendar.component(.year, from: self.date)) let t: Double = year / 1000 let julianDayJuneSolstice: Double = 1721233.2486 + 365.2417284 * year - 0.0530180 * pow(t, 2) + 0.0093320 * pow(t, 3) @@ -811,7 +806,6 @@ public struct Sun: Identifiable, Sendable { } private func getSeptemberEquinox() -> Date? { - let year = Double(calendar.component(.year, from: self.date)) let t: Double = year / 1000 let julianDaySeptemberEquinox: Double = 1721325.6978 + 365.2425055 * year - 0.126689 * pow(t, 2) + 0.0019401 * pow(t, 3) @@ -822,7 +816,6 @@ public struct Sun: Identifiable, Sendable { } private func getDecemberSolstice() -> Date? { - let year = Double(calendar.component(.year, from: self.date)) let t: Double = year / 1000 let julianDayDecemberSolstice: Double = 1721414.3920 + 365.2428898 * year - 0.0109650 * pow(t, 2) - 0.0084885 * pow(t, 3) @@ -832,9 +825,11 @@ public struct Sun: Identifiable, Sendable { return decemberSolsticeUTC } - /// - Returns: Length in meters of the object's shadow by the provided object height and current sun altitude. - public func shadowLength(for objectHeight: Double = 1, with altitude: Angle? = nil) -> Double? { + public func shadowLength( + for objectHeight: Double = 1, + with altitude: Angle? = nil + ) -> Double? { let altitude = altitude ?? self.altitude return if altitude.degrees > 0 && altitude.degrees < 90 { From cfe102a96a2b8e3e9aa23160fe3688202d14e93b Mon Sep 17 00:00:00 2001 From: Ignas Sireikis Date: Thu, 28 Aug 2025 15:42:53 -0500 Subject: [PATCH 04/91] Fixed formatting and style in HorizonCoordinates. --- Sources/SunKit/HorizonCoordinates.swift | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) diff --git a/Sources/SunKit/HorizonCoordinates.swift b/Sources/SunKit/HorizonCoordinates.swift index 74cc82f..9baf52a 100644 --- a/Sources/SunKit/HorizonCoordinates.swift +++ b/Sources/SunKit/HorizonCoordinates.swift @@ -1,6 +1,6 @@ // // HoorizonCoordinates.swift -// +// // // Copyright 2024 Leonardo Bertinelli, Davide Biancardi, Raffaele Fulgente, Clelia Iovine, Nicolas Mariniello, Fabio Pizzano // @@ -18,27 +18,24 @@ import Foundation + public struct HorizonCoordinates: Equatable, Hashable, Codable, Sendable { - public var altitude: Angle public var azimuth: Angle - + /// Converts horizon coordinates to equatorial coordinates /// - Returns: Equatorial coordinates of the instance. - public func horizon2Equatorial(latitude: Angle) -> EquatorialCoordinates{ - + public func horizon2Equatorial(latitude: Angle) -> EquatorialCoordinates { let tZeroHorizonToEquatorial = sin(altitude.radians) * sin(latitude.radians) + cos(altitude.radians) * cos(latitude.radians) * cos(azimuth.radians) let declination: Angle = .init(radians:asin(tZeroHorizonToEquatorial)) - let tOneHorizonToEquatorial = sin(altitude.radians) - sin(latitude.radians) * sin(declination.radians) - let tTwoHorizonToEquatorial = tOneHorizonToEquatorial / (cos(latitude.radians) * cos(declination.radians)) - var hourAngle: Angle = .init(radians: acos(tTwoHorizonToEquatorial)) - if sin(altitude.radians) >= 0{ + if sin(altitude.radians) >= 0 { hourAngle.degrees = 360 - hourAngle.degrees } + return .init(declination: declination, hourAngle: hourAngle) } } From 61174e84b2e8cfec8073665a40bddff33c1380d2 Mon Sep 17 00:00:00 2001 From: Ignas Sireikis Date: Thu, 28 Aug 2025 15:43:25 -0500 Subject: [PATCH 05/91] Fixed formatting and style in HMS. --- Sources/SunKit/HMS.swift | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/Sources/SunKit/HMS.swift b/Sources/SunKit/HMS.swift index 85c6c46..50f6b9b 100644 --- a/Sources/SunKit/HMS.swift +++ b/Sources/SunKit/HMS.swift @@ -1,6 +1,6 @@ // // HMS.swift -// +// // // Copyright 2024 Leonardo Bertinelli, Davide Biancardi, Raffaele Fulgente, Clelia Iovine, Nicolas Mariniello, Fabio Pizzano // @@ -21,13 +21,11 @@ import Foundation /// Time expressed in HMS format public struct HMS: Equatable, Hashable, Codable, Sendable { - public var hours: Double public var minutes: Double public var seconds: Double public init(from date: Date){ - var calendar: Calendar = .init(identifier: .gregorian) calendar.timeZone = .init(abbreviation: "GMT")! @@ -37,14 +35,12 @@ public struct HMS: Equatable, Hashable, Codable, Sendable { } public init(hours: Double,minutes: Double,seconds: Double){ - self.hours = hours self.minutes = minutes self.seconds = seconds } public init(decimal: Double){ - //Step1: let sign = decimal < 0 ? -1 : 1 //Step2: @@ -57,17 +53,16 @@ public struct HMS: Equatable, Hashable, Codable, Sendable { let seconds = 60 * (60 * dec.truncatingRemainder(dividingBy: 1)).truncatingRemainder(dividingBy: 1) //Step6: hours *= sign - + self.hours = Double(hours) self.minutes = Double(minutes) self.seconds = seconds - + } /// It converts from HMS format to decimal /// - Returns: HMS of the instance expressed in decimal format public func hMS2Decimal() -> Double { - //Step3: let dm: Double = Double(seconds / 60) //Step4: @@ -79,5 +74,4 @@ public struct HMS: Equatable, Hashable, Codable, Sendable { return decimalHour } - } From aa20431743e9cd1d0d84854a12226d3841df8ad9 Mon Sep 17 00:00:00 2001 From: Ignas Sireikis Date: Thu, 28 Aug 2025 15:44:27 -0500 Subject: [PATCH 06/91] Fixed formatting and style in Extensions. --- Sources/SunKit/Extensions.swift | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Sources/SunKit/Extensions.swift b/Sources/SunKit/Extensions.swift index bb574c3..ed4b710 100644 --- a/Sources/SunKit/Extensions.swift +++ b/Sources/SunKit/Extensions.swift @@ -1,6 +1,6 @@ // // Extensions.swift -// +// // // Copyright 2024 Leonardo Bertinelli, Davide Biancardi, Raffaele Fulgente, Clelia Iovine, Nicolas Mariniello, Fabio Pizzano // @@ -18,6 +18,7 @@ import Foundation + //It consents us too loop between two dates for n as interval time extension Date: @retroactive Strideable { public func distance(to other: Date) -> TimeInterval { @@ -31,6 +32,7 @@ extension Date: @retroactive Strideable { options: 0, locale: Locale(identifier: "en")) df.dateFormat = custom + return df.string(from: self) } @@ -51,18 +53,16 @@ extension Calendar { func startOfYear(_ date: Date) -> Date { return self.date(from: self.dateComponents([.year], from: date))! } - } extension TimeZone { - func offset(_ date: Date) -> Double { let res = Int(self.secondsFromGMT(for: date)) + Int(self.daylightSavingTimeOffset(for: date)) - Int(Calendar.current.timeZone.secondsFromGMT(for: date)) - Int(Calendar.current.timeZone.daylightSavingTimeOffset(for: date)) - return Double(res)/SECONDS_IN_ONE_HOUR + return Double(res)/SECONDS_IN_ONE_HOUR } } From 60744b1e01897bd4fbc3164db2e5ced059793898 Mon Sep 17 00:00:00 2001 From: Ignas Sireikis Date: Thu, 28 Aug 2025 15:47:31 -0500 Subject: [PATCH 07/91] Fixed formatting and style in EquatorialCoordinates. --- Sources/SunKit/EquatorialCoordinates.swift | 74 +++++++++------------- 1 file changed, 31 insertions(+), 43 deletions(-) diff --git a/Sources/SunKit/EquatorialCoordinates.swift b/Sources/SunKit/EquatorialCoordinates.swift index 7824388..2b90ab1 100644 --- a/Sources/SunKit/EquatorialCoordinates.swift +++ b/Sources/SunKit/EquatorialCoordinates.swift @@ -1,6 +1,6 @@ // // EquatorialCoordinates.swift -// +// // // Copyright 2024 Leonardo Bertinelli, Davide Biancardi, Raffaele Fulgente, Clelia Iovine, Nicolas Mariniello, Fabio Pizzano // @@ -18,66 +18,64 @@ import Foundation + public struct EquatorialCoordinates: Equatable, Hashable, Codable, Sendable { - public private(set) var rightAscension: Angle? // rightAscension.degrees refers to h format public private(set) var declination: Angle //delta private(set) var hourAngle: Angle? - init(declination: Angle,rightAscension: Angle, hourAngle: Angle){ + init(declination: Angle,rightAscension: Angle, hourAngle: Angle) { self.declination = declination self.rightAscension = rightAscension self.hourAngle = hourAngle } - init(declination: Angle,rightAscension: Angle){ + init(declination: Angle,rightAscension: Angle) { self.declination = declination self.rightAscension = rightAscension self.hourAngle = nil } - init(declination: Angle,hourAngle: Angle){ + init(declination: Angle,hourAngle: Angle) { self.declination = declination self.hourAngle = hourAngle self.rightAscension = nil } - init(declination: Angle){ + init(declination: Angle) { self.declination = declination } /// To set right ascension we need LST and right ascension. If right ascension is nill we can't set it. /// - Parameter lstDecimal: Local Sideral Time in decimal /// - Returns: The value of hour angle just been set. Nil if right ascension is also nil - public mutating func setHourAngleFrom(lstDecimal: Double) -> Angle?{ + public mutating func setHourAngleFrom(lstDecimal: Double) -> Angle? { + guard let rightAscension = self.rightAscension else { + return nil + } - guard let rightAscension = self.rightAscension else {return nil} - var hourAngleDecimal = lstDecimal - rightAscension.degrees if hourAngleDecimal < 0 { hourAngleDecimal += 24 } - self.hourAngle = .init(degrees: hourAngleDecimal * 15) - - return self.hourAngle + self.hourAngle = .init(degrees: hourAngleDecimal * 15) + + return self.hourAngle } /// To set right ascension we need LST and hour angle. If hour angle is nill we can't set it. /// - Parameter lstDecimal: Local Sideral Time in decimal /// - Returns: The value of right ascension just been set. Nil if hour angle is also nil - public mutating func setRightAscensionFrom(lstDecimal: Double) -> Angle?{ - + public mutating func setRightAscensionFrom(lstDecimal: Double) -> Angle? { guard let hourAngle = self.hourAngle else {return nil} let hourAngleDecimal = hourAngle.degrees / 15 - self.rightAscension = .init(degrees: lstDecimal - hourAngleDecimal) - + return self.rightAscension - } - - + } + /// Converts Equatorial coordinates to Horizon coordinates. /// /// Since horizon coordinates depend on the position, we need also latitude parameter to create an EquatorialCoordinates instance. @@ -86,22 +84,22 @@ public struct EquatorialCoordinates: Equatable, Hashable, Codable, Sendable { /// - lstDecimal: Local Sidereal Time in decimal format. /// - latitude: Latitude of the observer /// - Returns: Horizon coordinates for the given latitude and LST. Nil if hour angle cannot be computed due to the miss right ascnsion information - public mutating func equatorial2Horizon(lstDecimal: Double,latitude: Angle) -> HorizonCoordinates?{ - - guard let _ = setHourAngleFrom(lstDecimal: lstDecimal) else {return nil} + public mutating func equatorial2Horizon( + lstDecimal: Double, + latitude: Angle + ) -> HorizonCoordinates? { + guard let _ = setHourAngleFrom(lstDecimal: lstDecimal) else { + return nil + } //Step4: let tZeroEquatorialToHorizon = sin(declination.radians) * sin(latitude.radians) + cos(declination.radians) * cos(latitude.radians) * cos(hourAngle!.radians) - //Step5: let altitude: Angle = .init(radians: asin(tZeroEquatorialToHorizon)) - //Step6: let tOneEquatorialToHorizon = sin(declination.radians) - sin(latitude.radians) * sin(altitude.radians) - //Step7: let tTwoEquatorialToHorizon = tOneEquatorialToHorizon / (cos(latitude.radians) * cos(altitude.radians)) - //Step8: var azimuth: Angle = .init(radians: acos(tTwoEquatorialToHorizon)) if sin(hourAngle!.radians) >= 0{ @@ -118,22 +116,19 @@ public struct EquatorialCoordinates: Equatable, Hashable, Codable, Sendable { /// - Parameters: /// - latitude: Latitude of the observer /// - Returns: Horizon coordinates for the given latitude. Nil if hour angle is not defined. - public func equatorial2Horizon(latitude: Angle) -> HorizonCoordinates?{ - - guard let _ = self.hourAngle else {return nil} + public func equatorial2Horizon(latitude: Angle) -> HorizonCoordinates? { + guard let _ = self.hourAngle else { + return nil + } //Step4: let tZeroEquatorialToHorizon = sin(declination.radians) * sin(latitude.radians) + cos(declination.radians) * cos(latitude.radians) * cos(hourAngle!.radians) - //Step5: let altitude: Angle = .init(radians: asin(tZeroEquatorialToHorizon)) - //Step6: let tOneEquatorialToHorizon = sin(declination.radians) - sin(latitude.radians) * sin(altitude.radians) - //Step7: let tTwoEquatorialToHorizon = tOneEquatorialToHorizon / (cos(latitude.radians) * cos(altitude.radians)) - //Step8: var azimuth: Angle = .init(radians: acos(tTwoEquatorialToHorizon)) if sin(hourAngle!.radians) >= 0{ @@ -143,27 +138,20 @@ public struct EquatorialCoordinates: Equatable, Hashable, Codable, Sendable { return .init(altitude: altitude, azimuth: azimuth) } - public func equatorial2Ecliptic() -> EclipticCoordinates?{ - + public func equatorial2Ecliptic() -> EclipticCoordinates? { guard var rightAscension = rightAscension else {return nil} - - rightAscension.degrees = rightAscension.degrees * 15 //from h format to degrees + rightAscension.degrees = rightAscension.degrees * 15 //from h format to degrees //Step5: let tEquatorialToEcliptic: Angle = .init(radians: sin(declination.radians) * cos(EclipticCoordinates.obliquityOfTheEcliptic.radians) - cos(declination.radians) * sin(EclipticCoordinates.obliquityOfTheEcliptic.radians) * sin(rightAscension.radians)) - //Step6: let eclipticLatitude: Angle = .init(radians: asin(tEquatorialToEcliptic.radians)) - //Step7: let yEquatorialToEcliptic = sin(rightAscension.radians) * cos(EclipticCoordinates.obliquityOfTheEcliptic.radians) + tan(declination.radians) * sin(EclipticCoordinates.obliquityOfTheEcliptic.radians) - //Step8: let xEquatorialToEcliptic = cos(rightAscension.radians) - //Step9: var r: Angle = .init(radians: atan(yEquatorialToEcliptic / xEquatorialToEcliptic)) - //Step9: switch (yEquatorialToEcliptic >= 0,xEquatorialToEcliptic >= 0){ @@ -176,7 +164,7 @@ public struct EquatorialCoordinates: Equatable, Hashable, Codable, Sendable { case(false,false): r.degrees += 180 } - + let eclipticLongitude: Angle = .init(degrees: r.degrees) return .init(eclipticLatitude: eclipticLatitude, eclipticLongitude: eclipticLongitude) From d170ebdb60f4c8fcf0d3e2f08644be5febc9c08a Mon Sep 17 00:00:00 2001 From: Ignas Sireikis Date: Thu, 28 Aug 2025 15:48:44 -0500 Subject: [PATCH 08/91] Fixed formatting and style in EclipticCoordinates. --- Sources/SunKit/EclipticCoordinates.swift | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/Sources/SunKit/EclipticCoordinates.swift b/Sources/SunKit/EclipticCoordinates.swift index b0c627e..c8a75df 100644 --- a/Sources/SunKit/EclipticCoordinates.swift +++ b/Sources/SunKit/EclipticCoordinates.swift @@ -20,7 +20,6 @@ import Foundation public struct EclipticCoordinates: Equatable, Hashable, Codable, Sendable { - public static let obliquityOfTheEcliptic: Angle = .init(degrees: 23.439292) public var eclipticLatitude: Angle //beta @@ -28,23 +27,17 @@ public struct EclipticCoordinates: Equatable, Hashable, Codable, Sendable { /// Converts ecliptic coordinatates to equatorial coordinates /// - Returns: Equatorial coordinates of the instance - public func ecliptic2Equatorial() -> EquatorialCoordinates{ - + public func ecliptic2Equatorial() -> EquatorialCoordinates { //Step4: let tEclipticToEquatorial: Angle = .init(radians: sin(eclipticLatitude.radians) * cos(EclipticCoordinates.obliquityOfTheEcliptic.radians) + cos(eclipticLatitude.radians) * sin(EclipticCoordinates.obliquityOfTheEcliptic.radians) * sin(eclipticLongitude.radians)) - //Step5: let moonDeclination: Angle = .init(radians: asin(tEclipticToEquatorial.radians)) - //Step6: let yEclipticToEquatorial = sin(eclipticLongitude.radians) * cos(EclipticCoordinates.obliquityOfTheEcliptic.radians) - tan(eclipticLatitude.radians) * sin(EclipticCoordinates.obliquityOfTheEcliptic.radians) - //Step7: let xEclipticToEquatorial = cos(eclipticLongitude.radians) - //Step8: var r: Angle = .init(radians: atan(yEclipticToEquatorial / xEclipticToEquatorial)) - //Step9: switch (yEclipticToEquatorial >= 0,xEclipticToEquatorial >= 0){ From 216bf3404bdffcff6e986f8216394508288293b3 Mon Sep 17 00:00:00 2001 From: Ignas Sireikis Date: Thu, 28 Aug 2025 15:50:07 -0500 Subject: [PATCH 09/91] Fixed formatting and style in DMS. --- Sources/SunKit/DMS.swift | 21 ++++++++++----------- 1 file changed, 10 insertions(+), 11 deletions(-) diff --git a/Sources/SunKit/DMS.swift b/Sources/SunKit/DMS.swift index 7948f59..f5d9471 100644 --- a/Sources/SunKit/DMS.swift +++ b/Sources/SunKit/DMS.swift @@ -18,16 +18,20 @@ import Foundation + /// DMS format to express angles public struct DMS: Equatable, Hashable, Codable, Sendable { - public var degrees: Double public var minutes: Double public var seconds: Double public var isANegativeZero: Bool - - init(degrees: Double, minutes: Double, seconds: Double, isANegativeZero: Bool = false) { + init( + degrees: Double, + minutes: Double, + seconds: Double, + isANegativeZero: Bool = false + ) { self.degrees = degrees self.minutes = minutes self.seconds = seconds @@ -36,8 +40,7 @@ public struct DMS: Equatable, Hashable, Codable, Sendable { ///From decimal it will create the corresponding angle in DMS format /// - Parameter decimal: Decimal angle that will be converted in DMS format - public init(decimal: Double){ - + public init(decimal: Double) { //Step1: let sign = decimal < 0 ? -1 : 1 //Step2: @@ -50,26 +53,23 @@ public struct DMS: Equatable, Hashable, Codable, Sendable { let seconds = 60 * (60 * dec.truncatingRemainder(dividingBy: 1)).truncatingRemainder(dividingBy: 1) //Step6: degrees *= sign + if degrees == 0 && sign == -1 { self.degrees = Double(degrees) self.minutes = Double(minutes) self.seconds = seconds self.isANegativeZero = true - } - else{ + } else { self.degrees = Double(degrees) self.minutes = Double(minutes) self.seconds = seconds self.isANegativeZero = false } - - } /// It converts from DMS format to decimal /// - Returns: DMS of the instance expressed in decimal format public func dMS2Decimal() -> Double { - //Step1: let sign: Double = degrees < 0 ? -1 : 1 //Step2: @@ -86,7 +86,6 @@ public struct DMS: Equatable, Hashable, Codable, Sendable { decimal *= sign if isANegativeZero{ - decimal *= -1 } From e76b871b91c6256e8db1cc19144962be4952a0f6 Mon Sep 17 00:00:00 2001 From: Ignas Sireikis Date: Thu, 28 Aug 2025 15:51:01 -0500 Subject: [PATCH 10/91] Fixed formatting and style in Angle. --- Sources/SunKit/Angle.swift | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/Sources/SunKit/Angle.swift b/Sources/SunKit/Angle.swift index ec02601..8a89b04 100644 --- a/Sources/SunKit/Angle.swift +++ b/Sources/SunKit/Angle.swift @@ -16,11 +16,10 @@ // See the License for the specific language governing permissions and // limitations under the License. - import Foundation + public struct Angle: Equatable, Hashable, Codable, Sendable { - public static var zero: Angle = .init() public init() { @@ -30,11 +29,11 @@ public struct Angle: Equatable, Hashable, Codable, Sendable { public init(radians: Double) { _radians = radians } - + public init(degrees: Double) { _radians = degrees * Double.pi / 180.0 } - + public var degrees: Double { get { _radians * 180.0 / Double.pi } set { _radians = newValue * Double.pi / 180 } @@ -54,5 +53,4 @@ public struct Angle: Equatable, Hashable, Codable, Sendable { public static func radians(_ value: Double) -> Angle { .init(radians: value) } - } From 1c127e6d7fdcafb6d8cf24fecc39b1a29b7b1fa6 Mon Sep 17 00:00:00 2001 From: Ignas Sireikis Date: Thu, 28 Aug 2025 15:52:01 -0500 Subject: [PATCH 11/91] Fixed formatting and style in UT_DMS. --- Tests/SunKitTests/UT_DMS.swift | 26 +++++++++++--------------- 1 file changed, 11 insertions(+), 15 deletions(-) diff --git a/Tests/SunKitTests/UT_DMS.swift b/Tests/SunKitTests/UT_DMS.swift index d11f31a..602d0df 100644 --- a/Tests/SunKitTests/UT_DMS.swift +++ b/Tests/SunKitTests/UT_DMS.swift @@ -19,13 +19,12 @@ import XCTest @testable import SunKit + final class UT_DMS: XCTestCase { - /// Test of DMS init with decimal in input - func testOfDMSDecimailInit(){ - - //Test1: Convert −0.508333° to DMS format. The result shall be -0°30''30'. + func testOfDMSDecimailInit() { + //Test1: Convert −0.508333° to DMS format. The result shall be -0°30''30'. //Step1: Initialize dmsUnderTest to −0.508333° and saving the expected output var dmsUnderTest: DMS = .init(decimal: -0.508333) @@ -33,7 +32,7 @@ final class UT_DMS: XCTestCase { //Step2: Check if the the values set is inside the correct range and isANegativeZero is TRUE XCTAssertTrue((dmsUnderTest.degrees == 0) && (dmsUnderTest.minutes == 30) && (29.99...30).contains(dmsUnderTest.seconds) && dmsUnderTest.isANegativeZero) - //Test2: Convert 0.508333° to DMS format. The result shall be 0°30''30'. + //Test2: Convert 0.508333° to DMS format. The result shall be 0°30''30'. //Step3: Initialize dmsUnderTest to 0.508333° and saving the expected output dmsUnderTest = .init(decimal: 0.508333) @@ -41,32 +40,29 @@ final class UT_DMS: XCTestCase { //Step4: Check if the the values set is inside the correct range and isANegativeZero is FAlse XCTAssertTrue((dmsUnderTest.degrees == 0) && (dmsUnderTest.minutes == 30) && (29.99...30).contains(dmsUnderTest.seconds) && !dmsUnderTest.isANegativeZero) - //Test3: Convert -300.3333° to DMS format. The result shall be -300° 20′ 00′′. - + //Test3: Convert -300.3333° to DMS format. The result shall be -300° 20′ 00′′. + //Step5: Creating a DMS instance of and saving the expected output dmsUnderTest = .init(decimal: -300.3333) //Step6: Call function under test and check that it returns a value close to expected output XCTAssertTrue((dmsUnderTest.degrees == -300) && (19...20).contains(dmsUnderTest.minutes) && (59...59.99).contains(dmsUnderTest.seconds) && !dmsUnderTest.isANegativeZero) - } - /// Test of dMS2Decimal - func testOfdMS2Decimal(){ - - //Test1: For 300° 20′ 00′′ his decimal number should be 300.3333 + func testOfdMS2Decimal() { + //Test1: For 300° 20′ 00′′ his decimal number should be 300.3333 //Step1: Creating a DMS instance of 300° 20′ 00′′ and saving the expected output var dmsUnderTest: DMS = .init(degrees: 300, minutes: 20, seconds: 00) var expectedOutput: Double = 300.3333 //Step2: Call function under test and check that it returns a value close to expected output XCTAssertTrue(abs(dmsUnderTest.dMS2Decimal() - expectedOutput) < 0.01) - - //Test2: For -0°30''30' his decimal number should be -0.508333 + + //Test2: For -0°30''30' his decimal number should be -0.508333 //Step3: Creating a DMS instance of -0°30''30' and saving the expected output - dmsUnderTest = .init(degrees: 0, minutes: 30, seconds: 30,isANegativeZero: true) + dmsUnderTest = .init(degrees: 0, minutes: 30, seconds: 30, isANegativeZero: true) expectedOutput = -0.508333 //Step4: Call function under test and check that it returns a value close to expected output From 7a3b8268c1babd2a1318a5dedcd7d4a4ae6eb2f8 Mon Sep 17 00:00:00 2001 From: Ignas Sireikis Date: Thu, 28 Aug 2025 15:52:46 -0500 Subject: [PATCH 12/91] Fixed formatting and style in UT_EclipticCoordinates. --- Tests/SunKitTests/UT_EclipticCoordinates.swift | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/Tests/SunKitTests/UT_EclipticCoordinates.swift b/Tests/SunKitTests/UT_EclipticCoordinates.swift index c694f22..88dab5f 100644 --- a/Tests/SunKitTests/UT_EclipticCoordinates.swift +++ b/Tests/SunKitTests/UT_EclipticCoordinates.swift @@ -20,12 +20,12 @@ import XCTest @testable import SunKit import SwiftUI -final class UT_EclipticCoordinates: XCTestCase { +final class UT_EclipticCoordinates: XCTestCase { + /// Test of ecliptic2Equatorial func testOfecliptic2Equatorial() throws { - - //Test1: Convert ecliptic coordinates with latitude = −3.956258 and longitude = 65.059853. Expected Equatorial coordinates are declination = 17.248880 and right ascension = 4.257714. + //Test1: Convert ecliptic coordinates with latitude = −3.956258 and longitude = 65.059853. Expected Equatorial coordinates are declination = 17.248880 and right ascension = 4.257714. //Step1: let eclipticCoordinatesUnderTest: EclipticCoordinates = .init(eclipticLatitude: .init(degrees: -3.956258), eclipticLongitude: .init(degrees: 65.059853)) @@ -40,9 +40,5 @@ final class UT_EclipticCoordinates: XCTestCase { //Step3: Check if output of the funciton under test is close to expected output for both right ascension and declination XCTAssertTrue(abs(rightAscension - expectedRightAscension) < 0.1) XCTAssertTrue(abs(declination - expectedDeclination) < 0.1) - - - } - } From 9caf1a1e9ece8745598fd78353f65a9a2b915675 Mon Sep 17 00:00:00 2001 From: Ignas Sireikis Date: Thu, 28 Aug 2025 15:53:58 -0500 Subject: [PATCH 13/91] Fixed formatting and style in UT_EquatorialCoordinates. --- .../UT_EquatorialCoordinates.swift | 22 +++++++------------ 1 file changed, 8 insertions(+), 14 deletions(-) diff --git a/Tests/SunKitTests/UT_EquatorialCoordinates.swift b/Tests/SunKitTests/UT_EquatorialCoordinates.swift index e4496a7..32832cf 100644 --- a/Tests/SunKitTests/UT_EquatorialCoordinates.swift +++ b/Tests/SunKitTests/UT_EquatorialCoordinates.swift @@ -19,13 +19,12 @@ import XCTest @testable import SunKit -final class UT_EquatorialCoordinates: XCTestCase { +final class UT_EquatorialCoordinates: XCTestCase { /// Test of EquatorialCoordinates init func testOfInitEquatorialCoordinates() throws { - - //Test1: Consider a star whose right ascension is 3h24m06s and declination = −0°30'30''. Suppose the LST for an observer is 18h.Calculate the corresponding hour angle. + //Test1: Consider a star whose right ascension is 3h24m06s and declination = −0°30'30''. Suppose the LST for an observer is 18h.Calculate the corresponding hour angle. //Step1: let declinationUnderTest: Angle = .init(degrees: DMS.init(degrees:0 , minutes: 30, seconds: 30,isANegativeZero: true).dMS2Decimal()) @@ -39,12 +38,9 @@ final class UT_EquatorialCoordinates: XCTestCase { } - /// Test of equatorial2Horizon func testOfequatorial2Horizon() throws { - - - //Test1: Convert equatorial coordinates with declination = 17.248880 and right ascension = 4.257714. Expected horizon coordinates are altitude = 68°52 and Azimuth = 192°11′. For an observer at 38° N and LST is 4.562547h + //Test1: Convert equatorial coordinates with declination = 17.248880 and right ascension = 4.257714. Expected horizon coordinates are altitude = 68°52 and Azimuth = 192°11′. For an observer at 38° N and LST is 4.562547h //Step1: var equatorialCoordinatesUnderTest: EquatorialCoordinates = .init(declination: .init(degrees: 17.248880), rightAscension: .init(degrees: 4.257714)) @@ -62,10 +58,9 @@ final class UT_EquatorialCoordinates: XCTestCase { XCTAssertTrue(abs(azimuth - expectedAzimuth) < 0.1) XCTAssertTrue(abs(altitude - expectedAltitude) < 0.1) + //Test2: Suppose a star is located at δ = −0°30′30′′, H = 16h29m45s. For an observer at 25° N latitude. Expected output shall be Azimuth = 80°31′31′′ ,and −20°34′40′′. - //Test2: Suppose a star is located at δ = −0°30′30′′, H = 16h29m45s. For an observer at 25° N latitude. Expected output shall be Azimuth = 80°31′31′′ ,and −20°34′40′′. - - //Step4: + //Step4: let hourAngleDecimal = HMS.init(hours: 16, minutes: 29, seconds: 45).hMS2Decimal() let hourAngle: Angle = .init(degrees: hourAngleDecimal * 15) latitudeUnderTest = .degrees(25) @@ -88,8 +83,7 @@ final class UT_EquatorialCoordinates: XCTestCase { /// Test of equatorial2Ecliptic func testOfequatorial2Ecliptic() throws { - - //Test1: Given Jupiter’s equatorial coordinates of right ascension 12h18m47.5s, declination −0°43′35.5'', and the standard epoch J2000, compute Jupiter’s ecliptic coordinates.Expected output shall be eclipitc latitude = 1°12′00.0′′ and ecliptic longitude = 184°36′00.0′′ + //Test1: Given Jupiter’s equatorial coordinates of right ascension 12h18m47.5s, declination −0°43′35.5'', and the standard epoch J2000, compute Jupiter’s ecliptic coordinates.Expected output shall be eclipitc latitude = 1°12′00.0′′ and ecliptic longitude = 184°36′00.0′′ //Step1: let rightAscensionUnderTest = HMS.init(hours: 12, minutes: 18, seconds: 47.5).hMS2Decimal() @@ -101,9 +95,9 @@ final class UT_EquatorialCoordinates: XCTestCase { //Step2: Saving expected values in output for both latitude and longitude let expectedLatitude = DMS.init(degrees: 1, minutes: 12, seconds: 0).dMS2Decimal() let expectedLongitude = DMS.init(degrees: 184, minutes: 36, seconds: 0).dMS2Decimal() - + //Step3: Check if output of the function under test is close to expected output for both latitude and longitude XCTAssertTrue(abs(expectedLatitude - eclipticCoordinates.eclipticLatitude.degrees) < 0.1) XCTAssertTrue(abs(expectedLongitude - eclipticCoordinates.eclipticLongitude.degrees) < 0.1) - } + } } From 86186264a787226e4f77ab63da8537068f2e4af7 Mon Sep 17 00:00:00 2001 From: Ignas Sireikis Date: Thu, 28 Aug 2025 15:54:42 -0500 Subject: [PATCH 14/91] Fixed formatting and style in UT_HMS. --- Tests/SunKitTests/UT_HMS.swift | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/Tests/SunKitTests/UT_HMS.swift b/Tests/SunKitTests/UT_HMS.swift index 8783cdb..20f9fc8 100644 --- a/Tests/SunKitTests/UT_HMS.swift +++ b/Tests/SunKitTests/UT_HMS.swift @@ -19,12 +19,12 @@ import XCTest @testable import SunKit + final class UT_HMS: XCTestCase { /// Test of hMS2Decimal /// For 10h 25m 11s his decimal number should be 10.419722 func testOfhMS2Decimal() throws { - //Step1: Creating a HMS instance of 10h 25m 11s let hmsUnderTest: HMS = .init(hours: 10, minutes: 25, seconds: 11) let expectedOutput: Double = 10.419722 @@ -32,12 +32,10 @@ final class UT_HMS: XCTestCase { //Step2: Call function under test and check that it returns a value close to expected output XCTAssertTrue(abs(hmsUnderTest.hMS2Decimal() - expectedOutput) < 0.01) } - + /// Test of HMSDecimalInit func testOfHMSDecimalInit() throws { - - - //Test1: For 10.419722 decimal his HMS should be 10h 25m 11s + //Test1: For 10.419722 decimal his HMS should be 10h 25m 11s //Step1: Creating a HMS instance of 10.419722 decimal number var hmsUnderTest: HMS = .init(decimal: 10.419722) @@ -45,7 +43,7 @@ final class UT_HMS: XCTestCase { //Step2: Call function under test and check that it returns a value close to expected output XCTAssertTrue((hmsUnderTest.hours == 10) && (hmsUnderTest.minutes == 25) && (10.9...11.1).contains(hmsUnderTest.seconds)) - //Test2: For decimal equals 20.352 the DMS value should be equals to 20h 21m 7.2s + //Test2: For decimal equals 20.352 the DMS value should be equals to 20h 21m 7.2s //Step3: Creating a HMS instance of 10.419722 decimal number hmsUnderTest = .init(decimal: 20.352) From e61c09c7cc392abe944b72746b0291224ee36abc Mon Sep 17 00:00:00 2001 From: Ignas Sireikis Date: Thu, 28 Aug 2025 15:55:11 -0500 Subject: [PATCH 15/91] Fixed formatting and style in UT_HorizonCoordinates. --- Tests/SunKitTests/UT_HorizonCoordinates.swift | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Tests/SunKitTests/UT_HorizonCoordinates.swift b/Tests/SunKitTests/UT_HorizonCoordinates.swift index 9db4a07..0833386 100644 --- a/Tests/SunKitTests/UT_HorizonCoordinates.swift +++ b/Tests/SunKitTests/UT_HorizonCoordinates.swift @@ -19,12 +19,12 @@ import XCTest @testable import SunKit + final class UT_HorizonCoordinates: XCTestCase { /// Test of horizon2Equatorial func testOfhorizon2Equatorial() throws { - - //Test1: Converting altitude 40° and azimuth 115° to equatorial coordinates for an observer at 38° N latitude. Expected out shall be hour angle = 21.031560h and declination = 8.084044°. + //Test1: Converting altitude 40° and azimuth 115° to equatorial coordinates for an observer at 38° N latitude. Expected out shall be hour angle = 21.031560h and declination = 8.084044°. //Step1: let horizonCoordinatesUnderTest: HorizonCoordinates = .init(altitude: .degrees(40), azimuth: .degrees(115)) From 39d16bf1f8af8c95a5842d33ee8d47baa37b4e52 Mon Sep 17 00:00:00 2001 From: Ignas Sireikis Date: Thu, 28 Aug 2025 15:59:12 -0500 Subject: [PATCH 16/91] Fixed formatting and style in UT_Sun. --- Tests/SunKitTests/UT_Sun.swift | 132 ++++++++++++++------------------- 1 file changed, 56 insertions(+), 76 deletions(-) diff --git a/Tests/SunKitTests/UT_Sun.swift b/Tests/SunKitTests/UT_Sun.swift index 4d5fb9b..69f2f32 100644 --- a/Tests/SunKitTests/UT_Sun.swift +++ b/Tests/SunKitTests/UT_Sun.swift @@ -21,8 +21,8 @@ import Foundation import CoreLocation @testable import SunKit + final class UT_Sun: XCTestCase { - /*-------------------------------------------------------------------- Thresholds. UTs will pass if |output - expectedOutput| < threshold *-------------------------------------------------------------------*/ @@ -31,7 +31,7 @@ final class UT_Sun: XCTestCase { static let sunSetRiseThresholdInSeconds: Double = 120 //2 minutes in seconds static let sunEquinoxesAndSolsticesThresholdInSeconds: Double = 700 // approxametly 11 minutes - static let objectShadowThreshold: Double = 0.01 + static let objectShadowThreshold: Double = 0.01 /*-------------------------------------------------------------------- Naples timezone and location @@ -66,7 +66,6 @@ final class UT_Sun: XCTestCase { static let mumbaiLocation: CLLocation = .init(latitude: 18.94017, longitude: 72.83489) static let timeZoneMumbai = 5.5 - /// Test of Sun azimuth, sunrise, sunset, evening golden hour start and evening golden hour end /// Value for expected results have been taken from SunCalc.org func testOfSun() throws { @@ -108,30 +107,30 @@ final class UT_Sun: XCTestCase { let expectedastronomicalDusk = createDateCustomTimeZone(day: 19, month: 11, year: 2022, hour: 18, minute: 17, seconds: 20,timeZone: timeZoneUnderTest) - + //Step4: Check if the output are close to the expected ones XCTAssertTrue(abs(expectedAzimuth - sunUnderTest.azimuth.degrees) < UT_Sun.sunAzimuthThreshold) XCTAssertTrue(abs(expectedAltitude - sunUnderTest.altitude.degrees) < UT_Sun.sunAltitudeThreshold) - + XCTAssertTrue(abs(expectedSunRise.timeIntervalSince1970 - sunUnderTest.sunrise.timeIntervalSince1970) < UT_Sun.sunSetRiseThresholdInSeconds) XCTAssertTrue(abs(expectedSunset.timeIntervalSince1970 - sunUnderTest.sunset.timeIntervalSince1970) < UT_Sun.sunSetRiseThresholdInSeconds) - + XCTAssertTrue(abs(expectedGoldenHourStart.timeIntervalSince1970 - sunUnderTest.eveningGoldenHourStart.timeIntervalSince1970) < UT_Sun.sunSetRiseThresholdInSeconds) XCTAssertTrue(abs(expectedGoldenHourEnd.timeIntervalSince1970 - sunUnderTest.eveningGoldenHourEnd.timeIntervalSince1970) < UT_Sun.sunSetRiseThresholdInSeconds) - + XCTAssertTrue(abs(expectedcivilDusk.timeIntervalSince1970 - sunUnderTest.civilDusk.timeIntervalSince1970) < UT_Sun.sunSetRiseThresholdInSeconds) XCTAssertTrue(abs(expectedcivilDawn.timeIntervalSince1970 - sunUnderTest.civilDawn.timeIntervalSince1970) < UT_Sun.sunSetRiseThresholdInSeconds) - + XCTAssertTrue(abs(expectedSolarNoon.timeIntervalSince1970 - sunUnderTest.solarNoon.timeIntervalSince1970) < UT_Sun.sunSetRiseThresholdInSeconds) - + XCTAssertTrue(abs(expectednauticalDusk.timeIntervalSince1970 - sunUnderTest.nauticalDusk.timeIntervalSince1970) < UT_Sun.sunSetRiseThresholdInSeconds) XCTAssertTrue(abs(expectednauticalDawn.timeIntervalSince1970 - sunUnderTest.nauticalDawn.timeIntervalSince1970) < UT_Sun.sunSetRiseThresholdInSeconds) - + XCTAssertTrue(abs(expectedastronomicalDusk.timeIntervalSince1970 - sunUnderTest.astronomicalDusk.timeIntervalSince1970) < UT_Sun.sunSetRiseThresholdInSeconds) - + XCTAssertTrue(abs(expectedastronomicalDawn.timeIntervalSince1970 - sunUnderTest.astronomicalDawn.timeIntervalSince1970) < UT_Sun.sunSetRiseThresholdInSeconds) - + //Test: 31/12/2024 15:32. Timezone +1. Leap Year. @@ -158,21 +157,21 @@ final class UT_Sun: XCTestCase { expectedcivilDusk = createDateCustomTimeZone(day: 31, month: 12, year: 2024, hour: 17, minute: 16, seconds: 06,timeZone: timeZoneUnderTest) expectedSolarNoon = createDateCustomTimeZone(day: 31, month: 12, year: 2024, hour: 12, minute: 06, seconds: 11,timeZone: timeZoneUnderTest) - + //Step4: Check if the output are close to the expected ones XCTAssertTrue(abs(expectedAzimuth - sunUnderTest.azimuth.degrees) < UT_Sun.sunAzimuthThreshold) XCTAssertTrue(abs(expectedAltitude - sunUnderTest.altitude.degrees) < UT_Sun.sunAltitudeThreshold) - + XCTAssertTrue(abs(expectedSunRise.timeIntervalSince1970 - sunUnderTest.sunrise.timeIntervalSince1970) < UT_Sun.sunSetRiseThresholdInSeconds) XCTAssertTrue(abs(expectedSunset.timeIntervalSince1970 - sunUnderTest.sunset.timeIntervalSince1970) < UT_Sun.sunSetRiseThresholdInSeconds) - + XCTAssertTrue(abs(expectedGoldenHourStart.timeIntervalSince1970 - sunUnderTest.eveningGoldenHourStart.timeIntervalSince1970) < UT_Sun.sunSetRiseThresholdInSeconds) XCTAssertTrue(abs(expectedGoldenHourEnd.timeIntervalSince1970 - sunUnderTest.eveningGoldenHourEnd.timeIntervalSince1970) < UT_Sun.sunSetRiseThresholdInSeconds) - + XCTAssertTrue(abs(expectedcivilDusk.timeIntervalSince1970 - sunUnderTest.civilDusk.timeIntervalSince1970) < UT_Sun.sunSetRiseThresholdInSeconds) XCTAssertTrue(abs(expectedcivilDawn.timeIntervalSince1970 - sunUnderTest.civilDawn.timeIntervalSince1970) < UT_Sun.sunSetRiseThresholdInSeconds) - + XCTAssertTrue(abs(expectedSolarNoon.timeIntervalSince1970 - sunUnderTest.solarNoon.timeIntervalSince1970) < UT_Sun.sunSetRiseThresholdInSeconds) @@ -204,23 +203,23 @@ final class UT_Sun: XCTestCase { expectedcivilDusk = createDateCustomTimeZone(day: 1, month: 8, year: 2022, hour: 19, minute: 14, seconds: 00,timeZone: timeZoneUnderTest) expectedSolarNoon = createDateCustomTimeZone(day: 1, month: 8, year: 2022, hour: 11, minute: 47, seconds: 36,timeZone: timeZoneUnderTest) - + //Step4: Check if the output are close to the expected ones XCTAssertTrue(abs(expectedAzimuth - sunUnderTest.azimuth.degrees) < UT_Sun.sunAzimuthThreshold) XCTAssertTrue(abs(expectedAltitude - sunUnderTest.altitude.degrees) < UT_Sun.sunAltitudeThreshold) - + XCTAssertTrue(abs(expectedSunRise.timeIntervalSince1970 - sunUnderTest.sunrise.timeIntervalSince1970) < UT_Sun.sunSetRiseThresholdInSeconds) XCTAssertTrue(abs(expectedSunset.timeIntervalSince1970 - sunUnderTest.sunset.timeIntervalSince1970) < UT_Sun.sunSetRiseThresholdInSeconds) - + XCTAssertTrue(abs(expectedGoldenHourStart.timeIntervalSince1970 - sunUnderTest.eveningGoldenHourStart.timeIntervalSince1970) < UT_Sun.sunSetRiseThresholdInSeconds) XCTAssertTrue(abs(expectedGoldenHourEnd.timeIntervalSince1970 - sunUnderTest.eveningGoldenHourEnd.timeIntervalSince1970) < UT_Sun.sunSetRiseThresholdInSeconds) - + XCTAssertTrue(abs(expectedcivilDusk.timeIntervalSince1970 - sunUnderTest.civilDusk.timeIntervalSince1970) < UT_Sun.sunSetRiseThresholdInSeconds) XCTAssertTrue(abs(expectedcivilDawn.timeIntervalSince1970 - sunUnderTest.civilDawn.timeIntervalSince1970) < UT_Sun.sunSetRiseThresholdInSeconds) - + XCTAssertTrue(abs(expectedSolarNoon.timeIntervalSince1970 - sunUnderTest.solarNoon.timeIntervalSince1970) < UT_Sun.sunSetRiseThresholdInSeconds) - + /*-------------------------------------------------------------------- Louisa USA *-------------------------------------------------------------------*/ @@ -234,7 +233,7 @@ final class UT_Sun: XCTestCase { //Step2: Setting 1/01/15 22:00 as date. (No daylight saving) dateUnderTest = createDateCustomTimeZone(day: 1, month: 1, year: 2015, hour: 22, minute: 00, seconds: 00,timeZone: timeZoneUnderTest) sunUnderTest.setDate(dateUnderTest) - + //Step3: Saving expected outputs expectedAzimuth = 287.62 expectedAltitude = -57.41 @@ -249,21 +248,21 @@ final class UT_Sun: XCTestCase { expectedcivilDusk = createDateCustomTimeZone(day: 1, month: 1, year: 2015, hour: 17, minute: 32, seconds: 27,timeZone: timeZoneUnderTest) expectedSolarNoon = createDateCustomTimeZone(day: 1, month: 1, year: 2015, hour: 12, minute: 15, seconds: 23,timeZone: timeZoneUnderTest) - + //Step4: Check if the output are close to the expected ones XCTAssertTrue(abs(expectedAzimuth - sunUnderTest.azimuth.degrees) < UT_Sun.sunAzimuthThreshold) XCTAssertTrue(abs(expectedAltitude - sunUnderTest.altitude.degrees) < UT_Sun.sunAltitudeThreshold) - + XCTAssertTrue(abs(expectedSunRise.timeIntervalSince1970 - sunUnderTest.sunrise.timeIntervalSince1970) < UT_Sun.sunSetRiseThresholdInSeconds) XCTAssertTrue(abs(expectedSunset.timeIntervalSince1970 - sunUnderTest.sunset.timeIntervalSince1970) < UT_Sun.sunSetRiseThresholdInSeconds) - + XCTAssertTrue(abs(expectedGoldenHourStart.timeIntervalSince1970 - sunUnderTest.eveningGoldenHourStart.timeIntervalSince1970) < UT_Sun.sunSetRiseThresholdInSeconds) XCTAssertTrue(abs(expectedGoldenHourEnd.timeIntervalSince1970 - sunUnderTest.eveningGoldenHourEnd.timeIntervalSince1970) < UT_Sun.sunSetRiseThresholdInSeconds) - + XCTAssertTrue(abs(expectedcivilDusk.timeIntervalSince1970 - sunUnderTest.civilDusk.timeIntervalSince1970) < UT_Sun.sunSetRiseThresholdInSeconds) XCTAssertTrue(abs(expectedcivilDawn.timeIntervalSince1970 - sunUnderTest.civilDawn.timeIntervalSince1970) < UT_Sun.sunSetRiseThresholdInSeconds) - + XCTAssertTrue(abs(expectedSolarNoon.timeIntervalSince1970 - sunUnderTest.solarNoon.timeIntervalSince1970) < UT_Sun.sunSetRiseThresholdInSeconds) /*-------------------------------------------------------------------- @@ -290,12 +289,12 @@ final class UT_Sun: XCTestCase { expectedcivilDusk = createDateCustomTimeZone(day: 19, month: 1, year: 2022, hour: 15, minute: 05, seconds: 08,timeZone: timeZoneUnderTest) expectedSolarNoon = createDateCustomTimeZone(day: 19, month: 1, year: 2022, hour: 11, minute: 54, seconds: 52,timeZone: timeZoneUnderTest) - + //Step4: Check if the output are close to the expected ones XCTAssertTrue(abs(expectedAzimuth - sunUnderTest.azimuth.degrees) < UT_Sun.sunAzimuthThreshold) XCTAssertTrue(abs(expectedAltitude - sunUnderTest.altitude.degrees) < UT_Sun.sunAltitudeThreshold) - + XCTAssertTrue(abs(expectedSunRise.timeIntervalSince1970 - sunUnderTest.sunrise.timeIntervalSince1970) < UT_Sun.sunSetRiseThresholdInSeconds) XCTAssertTrue(abs(expectedSunset.timeIntervalSince1970 - sunUnderTest.sunset.timeIntervalSince1970) < UT_Sun.sunSetRiseThresholdInSeconds) XCTAssertTrue(abs(expectedcivilDusk.timeIntervalSince1970 - sunUnderTest.civilDusk.timeIntervalSince1970) < UT_Sun.sunSetRiseThresholdInSeconds) @@ -327,12 +326,12 @@ final class UT_Sun: XCTestCase { expectedcivilDusk = createDateCustomTimeZone(day: 12, month: 3, year: 2023, hour: 19, minute: 09, seconds: 19,timeZone: timeZoneUnderTest) expectedSolarNoon = createDateCustomTimeZone(day: 12, month: 3, year: 2023, hour: 12, minute: 48, seconds: 31,timeZone: timeZoneUnderTest) - + //Step4: Check if the output are close to the expected ones XCTAssertTrue(abs(expectedAzimuth - sunUnderTest.azimuth.degrees) < UT_Sun.sunAzimuthThreshold) XCTAssertTrue(abs(expectedAltitude - sunUnderTest.altitude.degrees) < UT_Sun.sunAltitudeThreshold) - + XCTAssertTrue(abs(expectedSunRise.timeIntervalSince1970 - sunUnderTest.sunrise.timeIntervalSince1970) < UT_Sun.sunSetRiseThresholdInSeconds) XCTAssertTrue(abs(expectedSunset.timeIntervalSince1970 - sunUnderTest.sunset.timeIntervalSince1970) < UT_Sun.sunSetRiseThresholdInSeconds) XCTAssertTrue(abs(expectedcivilDusk.timeIntervalSince1970 - sunUnderTest.civilDusk.timeIntervalSince1970) < UT_Sun.sunSetRiseThresholdInSeconds) @@ -342,51 +341,42 @@ final class UT_Sun: XCTestCase { //Test for issue #26 in github guard let pst = TimeZone(abbreviation: "PST") else { - abort() - } - - guard let utc = TimeZone(abbreviation: "UTC") else { - abort() - } - - let location: CLLocation = .init(latitude: 34.052235, longitude: -118.243683) - - var sun = Sun.init(location: location, timeZone: pst) - - sun.setDate(SunKit.createDateCustomTimeZone(day: 11, month: 3, year: 2023, hour: 22, minute: 00, seconds: 00, timeZone: pst)) - XCTAssertEqual(sun.sunrise.toString(pst), "03/11, 06:08") - XCTAssertEqual(sun.sunset.toString(pst), "03/11, 17:58") - XCTAssertEqual(sun.sunrise.toString(utc), "03/11, 14:08") - XCTAssertEqual(sun.sunset.toString(utc), "03/12, 01:58") - - sun.setDate(SunKit.createDateCustomTimeZone(day: 13, month: 3, year: 2023, hour: 22, minute: 00, seconds: 00, timeZone: pst)) - XCTAssertEqual(sun.sunrise.toString(pst), "03/13, 07:06") - XCTAssertEqual(sun.sunset.toString(pst), "03/13, 18:59") - XCTAssertEqual(sun.sunrise.toString(utc), "03/13, 14:06") - XCTAssertEqual(sun.sunset.toString(utc), "03/14, 01:59") + abort() + } + + guard let utc = TimeZone(abbreviation: "UTC") else { + abort() + } + + let location: CLLocation = .init(latitude: 34.052235, longitude: -118.243683) + + var sun = Sun.init(location: location, timeZone: pst) + + sun.setDate(SunKit.createDateCustomTimeZone(day: 11, month: 3, year: 2023, hour: 22, minute: 00, seconds: 00, timeZone: pst)) + XCTAssertEqual(sun.sunrise.toString(pst), "03/11, 06:08") + XCTAssertEqual(sun.sunset.toString(pst), "03/11, 17:58") + XCTAssertEqual(sun.sunrise.toString(utc), "03/11, 14:08") + XCTAssertEqual(sun.sunset.toString(utc), "03/12, 01:58") + sun.setDate(SunKit.createDateCustomTimeZone(day: 13, month: 3, year: 2023, hour: 22, minute: 00, seconds: 00, timeZone: pst)) + XCTAssertEqual(sun.sunrise.toString(pst), "03/13, 07:06") + XCTAssertEqual(sun.sunset.toString(pst), "03/13, 18:59") + XCTAssertEqual(sun.sunrise.toString(utc), "03/13, 14:06") + XCTAssertEqual(sun.sunset.toString(utc), "03/14, 01:59") } - func testOfObjectShadow() throws{ - + func testOfObjectShadow() throws { //Step1: Creating sun instance in Naples and with timezone +1 (No daylight saving) let timeZoneUnderTest: TimeZone = .init(secondsFromGMT: UT_Sun.timeZoneNaples * Int(SECONDS_IN_ONE_HOUR)) ?? .current -// let timeZoneDaylightSaving: TimeZone = .init(secondsFromGMT: UT_Sun.timeZoneNaplesDaylightSaving * Int(SECONDS_IN_ONE_HOUR)) ?? .current + // let timeZoneDaylightSaving: TimeZone = .init(secondsFromGMT: UT_Sun.timeZoneNaplesDaylightSaving * Int(SECONDS_IN_ONE_HOUR)) ?? .current let sunUnderTest = Sun.init(location: UT_Sun.naplesLocation, timeZone: timeZoneUnderTest) - - XCTAssertTrue(abs(sunUnderTest.shadowLength(with: .init(degrees: 43.40))! - 1.06 ) <= UT_Sun.objectShadowThreshold) - XCTAssertTrue(sunUnderTest.shadowLength(with: .init(degrees: -0.01)) == nil) - XCTAssertTrue(sunUnderTest.shadowLength(with: .init(degrees: 90)) == 0) - - } func testOfEquinoxesAndSolstices() throws { - //Test: 19/01/22 17:31. Timezone +1. Naples //Step1: Creating sun instance in Naples and with timezone +1 (No daylight saving) @@ -399,31 +389,24 @@ final class UT_Sun: XCTestCase { sunUnderTest.setDate(dateUnderTest) //Step3: Saving expected outputs - let expectedMarchEquinox = createDateCustomTimeZone(day: 20, month: 3, year: 2022, hour: 16, minute: 33, seconds: 00,timeZone: timeZoneUnderTest) let expectedJuneSolstice = createDateCustomTimeZone(day: 21, month: 6, year: 2022, hour: 11, minute: 13, seconds: 00,timeZone: timeZoneDaylightSaving) let expectedSeptemberEquinox = createDateCustomTimeZone(day: 23, month: 09, year: 2022, hour: 03, minute: 03, seconds: 00,timeZone: timeZoneDaylightSaving) let expectedDecemberSolstice = createDateCustomTimeZone(day: 21, month: 12, year: 2022, hour: 22, minute: 47, seconds: 00,timeZone: timeZoneUnderTest) - + //Step4: Check if the output are close to the expected ones XCTAssertTrue(abs(expectedMarchEquinox.timeIntervalSince1970 - sunUnderTest.marchEquinox.timeIntervalSince1970) < UT_Sun.sunEquinoxesAndSolsticesThresholdInSeconds) - XCTAssertTrue(abs(expectedJuneSolstice.timeIntervalSince1970 - sunUnderTest.juneSolstice.timeIntervalSince1970) < UT_Sun.sunEquinoxesAndSolsticesThresholdInSeconds) - XCTAssertTrue(abs(expectedSeptemberEquinox.timeIntervalSince1970 - sunUnderTest.septemberEquinox.timeIntervalSince1970) < UT_Sun.sunEquinoxesAndSolsticesThresholdInSeconds) - XCTAssertTrue(abs(expectedDecemberSolstice.timeIntervalSince1970 - sunUnderTest.decemberSolstice.timeIntervalSince1970) < UT_Sun.sunEquinoxesAndSolsticesThresholdInSeconds) - } - /// Test of a sun ionstance whenm you play with timezones and change location func testOfSunWhenTimezoneChanges() throws { - //Step1: Creating Sun instance in Naples and with timezone +1 let timeZoneNaples: TimeZone = .init(secondsFromGMT: UT_Sun.timeZoneNaples * Int(SECONDS_IN_ONE_HOUR)) ?? .current var sunUnderTest = Sun.init(location: UT_Sun.naplesLocation, timeZone: timeZoneNaples) @@ -441,11 +424,8 @@ final class UT_Sun: XCTestCase { //Step5: Check if output of sunUnderTest.date matches the expected output XCTAssertTrue(expectedDate == sunUnderTest.date) - } - - func testPerformance() throws { // Performance of setDate function that will refresh all the sun variables From 3c0b9ed02b87ea3fe6c4415fd8d5639db209937d Mon Sep 17 00:00:00 2001 From: Ignas Sireikis Date: Thu, 28 Aug 2025 16:01:03 -0500 Subject: [PATCH 17/91] Fixed formatting and style in UT_Utils. Completed first pass refactoring. --- Tests/SunKitTests/UT_Utils.swift | 63 +++++++++++++------------------- 1 file changed, 25 insertions(+), 38 deletions(-) diff --git a/Tests/SunKitTests/UT_Utils.swift b/Tests/SunKitTests/UT_Utils.swift index 3e80aee..307a94c 100644 --- a/Tests/SunKitTests/UT_Utils.swift +++ b/Tests/SunKitTests/UT_Utils.swift @@ -21,21 +21,19 @@ import XCTest final class UT_Utils: XCTestCase { - /// Test of mod func testOfmod() throws { - - //Test1: -100 % 4 shall be equal to 4 + //Test1: -100 % 4 shall be equal to 4 //Step1: call function under test and check that it returns 4 var a = -100 var n = 8 XCTAssertTrue(4 == mod(a, n)) - //Test2: -400 % 360 shall be equal to 320 + //Test2: -400 % 360 shall be equal to 320 //Step1: call function under test and check that it returns 320 a = -400 n = 360 XCTAssertTrue(320 == mod(a, n)) - //Test3: 270 % 180 shall be equal to 90 + //Test3: 270 % 180 shall be equal to 90 //Step1: call function under test and check that it returns 90 a = 270 n = 180 @@ -43,9 +41,8 @@ final class UT_Utils: XCTestCase { } /// Test of jdFromDate - func testOfjdFromDate() throws{ - - //Test1: For 5/6/2010 at noon UT, his JD number shall be 2455323.0 + func testOfjdFromDate() throws { + //Test1: For 5/6/2010 at noon UT, his JD number shall be 2455323.0 //Step1: Creating UTC date let dateUnderTest = createDateUTC(day: 6, month: 5, year: 2010, hour: 12, minute: 0, seconds: 0) @@ -54,9 +51,8 @@ final class UT_Utils: XCTestCase { } /// Test of dateFromJD - func testOfdateFromJD() throws{ - - //Test1: 2455323.0 Jd number shall be quals to date 5/6/2010 at noon UT + func testOfdateFromJD() throws { + //Test1: 2455323.0 Jd number shall be quals to date 5/6/2010 at noon UT //Step1: Creating jd number under test let jdNumberTest = 2455323.0 @@ -64,46 +60,42 @@ final class UT_Utils: XCTestCase { let expectedOutput = createDateUTC(day: 6, month: 5, year: 2010, hour: 12, minute: 0, seconds: 0) XCTAssertTrue(expectedOutput == dateFromJd(jd: jdNumberTest)) } - + /// Test of extendMod func testOfExtendedMod() throws { - - //Test4: -270.8 % 180 shall be equal to 89.2 + //Test4: -270.8 % 180 shall be equal to 89.2 var a: Double = -270.8 var n = 180 - XCTAssertTrue(abs(89.2 - extendedMod(a,n)) < 0.1) - - //Test2: -0.8 % 1 shall be equal to 0.2 + + //Test2: -0.8 % 1 shall be equal to 0.2 a = -0.8 n = 1 - XCTAssertTrue(abs(0.2 - extendedMod(a,n)) < 0.1) - - //Test3: 390.5 % 360 shall be equal to 30.5 + + //Test3: 390.5 % 360 shall be equal to 30.5 a = 390.5 n = 360 - XCTAssertTrue(30.5 == extendedMod(a,n)) - - //Test4: 0.3 % 1 shall be equal to 0.3 + + //Test4: 0.3 % 1 shall be equal to 0.3 a = 0.3 n = 1 - XCTAssertTrue(0.3 == extendedMod(a,n)) - - //Test1: -100 % 4 shall be equal to 4 + + //Test1: -100 % 4 shall be equal to 4 //Step1: call function under test and check that it returns 4 a = -100 n = 8 - XCTAssertTrue(4 == extendedMod(a, n)) - //Test2: -400 % 360 shall be equal to 320 + + //Test2: -400 % 360 shall be equal to 320 //Step1: call function under test and check that it returns 320 a = -400 n = 360 XCTAssertTrue(320 == extendedMod(a, n)) - //Test3: 270 % 180 shall be equal to 90 + + //Test3: 270 % 180 shall be equal to 90 //Step1: call function under test and check that it returns 90 a = 270 n = 180 @@ -111,8 +103,7 @@ final class UT_Utils: XCTestCase { } /// Test of uT2GST - func testOfuT2GST() throws{ - + func testOfuT2GST() throws { //Test2: Convert 23h30m00s UT to GST for February 7, 2010. UseSameTimeZone equals False. //Step1: Creating 7/02/2010 23h30m UTC @@ -126,9 +117,8 @@ final class UT_Utils: XCTestCase { } /// Test of gST2LST - func testOfgST2LST() throws{ - - //Test1: Converting GST to LST requires knowing an observer’s longitude. Assume that the GST is 2h03m41s for an observer at 40° W longitude. + func testOfgST2LST() throws { + //Test1: Converting GST to LST requires knowing an observer’s longitude. Assume that the GST is 2h03m41s for an observer at 40° W longitude. //Step1: Creating 7/02/2010 2h03m41s with current time zone(i.e the one set on your device) let gstHMS: HMS = .init(hours: 2, minutes: 03, seconds: 41) @@ -139,10 +129,7 @@ final class UT_Utils: XCTestCase { //Step3: Call function under test and check that it returns a value which differs from expected output up to 0.01 let output = gST2LST(gstHMS, longitude: longitudeUnderTest).hMS2Decimal() - XCTAssert(abs(expectedOutput - output) < 0.01) + XCTAssert(abs(expectedOutput - output) < 0.01) } - - - } From 63853dcd66fc897806891f8bd1ef3189b90bdccb Mon Sep 17 00:00:00 2001 From: Ignas Sireikis Date: Fri, 19 Sep 2025 11:12:10 -0500 Subject: [PATCH 18/91] Added sun folder. --- Sources/SunKit/{ => Sun}/Sun.swift | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename Sources/SunKit/{ => Sun}/Sun.swift (100%) diff --git a/Sources/SunKit/Sun.swift b/Sources/SunKit/Sun/Sun.swift similarity index 100% rename from Sources/SunKit/Sun.swift rename to Sources/SunKit/Sun/Sun.swift From 04c28b4fbfe0fe973d20867a0792ec22caddc1f9 Mon Sep 17 00:00:00 2001 From: Ignas Sireikis Date: Fri, 19 Sep 2025 11:25:39 -0500 Subject: [PATCH 19/91] Created debugging utils file. Moved functions. --- .../Sun/Debugging Utils/Debugging Utils.swift | 59 +++++++++++++++++++ Sources/SunKit/Sun/Sun.swift | 41 ------------- 2 files changed, 59 insertions(+), 41 deletions(-) create mode 100644 Sources/SunKit/Sun/Debugging Utils/Debugging Utils.swift diff --git a/Sources/SunKit/Sun/Debugging Utils/Debugging Utils.swift b/Sources/SunKit/Sun/Debugging Utils/Debugging Utils.swift new file mode 100644 index 0000000..8df3173 --- /dev/null +++ b/Sources/SunKit/Sun/Debugging Utils/Debugging Utils.swift @@ -0,0 +1,59 @@ +// +// Debugging Utils.swift +// +// +// Copyright 2024 Leonardo Bertinelli, Davide Biancardi, Raffaele Fulgente, Clelia Iovine, Nicolas Mariniello, Fabio Pizzano +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +import Foundation + + +extension Sun { + /// Dumps all the Sun Events dates. + public func dumpDateInfos() { + print("Current Date -> \(dateFormatter.string(from: date))") + print("Sunrise -> \(dateFormatter.string(from: sunrise))") + print("Sunset -> \(dateFormatter.string(from: sunset))") + print("Solar Noon -> \(dateFormatter.string(from: solarNoon))") + print("Solar Midnight -> \(dateFormatter.string(from: solarMidnight))") + print("Evening Golden Hour Start -> \(dateFormatter.string(from: eveningGoldenHourStart))") + print("Evening Golden Hour End -> \(dateFormatter.string(from: eveningGoldenHourEnd))") + print("Morning Golden Hour Start -> \(dateFormatter.string(from: morningGoldenHourStart))") + print("Morning Golden Hour End -> \(dateFormatter.string(from: morningGoldenHourEnd))") + print("Civil dusk -> \(dateFormatter.string(from: civilDusk))") + print("Civil Dawn -> \(dateFormatter.string(from: civilDawn))") + print("Nautical Dusk -> \(dateFormatter.string(from: nauticalDusk))") + print("Nautical Dawn -> \(dateFormatter.string(from: nauticalDawn))") + print("Astronomical Dusk -> \(dateFormatter.string(from: astronomicalDusk))") + print("Astronomical Dawn -> \(dateFormatter.string(from: astronomicalDawn))") + print("Morning Blue Hour Start -> \(dateFormatter.string(from: morningBlueHourStart))") + print("Morning Blue Hour End -> \(dateFormatter.string(from: morningBlueHourEnd))") + print("evening Blue Hour Start -> \(dateFormatter.string(from: eveningBlueHourStart))") + print("evening Blue Hour End -> \(dateFormatter.string(from: eveningBlueHourEnd))") + + print("March Equinox -> \(dateFormatter.string(from: marchEquinox))") + print("June Solstice -> \(dateFormatter.string(from: juneSolstice))") + print("September Equinox -> \(dateFormatter.string(from: septemberEquinox))") + print("December Solstice -> \(dateFormatter.string(from: decemberSolstice))") + } + + private var dateFormatter: DateFormatter { + let dateFormatter = DateFormatter() + dateFormatter.locale = .current + dateFormatter.timeZone = self.timeZone + dateFormatter.timeStyle = .full + dateFormatter.dateStyle = .full + return dateFormatter + } +} diff --git a/Sources/SunKit/Sun/Sun.swift b/Sources/SunKit/Sun/Sun.swift index 2da58da..d976a9f 100644 --- a/Sources/SunKit/Sun/Sun.swift +++ b/Sources/SunKit/Sun/Sun.swift @@ -315,38 +315,6 @@ public struct Sun: Identifiable, Sendable { refresh() } - /*-------------------------------------------------------------------- - Debug functions - *-------------------------------------------------------------------*/ - - /// Dumps all the Sun Events dates - public func dumpDateInfos(){ - print("Current Date -> \(dateFormatter.string(from: date))") - print("Sunrise -> \(dateFormatter.string(from: sunrise))") - print("Sunset -> \(dateFormatter.string(from: sunset))") - print("Solar Noon -> \(dateFormatter.string(from: solarNoon))") - print("Solar Midnight -> \(dateFormatter.string(from: solarMidnight))") - print("Evening Golden Hour Start -> \(dateFormatter.string(from: eveningGoldenHourStart))") - print("Evening Golden Hour End -> \(dateFormatter.string(from: eveningGoldenHourEnd))") - print("Morning Golden Hour Start -> \(dateFormatter.string(from: morningGoldenHourStart))") - print("Morning Golden Hour End -> \(dateFormatter.string(from: morningGoldenHourEnd))") - print("Civil dusk -> \(dateFormatter.string(from: civilDusk))") - print("Civil Dawn -> \(dateFormatter.string(from: civilDawn))") - print("Nautical Dusk -> \(dateFormatter.string(from: nauticalDusk))") - print("Nautical Dawn -> \(dateFormatter.string(from: nauticalDawn))") - print("Astronomical Dusk -> \(dateFormatter.string(from: astronomicalDusk))") - print("Astronomical Dawn -> \(dateFormatter.string(from: astronomicalDawn))") - print("Morning Blue Hour Start -> \(dateFormatter.string(from: morningBlueHourStart))") - print("Morning Blue Hour End -> \(dateFormatter.string(from: morningBlueHourEnd))") - print("evening Blue Hour Start -> \(dateFormatter.string(from: eveningBlueHourStart))") - print("evening Blue Hour End -> \(dateFormatter.string(from: eveningBlueHourEnd))") - - print("March Equinox -> \(dateFormatter.string(from: marchEquinox))") - print("June Solstice -> \(dateFormatter.string(from: juneSolstice))") - print("September Equinox -> \(dateFormatter.string(from: septemberEquinox))") - print("December Solstice -> \(dateFormatter.string(from: decemberSolstice))") - } - /*-------------------------------------------------------------------- Private Variables *-------------------------------------------------------------------*/ @@ -358,15 +326,6 @@ public struct Sun: Identifiable, Sendable { return calendar } - private var dateFormatter: DateFormatter { - let dateFormatter = DateFormatter() - dateFormatter.locale = .current - dateFormatter.timeZone = self.timeZone - dateFormatter.timeStyle = .full - dateFormatter.dateStyle = .full - return dateFormatter - } - private var timeZoneInSeconds: Int { timeZone.secondsFromGMT(for: self.date) } From 8474d85c40daf4c3f305849c5d0dced0ecef5e8e Mon Sep 17 00:00:00 2001 From: Ignas Sireikis Date: Fri, 19 Sep 2025 11:26:20 -0500 Subject: [PATCH 20/91] Code cleanup. --- Sources/SunKit/Sun/Debugging Utils/Debugging Utils.swift | 1 + 1 file changed, 1 insertion(+) diff --git a/Sources/SunKit/Sun/Debugging Utils/Debugging Utils.swift b/Sources/SunKit/Sun/Debugging Utils/Debugging Utils.swift index 8df3173..a3a4fe6 100644 --- a/Sources/SunKit/Sun/Debugging Utils/Debugging Utils.swift +++ b/Sources/SunKit/Sun/Debugging Utils/Debugging Utils.swift @@ -54,6 +54,7 @@ extension Sun { dateFormatter.timeZone = self.timeZone dateFormatter.timeStyle = .full dateFormatter.dateStyle = .full + return dateFormatter } } From 4557f3d5d9ddf97804da91c5ecfb5a1fe4c0ba11 Mon Sep 17 00:00:00 2001 From: Ignas Sireikis Date: Fri, 19 Sep 2025 11:37:40 -0500 Subject: [PATCH 21/91] Removed or shortened redundant comments. --- Sources/SunKit/Sun/Sun.swift | 149 ++++++++--------------------------- 1 file changed, 35 insertions(+), 114 deletions(-) diff --git a/Sources/SunKit/Sun/Sun.swift b/Sources/SunKit/Sun/Sun.swift index d976a9f..175bf64 100644 --- a/Sources/SunKit/Sun/Sun.swift +++ b/Sources/SunKit/Sun/Sun.swift @@ -35,43 +35,28 @@ public struct Sun: Identifiable, Sendable { Sun Events during the day *-------------------------------------------------------------------*/ - ///Date of Sunrise public private(set) var sunrise: Date = Date() - ///Date of Sunset public private(set) var sunset: Date = Date() - ///Date of Solar Noon for public private(set) var solarNoon: Date = Date() - ///Date of Solar Midnight public private(set) var solarMidnight: Date = Date() - ///Date at which evening evening Golden hour starts public private(set) var eveningGoldenHourStart: Date = Date() - ///Date at which evening evening Golden hour ends public private(set) var eveningGoldenHourEnd: Date = Date() - ///Date at which evening Morning Golden hour starts public private(set) var morningGoldenHourStart: Date = Date() - ///Date at which evening Morning Golden hour ends public private(set) var morningGoldenHourEnd: Date = Date() - - ///Date at which there is the Civil Dusk public private(set) var civilDusk: Date = Date() - ///Date at which there is the Civil Dawn public private(set) var civilDawn: Date = Date() - ///Date at which there is the Nautical Dusk public private(set) var nauticalDusk: Date = Date() - ///Date at which there is the Nautical Dawn public private(set) var nauticalDawn: Date = Date() - ///Date at which there is the Astronomical Dusk public private(set) var astronomicalDusk: Date = Date() - ///Date at which there is the Astronomical Dawn public private(set) var astronomicalDawn: Date = Date() ///Date at which morning Blue Hour starts. Sun at -6 degrees elevation = civil dusk - public var morningBlueHourStart: Date{ + public var morningBlueHourStart: Date { civilDawn } @@ -81,7 +66,7 @@ public struct Sun: Identifiable, Sendable { } ///Date at which evening Blue Hour starts. Sun at -4 degrees elevation = evening golden hour end - public var eveningBlueHourStart: Date{ + public var eveningBlueHourStart: Date { eveningGoldenHourEnd } @@ -95,11 +80,8 @@ public struct Sun: Identifiable, Sendable { Sun Azimuths for Self.date and for Sunrise,Sunset and Solar Noon *-------------------------------------------------------------------*/ - ///Azimuth of Sunrise public private(set) var sunriseAzimuth: Double = 0 - ///Azimuth of Sunset public private(set) var sunsetAzimuth: Double = 0 - ///Azimuth of Solar noon public private(set) var solarNoonAzimuth: Double = 0 // Sun azimuth for (Location,Date) in Self @@ -119,13 +101,9 @@ public struct Sun: Identifiable, Sendable { Sun Events during the year *-------------------------------------------------------------------*/ - ///Date at which there will be march equinox public private(set) var marchEquinox: Date = Date() - ///Date at which there will be june solstice public private(set) var juneSolstice: Date = Date() - ///Date at which there will be september solstice public private(set) var septemberEquinox: Date = Date() - ///Date at which there will be december solstice public private(set) var decemberSolstice: Date = Date() /*-------------------------------------------------------------------- @@ -142,14 +120,14 @@ public struct Sun: Identifiable, Sendable { .init(degrees: location.coordinate.latitude) } - /// Returns daylight time in seconds + /// Returns daylight time in seconds. public var totalDayLightTime: Int { let diffComponents = calendar.dateComponents([.second], from: sunrise, to: sunset) return diffComponents.second ?? 0 } - /// Returns night time in seconds + /// Returns night time in seconds. public var totalNightTime: Int { let startOfTheDay = calendar.startOfDay(for: date) let endOfTheDay = calendar.date(bySettingHour: 23, minute: 59, second: 59, of: startOfTheDay)! @@ -421,7 +399,6 @@ public struct Sun: Identifiable, Sendable { } private func getSunMeanAnomaly(from elapsedDaysSinceStandardEpoch: Double) -> Angle { - //Compute mean anomaly sun var sunMeanAnomaly: Angle = .init(degrees:(((360.0 * elapsedDaysSinceStandardEpoch) / 365.242191) + sunEclipticLongitudeAtTheEpoch.degrees - sunEclipticLongitudePerigee.degrees)) sunMeanAnomaly = .init(degrees: extendedMod(sunMeanAnomaly.degrees, 360)) @@ -429,7 +406,6 @@ public struct Sun: Identifiable, Sendable { } private func getSunEclipticLongitude(from sunMeanAnomaly: Angle) -> Angle { - //eclipticLatitude let equationOfCenter = 360 / Double.pi * sin(sunMeanAnomaly.radians) * 0.016708 let trueAnomaly = sunMeanAnomaly.degrees + equationOfCenter var eclipticLatitude: Angle = .init(degrees: trueAnomaly + sunEclipticLongitudePerigee.degrees) @@ -443,122 +419,84 @@ public struct Sun: Identifiable, Sendable { /// Updates Horizon coordinates, Ecliptic coordinates and Equatorial coordinates of the Sun private mutating func updateSunCoordinates() { - //Step1: - //Convert LCT to UT, GST, and LST times and adjust the date if needed + // Convert LCT to UT, GST, and LST times and adjust the date if needed let gstHMS = uT2GST(self.date) let lstHMS = gST2LST(gstHMS,longitude: longitude) - let lstDecimal = lstHMS.hMS2Decimal() - - //Step2: - //Julian number for standard epoch 2000 + // Julian number for standard epoch 2000 let jdEpoch = 2451545.00 - - //Step3: - //Compute the Julian day number for the desired date using the Greenwich date and TT - + // Compute the Julian day number for the desired date using the Greenwich date and TT let jdTT = jdFromDate(date: self.date) - - //Step5: - //Compute the total number of elapsed days, including fractional days, since the standard epoch (i.e., JD − JDe) + // Compute the total number of elapsed days, including fractional days, since the standard epoch (i.e., JD − JDe) let elapsedDaysSinceStandardEpoch: Double = jdTT - jdEpoch //De - - //Step6: Use the algorithm from section 6.2.3 to calculate the Sun’s ecliptic longitude and mean anomaly for the given UT date and time. + // Use the algorithm from section 6.2.3 to calculate the Sun’s ecliptic longitude and mean anomaly for the given UT date and time. let sunMeanAnomaly = getSunMeanAnomaly(from: elapsedDaysSinceStandardEpoch) - - //Step7: Use Equation 6.2.4 to aproximate the Equation of the center + // Use Equation 6.2.4 to aproximate the Equation of the center let equationOfCenter = 360 / Double.pi * sin(sunMeanAnomaly.radians) * 0.016708 - - //Step8: Add EoC to sun mean anomaly to get the sun true anomaly + // Add EoC to sun mean anomaly to get the sun true anomaly var sunTrueAnomaly = sunMeanAnomaly.degrees + equationOfCenter - - //Step9: sunTrueAnomaly = extendedMod(sunTrueAnomaly, 360) - - //Step10: var sunEclipticLongitude: Angle = .init(degrees: sunTrueAnomaly + sunEclipticLongitudePerigee.degrees) - //Step11: if sunEclipticLongitude.degrees > 360 { sunEclipticLongitude.degrees -= 360 } sunEclipticCoordinates = .init(eclipticLatitude: .zero, eclipticLongitude: sunEclipticLongitude) - - //Step12: Ecliptic to Equatorial + // Ecliptic to Equatorial sunEquatorialCoordinates = sunEclipticCoordinates.ecliptic2Equatorial() - - //Step13: Equatorial to Horizon + // Equatorial to Horizon sunHorizonCoordinates = sunEquatorialCoordinates.equatorial2Horizon(lstDecimal: lstDecimal,latitude: latitude) ?? .init(altitude: .zero, azimuth: .zero) } public func getSunHorizonCoordinatesFrom(date: Date) -> HorizonCoordinates { - //Step1: - //Convert LCT to UT, GST, and LST times and adjust the date if needed + // Convert LCT to UT, GST, and LST times and adjust the date if needed let gstHMS = uT2GST(date) let lstHMS = gST2LST(gstHMS,longitude: longitude) - let lstDecimal = lstHMS.hMS2Decimal() - //Step2: - //Julian number for standard epoch 2000 + // Julian number for standard epoch 2000 let jdEpoch = 2451545.00 - //Step3: - //Compute the Julian day number for the desired date using the Greenwich date and TT - + // Compute the Julian day number for the desired date using the Greenwich date and TT let jdTT = jdFromDate(date: date) - - //Step5: - //Compute the total number of elapsed days, including fractional days, since the standard epoch (i.e., JD − JDe) + // Compute the total number of elapsed days, including fractional days, since the standard epoch (i.e., JD − JDe) let elapsedDaysSinceStandardEpoch: Double = jdTT - jdEpoch //De - - //Step6: Use the algorithm from section 6.2.3 to calculate the Sun’s ecliptic longitude and mean anomaly for the given UT date and time. + // Use the algorithm from section 6.2.3 to calculate the Sun’s ecliptic longitude and mean anomaly for the given UT date and time. let sunMeanAnomaly = getSunMeanAnomaly(from: elapsedDaysSinceStandardEpoch) - - //Step7: Use Equation 6.2.4 to aproximate the Equation of the center + // Use Equation 6.2.4 to aproximate the Equation of the center let equationOfCenter = 360 / Double.pi * sin(sunMeanAnomaly.radians) * 0.016708 - - //Step8: Add EoC to sun mean anomaly to get the sun true anomaly + // Add EoC to sun mean anomaly to get the sun true anomaly var sunTrueAnomaly = sunMeanAnomaly.degrees + equationOfCenter - - //Step9: Add or subtract multiples of 360° to adjust sun true anomaly to the range of 0° to 360° + // Add or subtract multiples of 360° to adjust sun true anomaly to the range of 0° to 360° sunTrueAnomaly = extendedMod(sunTrueAnomaly, 360) - - - //Step10: Getting ecliptic longitude. + // Getting ecliptic longitude. var sunEclipticLongitude: Angle = .init(degrees: sunTrueAnomaly + sunEclipticLongitudePerigee.degrees) - //Step11: if sunEclipticLongitude.degrees > 360 { sunEclipticLongitude.degrees -= 360 } let sunEclipticCoordinates: EclipticCoordinates = .init(eclipticLatitude: .zero, eclipticLongitude: sunEclipticLongitude) - - //Step12: Ecliptic to Equatorial + // Ecliptic to Equatorial var sunEquatorialCoordinates: EquatorialCoordinates = sunEclipticCoordinates.ecliptic2Equatorial() - - //Step13: Equatorial to Horizon + // Equatorial to Horizon let sunHorizonCoordinates: HorizonCoordinates = sunEquatorialCoordinates.equatorial2Horizon(lstDecimal: lstDecimal,latitude: latitude) ?? .init(altitude: .zero, azimuth: .zero) return .init(altitude: sunHorizonCoordinates.altitude, azimuth: sunHorizonCoordinates.azimuth) } - /// Computes the solar noon for self.date. Solar noon is the time when the sun is highest in the sky. - /// - Returns: Solar noon time + /// Solar Noon is the time when the Sun is highest in the sky. private func getSolarNoon() -> Date? { let secondsForUTCSolarNoon = (720 - 4 * location.coordinate.longitude - equationOfTime) * 60 let secondsForSolarNoon = secondsForUTCSolarNoon + Double(timeZoneInSeconds) let startOfTheDay = calendar.startOfDay(for: date) - let solarNoon = calendar.date(byAdding: .second, value: Int(secondsForSolarNoon) , to: startOfTheDay) return solarNoon } /// Computes the solar midnight for self.date. - /// - Returns: Solar midnight time private func getSolarMidnight() -> Date? { let secondsForUTCSolarMidnight = (0 - 4 * location.coordinate.longitude - equationOfTime) * 60 let secondsForSolarMidnight = secondsForUTCSolarMidnight + Double(timeZoneInSeconds) @@ -569,8 +507,7 @@ public struct Sun: Identifiable, Sendable { return solarMidnight } - /// Computes the Sunrise time for self.date - /// - Returns: Sunrise time + /// Sunrise is when the Sun reaches 0 degrees of elevation, aka the horizon, at the start of the day. private func getSunrise() -> Date? { var haArg = (cos(Angle.degrees(90.833).radians)) / (cos(latitude.radians) * cos(sunEquatorialCoordinates.declination.radians)) - tan(latitude.radians) * tan(sunEquatorialCoordinates.declination.radians) @@ -585,14 +522,12 @@ public struct Sun: Identifiable, Sendable { } let hoursMinutesSeconds: (Int, Int, Int) = secondsToHoursMinutesSeconds(Int(sunriseSeconds)) - let sunriseDate = calendar.date(bySettingHour: hoursMinutesSeconds.0, minute: hoursMinutesSeconds.1, second: hoursMinutesSeconds.2, of: startOfDay) return sunriseDate } - /// Computes the Sunset time for self.date - /// - Returns: Sunset time + /// Sunset is when the Sun reaches 0 degrees of elevation, aka the horizon, at the end of the day. private func getSunset() -> Date? { var haArg = (cos(Angle.degrees(90.833).radians)) / (cos(latitude.radians) * cos(sunEquatorialCoordinates.declination.radians)) - tan(latitude.radians) * tan(sunEquatorialCoordinates.declination.radians) @@ -607,7 +542,6 @@ public struct Sun: Identifiable, Sendable { } let hoursMinutesSeconds: (Int, Int, Int) = secondsToHoursMinutesSeconds(Int(sunsetSeconds)) - let sunsetDate = calendar.date(bySettingHour: hoursMinutesSeconds.0, minute: hoursMinutesSeconds.1, second: hoursMinutesSeconds.2, of: startOfDay) return sunsetDate @@ -630,22 +564,18 @@ public struct Sun: Identifiable, Sendable { let startOfTheDay = calendar.startOfDay(for: date) if (Int(secondsForSunToReachElevation) > SECONDS_IN_ONE_DAY){ - secondsForSunToReachElevation = Double(SECONDS_IN_ONE_DAY) - } - else if (secondsForSunToReachElevation < SECONDS_IN_ONE_HOUR){ - + } else if (secondsForSunToReachElevation < SECONDS_IN_ONE_HOUR){ secondsForSunToReachElevation = 0 } - let hoursMinutesSeconds: (Int, Int, Int) = secondsToHoursMinutesSeconds(Int(secondsForSunToReachElevation)) + let hoursMinutesSeconds: (Int, Int, Int) = secondsToHoursMinutesSeconds(Int(secondsForSunToReachElevation)) let newDate = calendar.date(bySettingHour: hoursMinutesSeconds.0 , minute: hoursMinutesSeconds.1, second: hoursMinutesSeconds.2, of: startOfTheDay) return newDate } - /// Golden Hour in the evening begins when the sun reaches elevation equals to 6 degrees - /// - Returns: Time at which the GoldenHour starts + /// Evening Golden Hour begins when the Sun reaches 6 degrees of elevation. private func getEveningGoldenHourStart() -> Date? { guard let eveningGoldenHourStart = getDateFrom(sunEvent: .eveningGoldenHourStart) else { return nil @@ -654,8 +584,7 @@ public struct Sun: Identifiable, Sendable { return eveningGoldenHourStart } - /// Golden Hour in the evening ends when the sun reaches elevation equals to -4 degrees - /// - Returns: Time at which the GoldenHour ends + /// Evening Golden Hour ends when the sun reaches -4 degrees of elevation. private func getEveningGoldenHourEnd() -> Date? { guard let goldenHourFinish = getDateFrom(sunEvent: .eveningGoldenHourEnd) else { return nil @@ -664,8 +593,7 @@ public struct Sun: Identifiable, Sendable { return goldenHourFinish } - /// Civil Dawn is when the Sun reaches -6 degrees of elevation. Also known as civil sunrise - /// - Returns: Civil Dawn time + /// Civil Dawn is when the Sun reaches -6 degrees of elevation. private func getCivilDawn() -> Date? { guard let civilDawn = getDateFrom(sunEvent: .civil,morning: true) else { return nil @@ -674,8 +602,7 @@ public struct Sun: Identifiable, Sendable { return civilDawn } - /// civil dusk is when the Sun reaches -6 degrees of elevation. Also known as civil sunrise. - /// - Returns: civil dusk time + /// Civil Dusk is when the Sun reaches -6 degrees of elevation. private func getCivilDusk() -> Date? { guard let civilDusk = getDateFrom(sunEvent: .civil, morning: false) else { return nil @@ -685,7 +612,6 @@ public struct Sun: Identifiable, Sendable { } /// Nautical Dusk is when the Sun reaches -12 degrees of elevation. - /// - Returns: Nautical Dusk private func getNauticalDusk() -> Date? { guard let nauticalDusk = getDateFrom(sunEvent: .nautical, morning: false) else { return nil @@ -695,7 +621,6 @@ public struct Sun: Identifiable, Sendable { } /// Nautical Dusk is when the Sun reaches -12 degrees of elevation. - /// - Returns: Nautical Dawn private func getNauticalDawn() -> Date? { guard let nauticalDawn = getDateFrom(sunEvent: .nautical, morning: true) else { return nil @@ -705,7 +630,6 @@ public struct Sun: Identifiable, Sendable { } /// Astronomical Dusk is when the Sun reaches -18 degrees of elevation. - /// - Returns: Astronomical Dusk private func getAstronomicalDusk() -> Date? { guard let astronomicalDusk = getDateFrom(sunEvent: .astronomical, morning: false) else { return nil @@ -715,7 +639,6 @@ public struct Sun: Identifiable, Sendable { } /// Astronomical Dawn is when the Sun reaches -18 degrees of elevation. - /// - Returns: Astronomical Dawn private func getAstronomicalDawn() -> Date? { guard let astronomicalDawn = getDateFrom(sunEvent: .astronomical, morning: true) else { return nil @@ -724,8 +647,7 @@ public struct Sun: Identifiable, Sendable { return astronomicalDawn } - /// Morning Golden Hour start when Sun reaches -4 degress of elevation - /// - Returns: Morning golden hour start + /// Morning Golden Hour starts when the Sun reaches -4 degrees of elevation. private func getMorningGoldenHourStart() -> Date? { guard let morningGoldenHourStart = getDateFrom(sunEvent: .morningGoldenHourStart , morning: true) else { return nil @@ -734,8 +656,7 @@ public struct Sun: Identifiable, Sendable { return morningGoldenHourStart } - /// Morning Golden Hour ends when Sun reaches 6 degress of elevation - /// - Returns: Morning golden hour end + /// Morning Golden Hour ends when Sun reaches 6 degrees of elevation. private func getMorningGoldenHourEnd() -> Date? { guard let morningGoldenHourEnd = getDateFrom(sunEvent: .morningGoldenHourEnd , morning: true) else { return nil From 2747da4edf066dfec9c1a8808992ae57b4196fa5 Mon Sep 17 00:00:00 2001 From: Ignas Sireikis Date: Fri, 19 Sep 2025 11:53:02 -0500 Subject: [PATCH 22/91] Applied consistent horizontal spacing style. --- Sources/SunKit/Sun/Sun.swift | 123 +++++++++++++++++------------------ 1 file changed, 61 insertions(+), 62 deletions(-) diff --git a/Sources/SunKit/Sun/Sun.swift b/Sources/SunKit/Sun/Sun.swift index 175bf64..01f419d 100644 --- a/Sources/SunKit/Sun/Sun.swift +++ b/Sources/SunKit/Sun/Sun.swift @@ -55,22 +55,22 @@ public struct Sun: Identifiable, Sendable { public private(set) var astronomicalDusk: Date = Date() public private(set) var astronomicalDawn: Date = Date() - ///Date at which morning Blue Hour starts. Sun at -6 degrees elevation = civil dusk + /// Date at which morning Blue Hour starts. Sun at -6 degrees elevation = civil dusk public var morningBlueHourStart: Date { civilDawn } - ///Date at which morning Blue Hour ends. Sun at -4 degrees elevation = morning golden hour start + /// Date at which morning Blue Hour ends. Sun at -4 degrees elevation = morning golden hour start public var morningBlueHourEnd: Date { morningGoldenHourStart } - ///Date at which evening Blue Hour starts. Sun at -4 degrees elevation = evening golden hour end + /// Date at which evening Blue Hour starts. Sun at -4 degrees elevation = evening golden hour end public var eveningBlueHourStart: Date { eveningGoldenHourEnd } - ///Date at which morning Blue Hour ends. Sun at -6 degrees elevation = Civil Dawn + /// Date at which morning Blue Hour ends. Sun at -6 degrees elevation = Civil Dawn public var eveningBlueHourEnd: Date { civilDusk } @@ -95,7 +95,7 @@ public struct Sun: Identifiable, Sendable { } public private(set) var sunEquatorialCoordinates: EquatorialCoordinates = .init(declination: .zero) - public private(set) var sunEclipticCoordinates: EclipticCoordinates = .init(eclipticLatitude: .zero, eclipticLongitude: .zero) + public private(set) var sunEclipticCoordinates: EclipticCoordinates = .init(eclipticLatitude: .zero, eclipticLongitude: .zero) /*-------------------------------------------------------------------- Sun Events during the year @@ -129,12 +129,12 @@ public struct Sun: Identifiable, Sendable { /// Returns night time in seconds. public var totalNightTime: Int { - let startOfTheDay = calendar.startOfDay(for: date) - let endOfTheDay = calendar.date(bySettingHour: 23, minute: 59, second: 59, of: startOfTheDay)! - var diffComponents = calendar.dateComponents([.second], from: startOfTheDay, to: sunrise) + let startOfTheDay = calendar.startOfDay(for: date) + let endOfTheDay = calendar.date(bySettingHour: 23, minute: 59, second: 59, of: startOfTheDay)! + var diffComponents = calendar.dateComponents([.second], from: startOfTheDay, to: sunrise) var nightHours: Int = diffComponents.second ?? 0 - diffComponents = calendar.dateComponents([.second], from: sunset, to: endOfTheDay) - nightHours = nightHours + (diffComponents.second ?? 0) + diffComponents = calendar.dateComponents([.second], from: sunset, to: endOfTheDay) + nightHours = nightHours + (diffComponents.second ?? 0) return nightHours } @@ -238,7 +238,7 @@ public struct Sun: Identifiable, Sendable { let isSameDay: Bool = (newDay == oldDay) date = newDate - refresh(needToComputeSunEvents: !isSameDay) //If is the same day no need to compute again Daily Sun Events + refresh(needToComputeSunEvents: !isSameDay) // If is the same day no need to compute again Daily Sun Events } /*-------------------------------------------------------------------- @@ -299,7 +299,7 @@ public struct Sun: Identifiable, Sendable { private var calendar: Calendar { var calendar: Calendar = .init(identifier: .gregorian) - calendar.timeZone = self.timeZone + calendar.timeZone = self.timeZone return calendar } @@ -310,13 +310,13 @@ public struct Sun: Identifiable, Sendable { private var sunHorizonCoordinates: HorizonCoordinates = .init(altitude: .zero, azimuth: .zero) - //Sun constants + // Sun constants private let sunEclipticLongitudeAtTheEpoch: Angle = .init(degrees: 280.466069) - private let sunEclipticLongitudePerigee: Angle = .init(degrees: 282.938346) + private let sunEclipticLongitudePerigee: Angle = .init(degrees: 282.938346) /// Number of the days passed since the start of the year for the self.date private var daysPassedFromStartOfTheYear: Int { - let year = calendar.component(.year, from: date) + let year = calendar.component(.year, from: date) let dateFormatter: DateFormatter = DateFormatter() @@ -346,14 +346,14 @@ public struct Sun: Identifiable, Sendable { } private var localStandardTimeMeridian: Double { - return (Double(self.timeZoneInSeconds) / SECONDS_IN_ONE_HOUR) * 15 //TimeZone in hour + return (Double(self.timeZoneInSeconds) / SECONDS_IN_ONE_HOUR) * 15 // TimeZone in hour } private var timeCorrectionFactorInSeconds: Double { - let timeCorrectionFactor = 4 * (location.coordinate.longitude - localStandardTimeMeridian) + equationOfTime - let minutes: Double = Double(Int(timeCorrectionFactor) * 60) - let seconds: Double = timeCorrectionFactor.truncatingRemainder(dividingBy: 1) * 100 - let timeCorrectionFactorInSeconds = minutes + seconds + let timeCorrectionFactor = 4 * (location.coordinate.longitude - localStandardTimeMeridian) + equationOfTime + let minutes: Double = Double(Int(timeCorrectionFactor) * 60) + let seconds: Double = timeCorrectionFactor.truncatingRemainder(dividingBy: 1) * 100 + let timeCorrectionFactorInSeconds = minutes + seconds return timeCorrectionFactorInSeconds } @@ -372,43 +372,43 @@ public struct Sun: Identifiable, Sendable { private mutating func refresh(needToComputeSunEvents: Bool = true) { updateSunCoordinates() - if (needToComputeSunEvents){ - self.sunrise = getSunrise() ?? Date() - self.sunriseAzimuth = getSunHorizonCoordinatesFrom(date: sunrise).azimuth.degrees - self.sunset = getSunset() ?? Date() - self.sunsetAzimuth = getSunHorizonCoordinatesFrom(date: sunset).azimuth.degrees - self.solarNoon = getSolarNoon() ?? Date() + if (needToComputeSunEvents) { + self.sunrise = getSunrise() ?? Date() + self.sunriseAzimuth = getSunHorizonCoordinatesFrom(date: sunrise).azimuth.degrees + self.sunset = getSunset() ?? Date() + self.sunsetAzimuth = getSunHorizonCoordinatesFrom(date: sunset).azimuth.degrees + self.solarNoon = getSolarNoon() ?? Date() self.solarMidnight = getSolarMidnight() ?? Date() self.solarNoonAzimuth = getSunHorizonCoordinatesFrom(date: solarNoon).azimuth.degrees - self.eveningGoldenHourStart = getEveningGoldenHourStart() ?? Date() - self.eveningGoldenHourEnd = getEveningGoldenHourEnd() ?? Date() - self.civilDusk = getCivilDusk() ?? Date() - self.civilDawn = getCivilDawn() ?? Date() - self.nauticalDusk = getNauticalDusk() ?? Date() - self.nauticalDawn = getNauticalDawn() ?? Date() + self.eveningGoldenHourStart = getEveningGoldenHourStart() ?? Date() + self.eveningGoldenHourEnd = getEveningGoldenHourEnd() ?? Date() + self.civilDusk = getCivilDusk() ?? Date() + self.civilDawn = getCivilDawn() ?? Date() + self.nauticalDusk = getNauticalDusk() ?? Date() + self.nauticalDawn = getNauticalDawn() ?? Date() self.astronomicalDusk = getAstronomicalDusk() ?? Date() - self.astronomicalDawn = getAstronomicalDawn() ?? Date() + self.astronomicalDawn = getAstronomicalDawn() ?? Date() self.morningGoldenHourStart = getMorningGoldenHourStart() ?? Date() - self.morningGoldenHourEnd = getMorningGoldenHourEnd() ?? Date() + self.morningGoldenHourEnd = getMorningGoldenHourEnd() ?? Date() } - self.marchEquinox = getMarchEquinox() ?? Date() - self.juneSolstice = getJuneSolstice() ?? Date() + self.marchEquinox = getMarchEquinox() ?? Date() + self.juneSolstice = getJuneSolstice() ?? Date() self.septemberEquinox = getSeptemberEquinox() ?? Date() self.decemberSolstice = getDecemberSolstice() ?? Date() } private func getSunMeanAnomaly(from elapsedDaysSinceStandardEpoch: Double) -> Angle { - var sunMeanAnomaly: Angle = .init(degrees:(((360.0 * elapsedDaysSinceStandardEpoch) / 365.242191) + sunEclipticLongitudeAtTheEpoch.degrees - sunEclipticLongitudePerigee.degrees)) + var sunMeanAnomaly: Angle = .init(degrees:(((360.0 * elapsedDaysSinceStandardEpoch) / 365.242191) + sunEclipticLongitudeAtTheEpoch.degrees - sunEclipticLongitudePerigee.degrees)) sunMeanAnomaly = .init(degrees: extendedMod(sunMeanAnomaly.degrees, 360)) return sunMeanAnomaly } private func getSunEclipticLongitude(from sunMeanAnomaly: Angle) -> Angle { - let equationOfCenter = 360 / Double.pi * sin(sunMeanAnomaly.radians) * 0.016708 - let trueAnomaly = sunMeanAnomaly.degrees + equationOfCenter - var eclipticLatitude: Angle = .init(degrees: trueAnomaly + sunEclipticLongitudePerigee.degrees) + let equationOfCenter = 360 / Double.pi * sin(sunMeanAnomaly.radians) * 0.016708 + let trueAnomaly = sunMeanAnomaly.degrees + equationOfCenter + var eclipticLatitude: Angle = .init(degrees: trueAnomaly + sunEclipticLongitudePerigee.degrees) if eclipticLatitude.degrees > 360 { eclipticLatitude.degrees -= 360 @@ -428,7 +428,7 @@ public struct Sun: Identifiable, Sendable { // Compute the Julian day number for the desired date using the Greenwich date and TT let jdTT = jdFromDate(date: self.date) // Compute the total number of elapsed days, including fractional days, since the standard epoch (i.e., JD − JDe) - let elapsedDaysSinceStandardEpoch: Double = jdTT - jdEpoch //De + let elapsedDaysSinceStandardEpoch: Double = jdTT - jdEpoch // Use the algorithm from section 6.2.3 to calculate the Sun’s ecliptic longitude and mean anomaly for the given UT date and time. let sunMeanAnomaly = getSunMeanAnomaly(from: elapsedDaysSinceStandardEpoch) // Use Equation 6.2.4 to aproximate the Equation of the center @@ -461,7 +461,7 @@ public struct Sun: Identifiable, Sendable { // Compute the Julian day number for the desired date using the Greenwich date and TT let jdTT = jdFromDate(date: date) // Compute the total number of elapsed days, including fractional days, since the standard epoch (i.e., JD − JDe) - let elapsedDaysSinceStandardEpoch: Double = jdTT - jdEpoch //De + let elapsedDaysSinceStandardEpoch: Double = jdTT - jdEpoch // Use the algorithm from section 6.2.3 to calculate the Sun’s ecliptic longitude and mean anomaly for the given UT date and time. let sunMeanAnomaly = getSunMeanAnomaly(from: elapsedDaysSinceStandardEpoch) // Use Equation 6.2.4 to aproximate the Equation of the center @@ -489,9 +489,9 @@ public struct Sun: Identifiable, Sendable { /// Solar Noon is the time when the Sun is highest in the sky. private func getSolarNoon() -> Date? { let secondsForUTCSolarNoon = (720 - 4 * location.coordinate.longitude - equationOfTime) * 60 - let secondsForSolarNoon = secondsForUTCSolarNoon + Double(timeZoneInSeconds) - let startOfTheDay = calendar.startOfDay(for: date) - let solarNoon = calendar.date(byAdding: .second, value: Int(secondsForSolarNoon) , to: startOfTheDay) + let secondsForSolarNoon = secondsForUTCSolarNoon + Double(timeZoneInSeconds) + let startOfTheDay = calendar.startOfDay(for: date) + let solarNoon = calendar.date(byAdding: .second, value: Int(secondsForSolarNoon), to: startOfTheDay) return solarNoon } @@ -499,10 +499,9 @@ public struct Sun: Identifiable, Sendable { /// Computes the solar midnight for self.date. private func getSolarMidnight() -> Date? { let secondsForUTCSolarMidnight = (0 - 4 * location.coordinate.longitude - equationOfTime) * 60 - let secondsForSolarMidnight = secondsForUTCSolarMidnight + Double(timeZoneInSeconds) - let startOfTheDay = calendar.startOfDay(for: date) - - let solarMidnight = calendar.date(byAdding: .second, value: Int(secondsForSolarMidnight) , to: startOfTheDay) + let secondsForSolarMidnight = secondsForUTCSolarMidnight + Double(timeZoneInSeconds) + let startOfTheDay = calendar.startOfDay(for: date) + let solarMidnight = calendar.date(byAdding: .second, value: Int(secondsForSolarMidnight), to: startOfTheDay) return solarMidnight } @@ -511,11 +510,11 @@ public struct Sun: Identifiable, Sendable { private func getSunrise() -> Date? { var haArg = (cos(Angle.degrees(90.833).radians)) / (cos(latitude.radians) * cos(sunEquatorialCoordinates.declination.radians)) - tan(latitude.radians) * tan(sunEquatorialCoordinates.declination.radians) - haArg = clamp(lower: -1, upper: 1, number: haArg) - let ha: Angle = .radians(acos(haArg)) + haArg = clamp(lower: -1, upper: 1, number: haArg) + let ha: Angle = .radians(acos(haArg)) let sunriseUTCMinutes = 720 - 4 * (location.coordinate.longitude + ha.degrees) - equationOfTime - var sunriseSeconds = (Int(sunriseUTCMinutes) * 60) + timeZoneInSeconds - let startOfDay = calendar.startOfDay(for: date) + var sunriseSeconds = (Int(sunriseUTCMinutes) * 60) + timeZoneInSeconds + let startOfDay = calendar.startOfDay(for: date) if sunriseSeconds < Int(SECONDS_IN_ONE_HOUR) { sunriseSeconds = 0 @@ -531,11 +530,11 @@ public struct Sun: Identifiable, Sendable { private func getSunset() -> Date? { var haArg = (cos(Angle.degrees(90.833).radians)) / (cos(latitude.radians) * cos(sunEquatorialCoordinates.declination.radians)) - tan(latitude.radians) * tan(sunEquatorialCoordinates.declination.radians) - haArg = clamp(lower: -1, upper: 1, number: haArg) - let ha: Angle = .radians(-acos(haArg)) + haArg = clamp(lower: -1, upper: 1, number: haArg) + let ha: Angle = .radians(-acos(haArg)) let sunsetUTCMinutes = 720 - 4 * (location.coordinate.longitude + ha.degrees) - equationOfTime - var sunsetSeconds = (Int(sunsetUTCMinutes) * 60) + timeZoneInSeconds - let startOfDay = calendar.startOfDay(for: date) + var sunsetSeconds = (Int(sunsetUTCMinutes) * 60) + timeZoneInSeconds + let startOfDay = calendar.startOfDay(for: date) if sunsetSeconds > SECONDS_IN_ONE_DAY { sunsetSeconds = SECONDS_IN_ONE_DAY @@ -560,12 +559,12 @@ public struct Sun: Identifiable, Sendable { var cosHra = (sin(elevationSun.radians) - sin(sunEquatorialCoordinates.declination.radians) * sin(latitude.radians)) / (cos(sunEquatorialCoordinates.declination.radians) * cos(latitude.radians)) cosHra = clamp(lower: -1, upper: 1, number: cosHra) let hraAngle: Angle = .radians(acos(cosHra)) - var secondsForSunToReachElevation = (morning ? -1 : 1) * (hraAngle.degrees / 15) * SECONDS_IN_ONE_HOUR + TWELVE_HOUR_IN_SECONDS - timeCorrectionFactorInSeconds + var secondsForSunToReachElevation = (morning ? -1 : 1) * (hraAngle.degrees / 15) * SECONDS_IN_ONE_HOUR + TWELVE_HOUR_IN_SECONDS - timeCorrectionFactorInSeconds let startOfTheDay = calendar.startOfDay(for: date) - if (Int(secondsForSunToReachElevation) > SECONDS_IN_ONE_DAY){ + if (Int(secondsForSunToReachElevation) > SECONDS_IN_ONE_DAY) { secondsForSunToReachElevation = Double(SECONDS_IN_ONE_DAY) - } else if (secondsForSunToReachElevation < SECONDS_IN_ONE_HOUR){ + } else if (secondsForSunToReachElevation < SECONDS_IN_ONE_HOUR) { secondsForSunToReachElevation = 0 } @@ -649,7 +648,7 @@ public struct Sun: Identifiable, Sendable { /// Morning Golden Hour starts when the Sun reaches -4 degrees of elevation. private func getMorningGoldenHourStart() -> Date? { - guard let morningGoldenHourStart = getDateFrom(sunEvent: .morningGoldenHourStart , morning: true) else { + guard let morningGoldenHourStart = getDateFrom(sunEvent: .morningGoldenHourStart, morning: true) else { return nil } @@ -658,7 +657,7 @@ public struct Sun: Identifiable, Sendable { /// Morning Golden Hour ends when Sun reaches 6 degrees of elevation. private func getMorningGoldenHourEnd() -> Date? { - guard let morningGoldenHourEnd = getDateFrom(sunEvent: .morningGoldenHourEnd , morning: true) else { + guard let morningGoldenHourEnd = getDateFrom(sunEvent: .morningGoldenHourEnd, morning: true) else { return nil } From a748514cfb623acaef6f4bb3fe21338beb38bf4b Mon Sep 17 00:00:00 2001 From: Ignas Sireikis Date: Fri, 19 Sep 2025 11:58:01 -0500 Subject: [PATCH 23/91] Applied consistent vertical spacing style. --- Sources/SunKit/Sun/Sun.swift | 19 ------------------- 1 file changed, 19 deletions(-) diff --git a/Sources/SunKit/Sun/Sun.swift b/Sources/SunKit/Sun/Sun.swift index 01f419d..43b4bf6 100644 --- a/Sources/SunKit/Sun/Sun.swift +++ b/Sources/SunKit/Sun/Sun.swift @@ -39,19 +39,14 @@ public struct Sun: Identifiable, Sendable { public private(set) var sunset: Date = Date() public private(set) var solarNoon: Date = Date() public private(set) var solarMidnight: Date = Date() - public private(set) var eveningGoldenHourStart: Date = Date() public private(set) var eveningGoldenHourEnd: Date = Date() - public private(set) var morningGoldenHourStart: Date = Date() public private(set) var morningGoldenHourEnd: Date = Date() - public private(set) var civilDusk: Date = Date() public private(set) var civilDawn: Date = Date() - public private(set) var nauticalDusk: Date = Date() public private(set) var nauticalDawn: Date = Date() - public private(set) var astronomicalDusk: Date = Date() public private(set) var astronomicalDawn: Date = Date() @@ -75,7 +70,6 @@ public struct Sun: Identifiable, Sendable { civilDusk } - /*-------------------------------------------------------------------- Sun Azimuths for Self.date and for Sunrise,Sunset and Solar Noon *-------------------------------------------------------------------*/ @@ -183,7 +177,6 @@ public struct Sun: Identifiable, Sendable { isMorningBlueHour || isEveningBlueHour } - /// Returns true if we are near the pole and we are in a situation in which Sun Events during the day could have no meaning public var isCircumPolar: Bool { isAlwaysDay || isAlwaysNight @@ -234,7 +227,6 @@ public struct Sun: Identifiable, Sendable { public mutating func setDate(_ newDate: Date) { let newDay = calendar.dateComponents([.day,.month,.year], from: newDate) let oldDay = calendar.dateComponents([.day,.month,.year], from: date) - let isSameDay: Bool = (newDay == oldDay) date = newDate @@ -245,7 +237,6 @@ public struct Sun: Identifiable, Sendable { Changing Location *-------------------------------------------------------------------*/ - /// Changing location and timezone /// - Parameters: /// - newLocation: New location @@ -317,16 +308,12 @@ public struct Sun: Identifiable, Sendable { /// Number of the days passed since the start of the year for the self.date private var daysPassedFromStartOfTheYear: Int { let year = calendar.component(.year, from: date) - let dateFormatter: DateFormatter = DateFormatter() - dateFormatter.dateFormat = "yyyy/mm/dd" dateFormatter.calendar = calendar let dataFormatted = dateFormatter.date(from: "\(year)/01/01") - let startOfYear = calendar.startOfDay(for: dataFormatted!) let startOfDay = calendar.startOfDay(for: date) - var daysPassedFromStartOfTheYear = calendar.dateComponents([.day], from: startOfYear, to: startOfDay).day! daysPassedFromStartOfTheYear = daysPassedFromStartOfTheYear + 1 @@ -454,10 +441,8 @@ public struct Sun: Identifiable, Sendable { let gstHMS = uT2GST(date) let lstHMS = gST2LST(gstHMS,longitude: longitude) let lstDecimal = lstHMS.hMS2Decimal() - // Julian number for standard epoch 2000 let jdEpoch = 2451545.00 - // Compute the Julian day number for the desired date using the Greenwich date and TT let jdTT = jdFromDate(date: date) // Compute the total number of elapsed days, including fractional days, since the standard epoch (i.e., JD − JDe) @@ -668,7 +653,6 @@ public struct Sun: Identifiable, Sendable { let year = Double(calendar.component(.year, from: self.date)) let t: Double = year / 1000 let julianDayMarchEquinox: Double = 1721139.2855 + 365.2421376 * year + 0.0679190 * pow(t, 2) - 0.0027879 * pow(t, 3) - let marchEquinoxUTC = dateFromJd(jd: julianDayMarchEquinox) return marchEquinoxUTC @@ -678,7 +662,6 @@ public struct Sun: Identifiable, Sendable { let year = Double(calendar.component(.year, from: self.date)) let t: Double = year / 1000 let julianDayJuneSolstice: Double = 1721233.2486 + 365.2417284 * year - 0.0530180 * pow(t, 2) + 0.0093320 * pow(t, 3) - let juneSolsticeUTC = dateFromJd(jd: julianDayJuneSolstice) return juneSolsticeUTC @@ -688,7 +671,6 @@ public struct Sun: Identifiable, Sendable { let year = Double(calendar.component(.year, from: self.date)) let t: Double = year / 1000 let julianDaySeptemberEquinox: Double = 1721325.6978 + 365.2425055 * year - 0.126689 * pow(t, 2) + 0.0019401 * pow(t, 3) - let septemberEquinoxUTC = dateFromJd(jd: julianDaySeptemberEquinox) return septemberEquinoxUTC @@ -698,7 +680,6 @@ public struct Sun: Identifiable, Sendable { let year = Double(calendar.component(.year, from: self.date)) let t: Double = year / 1000 let julianDayDecemberSolstice: Double = 1721414.3920 + 365.2428898 * year - 0.0109650 * pow(t, 2) - 0.0084885 * pow(t, 3) - let decemberSolsticeUTC = dateFromJd(jd: julianDayDecemberSolstice) return decemberSolsticeUTC From 769093c1d3ab07ed2982c4931b7d2fd85b74a77d Mon Sep 17 00:00:00 2001 From: Ignas Sireikis Date: Fri, 19 Sep 2025 12:03:07 -0500 Subject: [PATCH 24/91] Reorganized the order of sun event variables to be chronological. --- Sources/SunKit/Sun/Sun.swift | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/Sources/SunKit/Sun/Sun.swift b/Sources/SunKit/Sun/Sun.swift index 43b4bf6..1348de3 100644 --- a/Sources/SunKit/Sun/Sun.swift +++ b/Sources/SunKit/Sun/Sun.swift @@ -35,20 +35,25 @@ public struct Sun: Identifiable, Sendable { Sun Events during the day *-------------------------------------------------------------------*/ + /* + Sun Events during the day organized chronologically + */ + public private(set) var astronomicalDawn: Date = Date() + public private(set) var nauticalDawn: Date = Date() + public private(set) var civilDawn: Date = Date() + public private(set) var morningGoldenHourStart: Date = Date() public private(set) var sunrise: Date = Date() - public private(set) var sunset: Date = Date() + public private(set) var morningGoldenHourEnd: Date = Date() + public private(set) var solarNoon: Date = Date() - public private(set) var solarMidnight: Date = Date() + public private(set) var eveningGoldenHourStart: Date = Date() + public private(set) var sunset: Date = Date() public private(set) var eveningGoldenHourEnd: Date = Date() - public private(set) var morningGoldenHourStart: Date = Date() - public private(set) var morningGoldenHourEnd: Date = Date() public private(set) var civilDusk: Date = Date() - public private(set) var civilDawn: Date = Date() public private(set) var nauticalDusk: Date = Date() - public private(set) var nauticalDawn: Date = Date() public private(set) var astronomicalDusk: Date = Date() - public private(set) var astronomicalDawn: Date = Date() + public private(set) var solarMidnight: Date = Date() /// Date at which morning Blue Hour starts. Sun at -6 degrees elevation = civil dusk public var morningBlueHourStart: Date { From 58a44578a215b4d6280ebfe3dd59b582496fdd68 Mon Sep 17 00:00:00 2001 From: Ignas Sireikis Date: Fri, 19 Sep 2025 12:11:18 -0500 Subject: [PATCH 25/91] Cleaned up comments for computed sun event variables. --- Sources/SunKit/Sun/Sun.swift | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/Sources/SunKit/Sun/Sun.swift b/Sources/SunKit/Sun/Sun.swift index 1348de3..1a98041 100644 --- a/Sources/SunKit/Sun/Sun.swift +++ b/Sources/SunKit/Sun/Sun.swift @@ -55,22 +55,26 @@ public struct Sun: Identifiable, Sendable { public private(set) var astronomicalDusk: Date = Date() public private(set) var solarMidnight: Date = Date() - /// Date at which morning Blue Hour starts. Sun at -6 degrees elevation = civil dusk + /// Date at which the morning Blue Hour starts. + /// The same elevation as Civil Dawn--when the Sun is at -6 degrees. public var morningBlueHourStart: Date { civilDawn } - /// Date at which morning Blue Hour ends. Sun at -4 degrees elevation = morning golden hour start + /// Date at which morning Blue Hour ends and the morning Golden Hour begins. + /// The same elevation as morning Golden Hour start--when the Sun is at -4 degrees. public var morningBlueHourEnd: Date { morningGoldenHourStart } - /// Date at which evening Blue Hour starts. Sun at -4 degrees elevation = evening golden hour end + /// Date at which evening Golden Hour ends and the evening Blue Hour begins. + /// The same elevation as morning Golden Hour end--when the Sun is at -4 degrees. public var eveningBlueHourStart: Date { eveningGoldenHourEnd } - /// Date at which morning Blue Hour ends. Sun at -6 degrees elevation = Civil Dawn + /// Date at which evening Blue Hour ends. + /// The same elevation as Civil Dusk--when the Sun is at -6 degrees. public var eveningBlueHourEnd: Date { civilDusk } From b30c9f3e78239417ab6e84c395d1d2c6a9eea882 Mon Sep 17 00:00:00 2001 From: Ignas Sireikis Date: Fri, 19 Sep 2025 12:16:22 -0500 Subject: [PATCH 26/91] Reorganized order of variables in refresh function. --- Sources/SunKit/Sun/Sun.swift | 25 ++++++++++++++----------- 1 file changed, 14 insertions(+), 11 deletions(-) diff --git a/Sources/SunKit/Sun/Sun.swift b/Sources/SunKit/Sun/Sun.swift index 1a98041..f93084e 100644 --- a/Sources/SunKit/Sun/Sun.swift +++ b/Sources/SunKit/Sun/Sun.swift @@ -84,8 +84,8 @@ public struct Sun: Identifiable, Sendable { *-------------------------------------------------------------------*/ public private(set) var sunriseAzimuth: Double = 0 - public private(set) var sunsetAzimuth: Double = 0 public private(set) var solarNoonAzimuth: Double = 0 + public private(set) var sunsetAzimuth: Double = 0 // Sun azimuth for (Location,Date) in Self public var azimuth: Angle { @@ -369,23 +369,26 @@ public struct Sun: Identifiable, Sendable { updateSunCoordinates() if (needToComputeSunEvents) { + self.astronomicalDawn = getAstronomicalDawn() ?? Date() + self.nauticalDawn = getNauticalDawn() ?? Date() + self.civilDawn = getCivilDawn() ?? Date() + self.morningGoldenHourStart = getMorningGoldenHourStart() ?? Date() self.sunrise = getSunrise() ?? Date() - self.sunriseAzimuth = getSunHorizonCoordinatesFrom(date: sunrise).azimuth.degrees - self.sunset = getSunset() ?? Date() - self.sunsetAzimuth = getSunHorizonCoordinatesFrom(date: sunset).azimuth.degrees + self.morningGoldenHourEnd = getMorningGoldenHourEnd() ?? Date() + self.solarNoon = getSolarNoon() ?? Date() - self.solarMidnight = getSolarMidnight() ?? Date() - self.solarNoonAzimuth = getSunHorizonCoordinatesFrom(date: solarNoon).azimuth.degrees + self.eveningGoldenHourStart = getEveningGoldenHourStart() ?? Date() + self.sunset = getSunset() ?? Date() self.eveningGoldenHourEnd = getEveningGoldenHourEnd() ?? Date() self.civilDusk = getCivilDusk() ?? Date() - self.civilDawn = getCivilDawn() ?? Date() self.nauticalDusk = getNauticalDusk() ?? Date() - self.nauticalDawn = getNauticalDawn() ?? Date() self.astronomicalDusk = getAstronomicalDusk() ?? Date() - self.astronomicalDawn = getAstronomicalDawn() ?? Date() - self.morningGoldenHourStart = getMorningGoldenHourStart() ?? Date() - self.morningGoldenHourEnd = getMorningGoldenHourEnd() ?? Date() + self.solarMidnight = getSolarMidnight() ?? Date() + + self.sunriseAzimuth = getSunHorizonCoordinatesFrom(date: sunrise).azimuth.degrees + self.solarNoonAzimuth = getSunHorizonCoordinatesFrom(date: solarNoon).azimuth.degrees + self.sunsetAzimuth = getSunHorizonCoordinatesFrom(date: sunset).azimuth.degrees } self.marchEquinox = getMarchEquinox() ?? Date() From 9373a71d54028432d59ddd1864bbf969ae292da4 Mon Sep 17 00:00:00 2001 From: Ignas Sireikis Date: Fri, 19 Sep 2025 12:26:44 -0500 Subject: [PATCH 27/91] Reorganized order of get functions used in refresh to match order of appearance. --- Sources/SunKit/Sun/Sun.swift | 302 +++++++++++++++++------------------ 1 file changed, 151 insertions(+), 151 deletions(-) diff --git a/Sources/SunKit/Sun/Sun.swift b/Sources/SunKit/Sun/Sun.swift index f93084e..5823b66 100644 --- a/Sources/SunKit/Sun/Sun.swift +++ b/Sources/SunKit/Sun/Sun.swift @@ -354,9 +354,23 @@ public struct Sun: Identifiable, Sendable { return timeCorrectionFactorInSeconds } - /*-------------------------------------------------------------------- - Private methods - *-------------------------------------------------------------------*/ + /// - Returns: Length in meters of the object's shadow by the provided object height and current sun altitude. + public func shadowLength( + for objectHeight: Double = 1, + with altitude: Angle? = nil + ) -> Double? { + let altitude = altitude ?? self.altitude + + return if altitude.degrees > 0 && altitude.degrees < 90 { + objectHeight / tan(altitude.radians) + } else if altitude.degrees <= 0 { + nil + } else { + 0 + } + } + + // MARK: - Refresh Sun State /// Updates in order all the sun coordinates: horizon, ecliptic and equatorial. /// Then get rise, set and noon times and their relative azimuths in degrees. @@ -397,25 +411,6 @@ public struct Sun: Identifiable, Sendable { self.decemberSolstice = getDecemberSolstice() ?? Date() } - private func getSunMeanAnomaly(from elapsedDaysSinceStandardEpoch: Double) -> Angle { - var sunMeanAnomaly: Angle = .init(degrees:(((360.0 * elapsedDaysSinceStandardEpoch) / 365.242191) + sunEclipticLongitudeAtTheEpoch.degrees - sunEclipticLongitudePerigee.degrees)) - sunMeanAnomaly = .init(degrees: extendedMod(sunMeanAnomaly.degrees, 360)) - - return sunMeanAnomaly - } - - private func getSunEclipticLongitude(from sunMeanAnomaly: Angle) -> Angle { - let equationOfCenter = 360 / Double.pi * sin(sunMeanAnomaly.radians) * 0.016708 - let trueAnomaly = sunMeanAnomaly.degrees + equationOfCenter - var eclipticLatitude: Angle = .init(degrees: trueAnomaly + sunEclipticLongitudePerigee.degrees) - - if eclipticLatitude.degrees > 360 { - eclipticLatitude.degrees -= 360 - } - - return eclipticLatitude - } - /// Updates Horizon coordinates, Ecliptic coordinates and Equatorial coordinates of the Sun private mutating func updateSunCoordinates() { // Convert LCT to UT, GST, and LST times and adjust the date if needed @@ -448,59 +443,61 @@ public struct Sun: Identifiable, Sendable { sunHorizonCoordinates = sunEquatorialCoordinates.equatorial2Horizon(lstDecimal: lstDecimal,latitude: latitude) ?? .init(altitude: .zero, azimuth: .zero) } - public func getSunHorizonCoordinatesFrom(date: Date) -> HorizonCoordinates { - // Convert LCT to UT, GST, and LST times and adjust the date if needed - let gstHMS = uT2GST(date) - let lstHMS = gST2LST(gstHMS,longitude: longitude) - let lstDecimal = lstHMS.hMS2Decimal() - // Julian number for standard epoch 2000 - let jdEpoch = 2451545.00 - // Compute the Julian day number for the desired date using the Greenwich date and TT - let jdTT = jdFromDate(date: date) - // Compute the total number of elapsed days, including fractional days, since the standard epoch (i.e., JD − JDe) - let elapsedDaysSinceStandardEpoch: Double = jdTT - jdEpoch - // Use the algorithm from section 6.2.3 to calculate the Sun’s ecliptic longitude and mean anomaly for the given UT date and time. - let sunMeanAnomaly = getSunMeanAnomaly(from: elapsedDaysSinceStandardEpoch) - // Use Equation 6.2.4 to aproximate the Equation of the center + private func getSunMeanAnomaly(from elapsedDaysSinceStandardEpoch: Double) -> Angle { + var sunMeanAnomaly: Angle = .init(degrees:(((360.0 * elapsedDaysSinceStandardEpoch) / 365.242191) + sunEclipticLongitudeAtTheEpoch.degrees - sunEclipticLongitudePerigee.degrees)) + sunMeanAnomaly = .init(degrees: extendedMod(sunMeanAnomaly.degrees, 360)) + + return sunMeanAnomaly + } + + private func getSunEclipticLongitude(from sunMeanAnomaly: Angle) -> Angle { let equationOfCenter = 360 / Double.pi * sin(sunMeanAnomaly.radians) * 0.016708 - // Add EoC to sun mean anomaly to get the sun true anomaly - var sunTrueAnomaly = sunMeanAnomaly.degrees + equationOfCenter - // Add or subtract multiples of 360° to adjust sun true anomaly to the range of 0° to 360° - sunTrueAnomaly = extendedMod(sunTrueAnomaly, 360) - // Getting ecliptic longitude. - var sunEclipticLongitude: Angle = .init(degrees: sunTrueAnomaly + sunEclipticLongitudePerigee.degrees) + let trueAnomaly = sunMeanAnomaly.degrees + equationOfCenter + var eclipticLatitude: Angle = .init(degrees: trueAnomaly + sunEclipticLongitudePerigee.degrees) - if sunEclipticLongitude.degrees > 360 { - sunEclipticLongitude.degrees -= 360 + if eclipticLatitude.degrees > 360 { + eclipticLatitude.degrees -= 360 } - let sunEclipticCoordinates: EclipticCoordinates = .init(eclipticLatitude: .zero, eclipticLongitude: sunEclipticLongitude) - // Ecliptic to Equatorial - var sunEquatorialCoordinates: EquatorialCoordinates = sunEclipticCoordinates.ecliptic2Equatorial() - // Equatorial to Horizon - let sunHorizonCoordinates: HorizonCoordinates = sunEquatorialCoordinates.equatorial2Horizon(lstDecimal: lstDecimal,latitude: latitude) ?? .init(altitude: .zero, azimuth: .zero) + return eclipticLatitude + } + + // MARK: - Get Day Events + + /// Astronomical Dawn is when the Sun reaches -18 degrees of elevation. + private func getAstronomicalDawn() -> Date? { + guard let astronomicalDawn = getDateFrom(sunEvent: .astronomical, morning: true) else { + return nil + } - return .init(altitude: sunHorizonCoordinates.altitude, azimuth: sunHorizonCoordinates.azimuth) + return astronomicalDawn } - /// Solar Noon is the time when the Sun is highest in the sky. - private func getSolarNoon() -> Date? { - let secondsForUTCSolarNoon = (720 - 4 * location.coordinate.longitude - equationOfTime) * 60 - let secondsForSolarNoon = secondsForUTCSolarNoon + Double(timeZoneInSeconds) - let startOfTheDay = calendar.startOfDay(for: date) - let solarNoon = calendar.date(byAdding: .second, value: Int(secondsForSolarNoon), to: startOfTheDay) + /// Nautical Dusk is when the Sun reaches -12 degrees of elevation. + private func getNauticalDawn() -> Date? { + guard let nauticalDawn = getDateFrom(sunEvent: .nautical, morning: true) else { + return nil + } - return solarNoon + return nauticalDawn } - /// Computes the solar midnight for self.date. - private func getSolarMidnight() -> Date? { - let secondsForUTCSolarMidnight = (0 - 4 * location.coordinate.longitude - equationOfTime) * 60 - let secondsForSolarMidnight = secondsForUTCSolarMidnight + Double(timeZoneInSeconds) - let startOfTheDay = calendar.startOfDay(for: date) - let solarMidnight = calendar.date(byAdding: .second, value: Int(secondsForSolarMidnight), to: startOfTheDay) + /// Civil Dawn is when the Sun reaches -6 degrees of elevation. + private func getCivilDawn() -> Date? { + guard let civilDawn = getDateFrom(sunEvent: .civil,morning: true) else { + return nil + } - return solarMidnight + return civilDawn + } + + /// Morning Golden Hour starts when the Sun reaches -4 degrees of elevation. + private func getMorningGoldenHourStart() -> Date? { + guard let morningGoldenHourStart = getDateFrom(sunEvent: .morningGoldenHourStart, morning: true) else { + return nil + } + + return morningGoldenHourStart } /// Sunrise is when the Sun reaches 0 degrees of elevation, aka the horizon, at the start of the day. @@ -523,6 +520,34 @@ public struct Sun: Identifiable, Sendable { return sunriseDate } + /// Morning Golden Hour ends when Sun reaches 6 degrees of elevation. + private func getMorningGoldenHourEnd() -> Date? { + guard let morningGoldenHourEnd = getDateFrom(sunEvent: .morningGoldenHourEnd, morning: true) else { + return nil + } + + return morningGoldenHourEnd + } + + /// Solar Noon is the time when the Sun is highest in the sky. + private func getSolarNoon() -> Date? { + let secondsForUTCSolarNoon = (720 - 4 * location.coordinate.longitude - equationOfTime) * 60 + let secondsForSolarNoon = secondsForUTCSolarNoon + Double(timeZoneInSeconds) + let startOfTheDay = calendar.startOfDay(for: date) + let solarNoon = calendar.date(byAdding: .second, value: Int(secondsForSolarNoon), to: startOfTheDay) + + return solarNoon + } + + /// Evening Golden Hour begins when the Sun reaches 6 degrees of elevation. + private func getEveningGoldenHourStart() -> Date? { + guard let eveningGoldenHourStart = getDateFrom(sunEvent: .eveningGoldenHourStart) else { + return nil + } + + return eveningGoldenHourStart + } + /// Sunset is when the Sun reaches 0 degrees of elevation, aka the horizon, at the end of the day. private func getSunset() -> Date? { var haArg = (cos(Angle.degrees(90.833).radians)) / (cos(latitude.radians) * cos(sunEquatorialCoordinates.declination.radians)) - tan(latitude.radians) * tan(sunEquatorialCoordinates.declination.radians) @@ -543,43 +568,6 @@ public struct Sun: Identifiable, Sendable { return sunsetDate } - /// Computes the time at which the sun will reach the elevation given in input for self.date - /// - Parameters: - /// - elevation: Elevation - /// - morning: Sun reaches a specific elevation twice, this boolean variable is needed to find out which one need to be considered. The one reached in the morning or not. - /// - Returns: Time at which the Sun reaches that elevation. Nil if it didn't find it. - private func getDateFrom( - sunEvent : SunElevationEvents, - morning: Bool = false - ) -> Date? { - let elevationSun: Angle = .degrees(sunEvent.rawValue) - var cosHra = (sin(elevationSun.radians) - sin(sunEquatorialCoordinates.declination.radians) * sin(latitude.radians)) / (cos(sunEquatorialCoordinates.declination.radians) * cos(latitude.radians)) - cosHra = clamp(lower: -1, upper: 1, number: cosHra) - let hraAngle: Angle = .radians(acos(cosHra)) - var secondsForSunToReachElevation = (morning ? -1 : 1) * (hraAngle.degrees / 15) * SECONDS_IN_ONE_HOUR + TWELVE_HOUR_IN_SECONDS - timeCorrectionFactorInSeconds - let startOfTheDay = calendar.startOfDay(for: date) - - if (Int(secondsForSunToReachElevation) > SECONDS_IN_ONE_DAY) { - secondsForSunToReachElevation = Double(SECONDS_IN_ONE_DAY) - } else if (secondsForSunToReachElevation < SECONDS_IN_ONE_HOUR) { - secondsForSunToReachElevation = 0 - } - - let hoursMinutesSeconds: (Int, Int, Int) = secondsToHoursMinutesSeconds(Int(secondsForSunToReachElevation)) - let newDate = calendar.date(bySettingHour: hoursMinutesSeconds.0 , minute: hoursMinutesSeconds.1, second: hoursMinutesSeconds.2, of: startOfTheDay) - - return newDate - } - - /// Evening Golden Hour begins when the Sun reaches 6 degrees of elevation. - private func getEveningGoldenHourStart() -> Date? { - guard let eveningGoldenHourStart = getDateFrom(sunEvent: .eveningGoldenHourStart) else { - return nil - } - - return eveningGoldenHourStart - } - /// Evening Golden Hour ends when the sun reaches -4 degrees of elevation. private func getEveningGoldenHourEnd() -> Date? { guard let goldenHourFinish = getDateFrom(sunEvent: .eveningGoldenHourEnd) else { @@ -589,15 +577,6 @@ public struct Sun: Identifiable, Sendable { return goldenHourFinish } - /// Civil Dawn is when the Sun reaches -6 degrees of elevation. - private func getCivilDawn() -> Date? { - guard let civilDawn = getDateFrom(sunEvent: .civil,morning: true) else { - return nil - } - - return civilDawn - } - /// Civil Dusk is when the Sun reaches -6 degrees of elevation. private func getCivilDusk() -> Date? { guard let civilDusk = getDateFrom(sunEvent: .civil, morning: false) else { @@ -616,15 +595,6 @@ public struct Sun: Identifiable, Sendable { return nauticalDusk } - /// Nautical Dusk is when the Sun reaches -12 degrees of elevation. - private func getNauticalDawn() -> Date? { - guard let nauticalDawn = getDateFrom(sunEvent: .nautical, morning: true) else { - return nil - } - - return nauticalDawn - } - /// Astronomical Dusk is when the Sun reaches -18 degrees of elevation. private func getAstronomicalDusk() -> Date? { guard let astronomicalDusk = getDateFrom(sunEvent: .astronomical, morning: false) else { @@ -634,31 +604,77 @@ public struct Sun: Identifiable, Sendable { return astronomicalDusk } - /// Astronomical Dawn is when the Sun reaches -18 degrees of elevation. - private func getAstronomicalDawn() -> Date? { - guard let astronomicalDawn = getDateFrom(sunEvent: .astronomical, morning: true) else { - return nil - } + /// Computes the solar midnight for self.date. + private func getSolarMidnight() -> Date? { + let secondsForUTCSolarMidnight = (0 - 4 * location.coordinate.longitude - equationOfTime) * 60 + let secondsForSolarMidnight = secondsForUTCSolarMidnight + Double(timeZoneInSeconds) + let startOfTheDay = calendar.startOfDay(for: date) + let solarMidnight = calendar.date(byAdding: .second, value: Int(secondsForSolarMidnight), to: startOfTheDay) - return astronomicalDawn + return solarMidnight } - /// Morning Golden Hour starts when the Sun reaches -4 degrees of elevation. - private func getMorningGoldenHourStart() -> Date? { - guard let morningGoldenHourStart = getDateFrom(sunEvent: .morningGoldenHourStart, morning: true) else { - return nil + /// Computes the time at which the sun will reach the elevation given in input for self.date + /// - Parameters: + /// - elevation: Elevation + /// - morning: Sun reaches a specific elevation twice, this boolean variable is needed to find out which one need to be considered. The one reached in the morning or not. + /// - Returns: Time at which the Sun reaches that elevation. Nil if it didn't find it. + private func getDateFrom( + sunEvent : SunElevationEvents, + morning: Bool = false + ) -> Date? { + let elevationSun: Angle = .degrees(sunEvent.rawValue) + var cosHra = (sin(elevationSun.radians) - sin(sunEquatorialCoordinates.declination.radians) * sin(latitude.radians)) / (cos(sunEquatorialCoordinates.declination.radians) * cos(latitude.radians)) + cosHra = clamp(lower: -1, upper: 1, number: cosHra) + let hraAngle: Angle = .radians(acos(cosHra)) + var secondsForSunToReachElevation = (morning ? -1 : 1) * (hraAngle.degrees / 15) * SECONDS_IN_ONE_HOUR + TWELVE_HOUR_IN_SECONDS - timeCorrectionFactorInSeconds + let startOfTheDay = calendar.startOfDay(for: date) + + if (Int(secondsForSunToReachElevation) > SECONDS_IN_ONE_DAY) { + secondsForSunToReachElevation = Double(SECONDS_IN_ONE_DAY) + } else if (secondsForSunToReachElevation < SECONDS_IN_ONE_HOUR) { + secondsForSunToReachElevation = 0 } - return morningGoldenHourStart + let hoursMinutesSeconds: (Int, Int, Int) = secondsToHoursMinutesSeconds(Int(secondsForSunToReachElevation)) + let newDate = calendar.date(bySettingHour: hoursMinutesSeconds.0 , minute: hoursMinutesSeconds.1, second: hoursMinutesSeconds.2, of: startOfTheDay) + + return newDate } - /// Morning Golden Hour ends when Sun reaches 6 degrees of elevation. - private func getMorningGoldenHourEnd() -> Date? { - guard let morningGoldenHourEnd = getDateFrom(sunEvent: .morningGoldenHourEnd, morning: true) else { - return nil + public func getSunHorizonCoordinatesFrom(date: Date) -> HorizonCoordinates { + // Convert LCT to UT, GST, and LST times and adjust the date if needed + let gstHMS = uT2GST(date) + let lstHMS = gST2LST(gstHMS,longitude: longitude) + let lstDecimal = lstHMS.hMS2Decimal() + // Julian number for standard epoch 2000 + let jdEpoch = 2451545.00 + // Compute the Julian day number for the desired date using the Greenwich date and TT + let jdTT = jdFromDate(date: date) + // Compute the total number of elapsed days, including fractional days, since the standard epoch (i.e., JD − JDe) + let elapsedDaysSinceStandardEpoch: Double = jdTT - jdEpoch + // Use the algorithm from section 6.2.3 to calculate the Sun’s ecliptic longitude and mean anomaly for the given UT date and time. + let sunMeanAnomaly = getSunMeanAnomaly(from: elapsedDaysSinceStandardEpoch) + // Use Equation 6.2.4 to aproximate the Equation of the center + let equationOfCenter = 360 / Double.pi * sin(sunMeanAnomaly.radians) * 0.016708 + // Add EoC to sun mean anomaly to get the sun true anomaly + var sunTrueAnomaly = sunMeanAnomaly.degrees + equationOfCenter + // Add or subtract multiples of 360° to adjust sun true anomaly to the range of 0° to 360° + sunTrueAnomaly = extendedMod(sunTrueAnomaly, 360) + // Getting ecliptic longitude. + var sunEclipticLongitude: Angle = .init(degrees: sunTrueAnomaly + sunEclipticLongitudePerigee.degrees) + + if sunEclipticLongitude.degrees > 360 { + sunEclipticLongitude.degrees -= 360 } - return morningGoldenHourEnd + let sunEclipticCoordinates: EclipticCoordinates = .init(eclipticLatitude: .zero, eclipticLongitude: sunEclipticLongitude) + // Ecliptic to Equatorial + var sunEquatorialCoordinates: EquatorialCoordinates = sunEclipticCoordinates.ecliptic2Equatorial() + // Equatorial to Horizon + let sunHorizonCoordinates: HorizonCoordinates = sunEquatorialCoordinates.equatorial2Horizon(lstDecimal: lstDecimal,latitude: latitude) ?? .init(altitude: .zero, azimuth: .zero) + + return .init(altitude: sunHorizonCoordinates.altitude, azimuth: sunHorizonCoordinates.azimuth) } private func getMarchEquinox() -> Date? { @@ -696,22 +712,6 @@ public struct Sun: Identifiable, Sendable { return decemberSolsticeUTC } - - /// - Returns: Length in meters of the object's shadow by the provided object height and current sun altitude. - public func shadowLength( - for objectHeight: Double = 1, - with altitude: Angle? = nil - ) -> Double? { - let altitude = altitude ?? self.altitude - - return if altitude.degrees > 0 && altitude.degrees < 90 { - objectHeight / tan(altitude.radians) - } else if altitude.degrees <= 0 { - nil - } else { - 0 - } - } } extension Sun: Equatable { From 553562bd75c4b1b1be6fbe95640811346fb4e954 Mon Sep 17 00:00:00 2001 From: Ignas Sireikis Date: Fri, 19 Sep 2025 12:29:16 -0500 Subject: [PATCH 28/91] Moved debugging utils into extension folder. --- .../Sun/Debugging Utils/Debugging Utils.swift | 60 ------------------- 1 file changed, 60 deletions(-) delete mode 100644 Sources/SunKit/Sun/Debugging Utils/Debugging Utils.swift diff --git a/Sources/SunKit/Sun/Debugging Utils/Debugging Utils.swift b/Sources/SunKit/Sun/Debugging Utils/Debugging Utils.swift deleted file mode 100644 index a3a4fe6..0000000 --- a/Sources/SunKit/Sun/Debugging Utils/Debugging Utils.swift +++ /dev/null @@ -1,60 +0,0 @@ -// -// Debugging Utils.swift -// -// -// Copyright 2024 Leonardo Bertinelli, Davide Biancardi, Raffaele Fulgente, Clelia Iovine, Nicolas Mariniello, Fabio Pizzano -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import Foundation - - -extension Sun { - /// Dumps all the Sun Events dates. - public func dumpDateInfos() { - print("Current Date -> \(dateFormatter.string(from: date))") - print("Sunrise -> \(dateFormatter.string(from: sunrise))") - print("Sunset -> \(dateFormatter.string(from: sunset))") - print("Solar Noon -> \(dateFormatter.string(from: solarNoon))") - print("Solar Midnight -> \(dateFormatter.string(from: solarMidnight))") - print("Evening Golden Hour Start -> \(dateFormatter.string(from: eveningGoldenHourStart))") - print("Evening Golden Hour End -> \(dateFormatter.string(from: eveningGoldenHourEnd))") - print("Morning Golden Hour Start -> \(dateFormatter.string(from: morningGoldenHourStart))") - print("Morning Golden Hour End -> \(dateFormatter.string(from: morningGoldenHourEnd))") - print("Civil dusk -> \(dateFormatter.string(from: civilDusk))") - print("Civil Dawn -> \(dateFormatter.string(from: civilDawn))") - print("Nautical Dusk -> \(dateFormatter.string(from: nauticalDusk))") - print("Nautical Dawn -> \(dateFormatter.string(from: nauticalDawn))") - print("Astronomical Dusk -> \(dateFormatter.string(from: astronomicalDusk))") - print("Astronomical Dawn -> \(dateFormatter.string(from: astronomicalDawn))") - print("Morning Blue Hour Start -> \(dateFormatter.string(from: morningBlueHourStart))") - print("Morning Blue Hour End -> \(dateFormatter.string(from: morningBlueHourEnd))") - print("evening Blue Hour Start -> \(dateFormatter.string(from: eveningBlueHourStart))") - print("evening Blue Hour End -> \(dateFormatter.string(from: eveningBlueHourEnd))") - - print("March Equinox -> \(dateFormatter.string(from: marchEquinox))") - print("June Solstice -> \(dateFormatter.string(from: juneSolstice))") - print("September Equinox -> \(dateFormatter.string(from: septemberEquinox))") - print("December Solstice -> \(dateFormatter.string(from: decemberSolstice))") - } - - private var dateFormatter: DateFormatter { - let dateFormatter = DateFormatter() - dateFormatter.locale = .current - dateFormatter.timeZone = self.timeZone - dateFormatter.timeStyle = .full - dateFormatter.dateStyle = .full - - return dateFormatter - } -} From ab5a5d0cbc01a9a1f612f817e6fe35af8095d1b9 Mon Sep 17 00:00:00 2001 From: Ignas Sireikis Date: Fri, 19 Sep 2025 12:31:22 -0500 Subject: [PATCH 29/91] Moved equatable comformance into extension folder. --- .../SunKit/Sun/Extensions/Sun+Equatable.swift | 28 +++++++++++++++++++ Sources/SunKit/Sun/Sun.swift | 8 ------ 2 files changed, 28 insertions(+), 8 deletions(-) create mode 100644 Sources/SunKit/Sun/Extensions/Sun+Equatable.swift diff --git a/Sources/SunKit/Sun/Extensions/Sun+Equatable.swift b/Sources/SunKit/Sun/Extensions/Sun+Equatable.swift new file mode 100644 index 0000000..271f41f --- /dev/null +++ b/Sources/SunKit/Sun/Extensions/Sun+Equatable.swift @@ -0,0 +1,28 @@ +// +// Sun+Equatable.swift +// +// +// Copyright 2024 Leonardo Bertinelli, Davide Biancardi, Raffaele Fulgente, Clelia Iovine, Nicolas Mariniello, Fabio Pizzano +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +import Foundation + + +extension Sun: Equatable { + public static func == (lhs: Sun, rhs: Sun) -> Bool { + lhs.location == rhs.location && + lhs.timeZone == rhs.timeZone && + lhs.date == rhs.date + } +} diff --git a/Sources/SunKit/Sun/Sun.swift b/Sources/SunKit/Sun/Sun.swift index 5823b66..35ccc2a 100644 --- a/Sources/SunKit/Sun/Sun.swift +++ b/Sources/SunKit/Sun/Sun.swift @@ -714,14 +714,6 @@ public struct Sun: Identifiable, Sendable { } } -extension Sun: Equatable { - public static func == (lhs: Sun, rhs: Sun) -> Bool { - lhs.location == rhs.location && - lhs.timeZone == rhs.timeZone && - lhs.date == rhs.date - } -} - extension Sun: Hashable { public func hash(into hasher: inout Hasher) { hasher.combine(location) From 96e3c5cfcbb71f76c65b5e8ae840c534ec86db23 Mon Sep 17 00:00:00 2001 From: Ignas Sireikis Date: Fri, 19 Sep 2025 12:32:19 -0500 Subject: [PATCH 30/91] Moved hashable comformance into extension folder. --- .../SunKit/Sun/Extensions/Sun+Hashable.swift | 28 +++++++++++++++++++ Sources/SunKit/Sun/Sun.swift | 8 ------ 2 files changed, 28 insertions(+), 8 deletions(-) create mode 100644 Sources/SunKit/Sun/Extensions/Sun+Hashable.swift diff --git a/Sources/SunKit/Sun/Extensions/Sun+Hashable.swift b/Sources/SunKit/Sun/Extensions/Sun+Hashable.swift new file mode 100644 index 0000000..bbcbce5 --- /dev/null +++ b/Sources/SunKit/Sun/Extensions/Sun+Hashable.swift @@ -0,0 +1,28 @@ +// +// Sun+Hashable.swift +// +// +// Copyright 2024 Leonardo Bertinelli, Davide Biancardi, Raffaele Fulgente, Clelia Iovine, Nicolas Mariniello, Fabio Pizzano +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +import Foundation + + +extension Sun: Hashable { + public func hash(into hasher: inout Hasher) { + hasher.combine(location) + hasher.combine(timeZone) + hasher.combine(date) + } +} diff --git a/Sources/SunKit/Sun/Sun.swift b/Sources/SunKit/Sun/Sun.swift index 35ccc2a..a0245fd 100644 --- a/Sources/SunKit/Sun/Sun.swift +++ b/Sources/SunKit/Sun/Sun.swift @@ -713,11 +713,3 @@ public struct Sun: Identifiable, Sendable { return decemberSolsticeUTC } } - -extension Sun: Hashable { - public func hash(into hasher: inout Hasher) { - hasher.combine(location) - hasher.combine(timeZone) - hasher.combine(date) - } -} From 0c81cb710fe7070a49ce985e4cb8487a9f6a04df Mon Sep 17 00:00:00 2001 From: Ignas Sireikis Date: Fri, 19 Sep 2025 12:44:34 -0500 Subject: [PATCH 31/91] Extracted sun horizon coordinate calculation as nested function. --- Sources/SunKit/Sun/Sun.swift | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/Sources/SunKit/Sun/Sun.swift b/Sources/SunKit/Sun/Sun.swift index a0245fd..1b95864 100644 --- a/Sources/SunKit/Sun/Sun.swift +++ b/Sources/SunKit/Sun/Sun.swift @@ -415,7 +415,7 @@ public struct Sun: Identifiable, Sendable { private mutating func updateSunCoordinates() { // Convert LCT to UT, GST, and LST times and adjust the date if needed let gstHMS = uT2GST(self.date) - let lstHMS = gST2LST(gstHMS,longitude: longitude) + let lstHMS = gST2LST(gstHMS, longitude: longitude) let lstDecimal = lstHMS.hMS2Decimal() // Julian number for standard epoch 2000 let jdEpoch = 2451545.00 @@ -429,6 +429,7 @@ public struct Sun: Identifiable, Sendable { let equationOfCenter = 360 / Double.pi * sin(sunMeanAnomaly.radians) * 0.016708 // Add EoC to sun mean anomaly to get the sun true anomaly var sunTrueAnomaly = sunMeanAnomaly.degrees + equationOfCenter + // Add or subtract multiples of 360° to adjust sun true anomaly to the range of 0° to 360° sunTrueAnomaly = extendedMod(sunTrueAnomaly, 360) var sunEclipticLongitude: Angle = .init(degrees: sunTrueAnomaly + sunEclipticLongitudePerigee.degrees) @@ -440,7 +441,11 @@ public struct Sun: Identifiable, Sendable { // Ecliptic to Equatorial sunEquatorialCoordinates = sunEclipticCoordinates.ecliptic2Equatorial() // Equatorial to Horizon - sunHorizonCoordinates = sunEquatorialCoordinates.equatorial2Horizon(lstDecimal: lstDecimal,latitude: latitude) ?? .init(altitude: .zero, azimuth: .zero) + sunHorizonCoordinates = calculateSunHorizonCoordinates() + + func calculateSunHorizonCoordinates() -> HorizonCoordinates { + sunEquatorialCoordinates.equatorial2Horizon(lstDecimal: lstDecimal,latitude: latitude) ?? .init(altitude: .zero, azimuth: .zero) + } } private func getSunMeanAnomaly(from elapsedDaysSinceStandardEpoch: Double) -> Angle { @@ -642,10 +647,11 @@ public struct Sun: Identifiable, Sendable { return newDate } + // TODO public func getSunHorizonCoordinatesFrom(date: Date) -> HorizonCoordinates { // Convert LCT to UT, GST, and LST times and adjust the date if needed let gstHMS = uT2GST(date) - let lstHMS = gST2LST(gstHMS,longitude: longitude) + let lstHMS = gST2LST(gstHMS, longitude: longitude) let lstDecimal = lstHMS.hMS2Decimal() // Julian number for standard epoch 2000 let jdEpoch = 2451545.00 @@ -661,7 +667,6 @@ public struct Sun: Identifiable, Sendable { var sunTrueAnomaly = sunMeanAnomaly.degrees + equationOfCenter // Add or subtract multiples of 360° to adjust sun true anomaly to the range of 0° to 360° sunTrueAnomaly = extendedMod(sunTrueAnomaly, 360) - // Getting ecliptic longitude. var sunEclipticLongitude: Angle = .init(degrees: sunTrueAnomaly + sunEclipticLongitudePerigee.degrees) if sunEclipticLongitude.degrees > 360 { @@ -672,9 +677,13 @@ public struct Sun: Identifiable, Sendable { // Ecliptic to Equatorial var sunEquatorialCoordinates: EquatorialCoordinates = sunEclipticCoordinates.ecliptic2Equatorial() // Equatorial to Horizon - let sunHorizonCoordinates: HorizonCoordinates = sunEquatorialCoordinates.equatorial2Horizon(lstDecimal: lstDecimal,latitude: latitude) ?? .init(altitude: .zero, azimuth: .zero) + let sunHorizonCoordinates: HorizonCoordinates = calculateSunHorizonCoordinates() return .init(altitude: sunHorizonCoordinates.altitude, azimuth: sunHorizonCoordinates.azimuth) + + func calculateSunHorizonCoordinates() -> HorizonCoordinates { + sunEquatorialCoordinates.equatorial2Horizon(lstDecimal: lstDecimal,latitude: latitude) ?? .init(altitude: .zero, azimuth: .zero) + } } private func getMarchEquinox() -> Date? { From a15c219aac847189fc230cf5265275a388884d99 Mon Sep 17 00:00:00 2001 From: Ignas Sireikis Date: Fri, 19 Sep 2025 12:48:44 -0500 Subject: [PATCH 32/91] Extracted sun equatorial coordinates calculation as nested function. --- Sources/SunKit/Sun/Sun.swift | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/Sources/SunKit/Sun/Sun.swift b/Sources/SunKit/Sun/Sun.swift index 1b95864..3ef5cb9 100644 --- a/Sources/SunKit/Sun/Sun.swift +++ b/Sources/SunKit/Sun/Sun.swift @@ -439,12 +439,16 @@ public struct Sun: Identifiable, Sendable { sunEclipticCoordinates = .init(eclipticLatitude: .zero, eclipticLongitude: sunEclipticLongitude) // Ecliptic to Equatorial - sunEquatorialCoordinates = sunEclipticCoordinates.ecliptic2Equatorial() + sunEquatorialCoordinates = calculateSunEquatorialCoordinates() // Equatorial to Horizon sunHorizonCoordinates = calculateSunHorizonCoordinates() func calculateSunHorizonCoordinates() -> HorizonCoordinates { - sunEquatorialCoordinates.equatorial2Horizon(lstDecimal: lstDecimal,latitude: latitude) ?? .init(altitude: .zero, azimuth: .zero) + sunEquatorialCoordinates.equatorial2Horizon(lstDecimal: lstDecimal, latitude: latitude) ?? .init(altitude: .zero, azimuth: .zero) + } + + func calculateSunEquatorialCoordinates() -> EquatorialCoordinates { + sunEclipticCoordinates.ecliptic2Equatorial() } } @@ -675,14 +679,18 @@ public struct Sun: Identifiable, Sendable { let sunEclipticCoordinates: EclipticCoordinates = .init(eclipticLatitude: .zero, eclipticLongitude: sunEclipticLongitude) // Ecliptic to Equatorial - var sunEquatorialCoordinates: EquatorialCoordinates = sunEclipticCoordinates.ecliptic2Equatorial() + var sunEquatorialCoordinates: EquatorialCoordinates = calculateSunEquatorialCoordinates() // Equatorial to Horizon let sunHorizonCoordinates: HorizonCoordinates = calculateSunHorizonCoordinates() return .init(altitude: sunHorizonCoordinates.altitude, azimuth: sunHorizonCoordinates.azimuth) func calculateSunHorizonCoordinates() -> HorizonCoordinates { - sunEquatorialCoordinates.equatorial2Horizon(lstDecimal: lstDecimal,latitude: latitude) ?? .init(altitude: .zero, azimuth: .zero) + sunEquatorialCoordinates.equatorial2Horizon(lstDecimal: lstDecimal, latitude: latitude) ?? .init(altitude: .zero, azimuth: .zero) + } + + func calculateSunEquatorialCoordinates() -> EquatorialCoordinates { + sunEclipticCoordinates.ecliptic2Equatorial() } } From 5550400cb195b956a29017634cf12f88cac0353f Mon Sep 17 00:00:00 2001 From: Ignas Sireikis Date: Fri, 19 Sep 2025 12:51:33 -0500 Subject: [PATCH 33/91] Extracted sun ecliptic coordinates calculation as nested function. --- Sources/SunKit/Sun/Sun.swift | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/Sources/SunKit/Sun/Sun.swift b/Sources/SunKit/Sun/Sun.swift index 3ef5cb9..6926ec6 100644 --- a/Sources/SunKit/Sun/Sun.swift +++ b/Sources/SunKit/Sun/Sun.swift @@ -437,7 +437,7 @@ public struct Sun: Identifiable, Sendable { sunEclipticLongitude.degrees -= 360 } - sunEclipticCoordinates = .init(eclipticLatitude: .zero, eclipticLongitude: sunEclipticLongitude) + sunEclipticCoordinates = calculateSunEclipticCoordinates() // Ecliptic to Equatorial sunEquatorialCoordinates = calculateSunEquatorialCoordinates() // Equatorial to Horizon @@ -450,6 +450,10 @@ public struct Sun: Identifiable, Sendable { func calculateSunEquatorialCoordinates() -> EquatorialCoordinates { sunEclipticCoordinates.ecliptic2Equatorial() } + + func calculateSunEclipticCoordinates() -> EclipticCoordinates { + .init(eclipticLatitude: .zero, eclipticLongitude: sunEclipticLongitude) + } } private func getSunMeanAnomaly(from elapsedDaysSinceStandardEpoch: Double) -> Angle { @@ -677,7 +681,7 @@ public struct Sun: Identifiable, Sendable { sunEclipticLongitude.degrees -= 360 } - let sunEclipticCoordinates: EclipticCoordinates = .init(eclipticLatitude: .zero, eclipticLongitude: sunEclipticLongitude) + let sunEclipticCoordinates: EclipticCoordinates = calculateSunEclipticCoordinates() // Ecliptic to Equatorial var sunEquatorialCoordinates: EquatorialCoordinates = calculateSunEquatorialCoordinates() // Equatorial to Horizon @@ -692,6 +696,10 @@ public struct Sun: Identifiable, Sendable { func calculateSunEquatorialCoordinates() -> EquatorialCoordinates { sunEclipticCoordinates.ecliptic2Equatorial() } + + func calculateSunEclipticCoordinates() -> EclipticCoordinates { + .init(eclipticLatitude: .zero, eclipticLongitude: sunEclipticLongitude) + } } private func getMarchEquinox() -> Date? { From c4ac1cb70f8d1b6c2e8b641d1e76cc8e083aabe1 Mon Sep 17 00:00:00 2001 From: Ignas Sireikis Date: Fri, 19 Sep 2025 12:54:05 -0500 Subject: [PATCH 34/91] Added todo comment. --- Sources/SunKit/Sun/Sun.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Sources/SunKit/Sun/Sun.swift b/Sources/SunKit/Sun/Sun.swift index 6926ec6..9396dbc 100644 --- a/Sources/SunKit/Sun/Sun.swift +++ b/Sources/SunKit/Sun/Sun.swift @@ -655,7 +655,7 @@ public struct Sun: Identifiable, Sendable { return newDate } - // TODO + // TODO: Extract functions to collapse into updateSunCoordinates public func getSunHorizonCoordinatesFrom(date: Date) -> HorizonCoordinates { // Convert LCT to UT, GST, and LST times and adjust the date if needed let gstHMS = uT2GST(date) From 2365b0b273042bc9073b6df1039f1fe7727f55ba Mon Sep 17 00:00:00 2001 From: Ignas Sireikis Date: Fri, 19 Sep 2025 12:59:23 -0500 Subject: [PATCH 35/91] Parameterized calculateSunEclipticCoordinates function. Extracted nested function. --- Sources/SunKit/Sun/Sun.swift | 20 ++++++++------------ 1 file changed, 8 insertions(+), 12 deletions(-) diff --git a/Sources/SunKit/Sun/Sun.swift b/Sources/SunKit/Sun/Sun.swift index 9396dbc..e8a2158 100644 --- a/Sources/SunKit/Sun/Sun.swift +++ b/Sources/SunKit/Sun/Sun.swift @@ -97,6 +97,8 @@ public struct Sun: Identifiable, Sendable { sunHorizonCoordinates.altitude } + private var sunHorizonCoordinates: HorizonCoordinates = .init(altitude: .zero, azimuth: .zero) + public private(set) var sunEquatorialCoordinates: EquatorialCoordinates = .init(declination: .zero) public private(set) var sunEclipticCoordinates: EclipticCoordinates = .init(eclipticLatitude: .zero, eclipticLongitude: .zero) @@ -308,8 +310,6 @@ public struct Sun: Identifiable, Sendable { timeZone.secondsFromGMT(for: self.date) } - private var sunHorizonCoordinates: HorizonCoordinates = .init(altitude: .zero, azimuth: .zero) - // Sun constants private let sunEclipticLongitudeAtTheEpoch: Angle = .init(degrees: 280.466069) private let sunEclipticLongitudePerigee: Angle = .init(degrees: 282.938346) @@ -437,7 +437,7 @@ public struct Sun: Identifiable, Sendable { sunEclipticLongitude.degrees -= 360 } - sunEclipticCoordinates = calculateSunEclipticCoordinates() + sunEclipticCoordinates = calculateSunEclipticCoordinates(using: sunEclipticLongitude) // Ecliptic to Equatorial sunEquatorialCoordinates = calculateSunEquatorialCoordinates() // Equatorial to Horizon @@ -450,10 +450,10 @@ public struct Sun: Identifiable, Sendable { func calculateSunEquatorialCoordinates() -> EquatorialCoordinates { sunEclipticCoordinates.ecliptic2Equatorial() } - - func calculateSunEclipticCoordinates() -> EclipticCoordinates { - .init(eclipticLatitude: .zero, eclipticLongitude: sunEclipticLongitude) - } + } + + private func calculateSunEclipticCoordinates(using sunEclipticLongitude: Angle) -> EclipticCoordinates { + .init(eclipticLatitude: .zero, eclipticLongitude: sunEclipticLongitude) } private func getSunMeanAnomaly(from elapsedDaysSinceStandardEpoch: Double) -> Angle { @@ -681,7 +681,7 @@ public struct Sun: Identifiable, Sendable { sunEclipticLongitude.degrees -= 360 } - let sunEclipticCoordinates: EclipticCoordinates = calculateSunEclipticCoordinates() + let sunEclipticCoordinates: EclipticCoordinates = calculateSunEclipticCoordinates(using: sunEclipticLongitude) // Ecliptic to Equatorial var sunEquatorialCoordinates: EquatorialCoordinates = calculateSunEquatorialCoordinates() // Equatorial to Horizon @@ -696,10 +696,6 @@ public struct Sun: Identifiable, Sendable { func calculateSunEquatorialCoordinates() -> EquatorialCoordinates { sunEclipticCoordinates.ecliptic2Equatorial() } - - func calculateSunEclipticCoordinates() -> EclipticCoordinates { - .init(eclipticLatitude: .zero, eclipticLongitude: sunEclipticLongitude) - } } private func getMarchEquinox() -> Date? { From 3990efaa7d57d9b1f3ffc941efbf4eb37ff389eb Mon Sep 17 00:00:00 2001 From: Ignas Sireikis Date: Fri, 19 Sep 2025 13:02:58 -0500 Subject: [PATCH 36/91] Parameterized calculateSunEquatorialCoordinates function. Extracted nested function. --- Sources/SunKit/Sun/Sun.swift | 20 +++++++++----------- 1 file changed, 9 insertions(+), 11 deletions(-) diff --git a/Sources/SunKit/Sun/Sun.swift b/Sources/SunKit/Sun/Sun.swift index e8a2158..3306e40 100644 --- a/Sources/SunKit/Sun/Sun.swift +++ b/Sources/SunKit/Sun/Sun.swift @@ -439,23 +439,25 @@ public struct Sun: Identifiable, Sendable { sunEclipticCoordinates = calculateSunEclipticCoordinates(using: sunEclipticLongitude) // Ecliptic to Equatorial - sunEquatorialCoordinates = calculateSunEquatorialCoordinates() + sunEquatorialCoordinates = calculateSunEquatorialCoordinates(from: sunEclipticCoordinates) // Equatorial to Horizon sunHorizonCoordinates = calculateSunHorizonCoordinates() func calculateSunHorizonCoordinates() -> HorizonCoordinates { sunEquatorialCoordinates.equatorial2Horizon(lstDecimal: lstDecimal, latitude: latitude) ?? .init(altitude: .zero, azimuth: .zero) } - - func calculateSunEquatorialCoordinates() -> EquatorialCoordinates { - sunEclipticCoordinates.ecliptic2Equatorial() - } } - private func calculateSunEclipticCoordinates(using sunEclipticLongitude: Angle) -> EclipticCoordinates { + // TODO: Move behavior into EclipticCoordinates + private func calculateSunEclipticCoordinates(using sunEclipticLongitude: Angle) -> EclipticCoordinates { .init(eclipticLatitude: .zero, eclipticLongitude: sunEclipticLongitude) } + // TODO: Move behavior into EclipticCoordinates + private func calculateSunEquatorialCoordinates(from sunEclipticCoordinates: EclipticCoordinates) -> EquatorialCoordinates { + sunEclipticCoordinates.ecliptic2Equatorial() + } + private func getSunMeanAnomaly(from elapsedDaysSinceStandardEpoch: Double) -> Angle { var sunMeanAnomaly: Angle = .init(degrees:(((360.0 * elapsedDaysSinceStandardEpoch) / 365.242191) + sunEclipticLongitudeAtTheEpoch.degrees - sunEclipticLongitudePerigee.degrees)) sunMeanAnomaly = .init(degrees: extendedMod(sunMeanAnomaly.degrees, 360)) @@ -683,7 +685,7 @@ public struct Sun: Identifiable, Sendable { let sunEclipticCoordinates: EclipticCoordinates = calculateSunEclipticCoordinates(using: sunEclipticLongitude) // Ecliptic to Equatorial - var sunEquatorialCoordinates: EquatorialCoordinates = calculateSunEquatorialCoordinates() + var sunEquatorialCoordinates: EquatorialCoordinates = calculateSunEquatorialCoordinates(from: sunEclipticCoordinates) // Equatorial to Horizon let sunHorizonCoordinates: HorizonCoordinates = calculateSunHorizonCoordinates() @@ -692,10 +694,6 @@ public struct Sun: Identifiable, Sendable { func calculateSunHorizonCoordinates() -> HorizonCoordinates { sunEquatorialCoordinates.equatorial2Horizon(lstDecimal: lstDecimal, latitude: latitude) ?? .init(altitude: .zero, azimuth: .zero) } - - func calculateSunEquatorialCoordinates() -> EquatorialCoordinates { - sunEclipticCoordinates.ecliptic2Equatorial() - } } private func getMarchEquinox() -> Date? { From ee5eed2e25ab267b7eeedeb3b0a6b2f1bb9a2d9c Mon Sep 17 00:00:00 2001 From: Ignas Sireikis Date: Fri, 19 Sep 2025 13:09:48 -0500 Subject: [PATCH 37/91] Parameterized calculateSunHorizonCoordinates function. Added comment to refactor EquatorialCoordinates. --- Sources/SunKit/Sun/Sun.swift | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/Sources/SunKit/Sun/Sun.swift b/Sources/SunKit/Sun/Sun.swift index 3306e40..af38766 100644 --- a/Sources/SunKit/Sun/Sun.swift +++ b/Sources/SunKit/Sun/Sun.swift @@ -439,25 +439,26 @@ public struct Sun: Identifiable, Sendable { sunEclipticCoordinates = calculateSunEclipticCoordinates(using: sunEclipticLongitude) // Ecliptic to Equatorial - sunEquatorialCoordinates = calculateSunEquatorialCoordinates(from: sunEclipticCoordinates) + sunEquatorialCoordinates = calculateSunEquatorialCoordinates(using: sunEclipticCoordinates) // Equatorial to Horizon - sunHorizonCoordinates = calculateSunHorizonCoordinates() + sunHorizonCoordinates = calculateSunHorizonCoordinates(using: lstDecimal) - func calculateSunHorizonCoordinates() -> HorizonCoordinates { + // TODO: Move behavior into EquatorialCoordinates, refactor to Object + func calculateSunHorizonCoordinates(using lstDecimal: Double) -> HorizonCoordinates { sunEquatorialCoordinates.equatorial2Horizon(lstDecimal: lstDecimal, latitude: latitude) ?? .init(altitude: .zero, azimuth: .zero) } } - // TODO: Move behavior into EclipticCoordinates + // TODO: Move behavior into EclipticCoordinates, refactor to Object private func calculateSunEclipticCoordinates(using sunEclipticLongitude: Angle) -> EclipticCoordinates { .init(eclipticLatitude: .zero, eclipticLongitude: sunEclipticLongitude) } // TODO: Move behavior into EclipticCoordinates - private func calculateSunEquatorialCoordinates(from sunEclipticCoordinates: EclipticCoordinates) -> EquatorialCoordinates { + private func calculateSunEquatorialCoordinates(using sunEclipticCoordinates: EclipticCoordinates) -> EquatorialCoordinates { sunEclipticCoordinates.ecliptic2Equatorial() } - + private func getSunMeanAnomaly(from elapsedDaysSinceStandardEpoch: Double) -> Angle { var sunMeanAnomaly: Angle = .init(degrees:(((360.0 * elapsedDaysSinceStandardEpoch) / 365.242191) + sunEclipticLongitudeAtTheEpoch.degrees - sunEclipticLongitudePerigee.degrees)) sunMeanAnomaly = .init(degrees: extendedMod(sunMeanAnomaly.degrees, 360)) @@ -685,13 +686,14 @@ public struct Sun: Identifiable, Sendable { let sunEclipticCoordinates: EclipticCoordinates = calculateSunEclipticCoordinates(using: sunEclipticLongitude) // Ecliptic to Equatorial - var sunEquatorialCoordinates: EquatorialCoordinates = calculateSunEquatorialCoordinates(from: sunEclipticCoordinates) + var sunEquatorialCoordinates: EquatorialCoordinates = calculateSunEquatorialCoordinates(using: sunEclipticCoordinates) // Equatorial to Horizon - let sunHorizonCoordinates: HorizonCoordinates = calculateSunHorizonCoordinates() + let sunHorizonCoordinates: HorizonCoordinates = calculateSunHorizonCoordinates(using: lstDecimal) return .init(altitude: sunHorizonCoordinates.altitude, azimuth: sunHorizonCoordinates.azimuth) - func calculateSunHorizonCoordinates() -> HorizonCoordinates { + // TODO: Move behavior intoEquatorialCoordinates, refactor to Object + func calculateSunHorizonCoordinates(using lstDecimal: Double) -> HorizonCoordinates { sunEquatorialCoordinates.equatorial2Horizon(lstDecimal: lstDecimal, latitude: latitude) ?? .init(altitude: .zero, azimuth: .zero) } } From a115b3ed3e8e02352b8ed75510c609fdb27e7173 Mon Sep 17 00:00:00 2001 From: Ignas Sireikis Date: Fri, 19 Sep 2025 13:14:29 -0500 Subject: [PATCH 38/91] Made horizontal spacing consistent. --- Sources/SunKit/EquatorialCoordinates.swift | 23 ++++++++++++---------- 1 file changed, 13 insertions(+), 10 deletions(-) diff --git a/Sources/SunKit/EquatorialCoordinates.swift b/Sources/SunKit/EquatorialCoordinates.swift index 2b90ab1..85678b0 100644 --- a/Sources/SunKit/EquatorialCoordinates.swift +++ b/Sources/SunKit/EquatorialCoordinates.swift @@ -25,19 +25,19 @@ public struct EquatorialCoordinates: Equatable, Hashable, Codable, Sendable { private(set) var hourAngle: Angle? - init(declination: Angle,rightAscension: Angle, hourAngle: Angle) { + init(declination: Angle, rightAscension: Angle, hourAngle: Angle) { self.declination = declination self.rightAscension = rightAscension self.hourAngle = hourAngle } - init(declination: Angle,rightAscension: Angle) { + init(declination: Angle, rightAscension: Angle) { self.declination = declination self.rightAscension = rightAscension self.hourAngle = nil } - init(declination: Angle,hourAngle: Angle) { + init(declination: Angle, hourAngle: Angle) { self.declination = declination self.hourAngle = hourAngle self.rightAscension = nil @@ -68,7 +68,9 @@ public struct EquatorialCoordinates: Equatable, Hashable, Codable, Sendable { /// - Parameter lstDecimal: Local Sideral Time in decimal /// - Returns: The value of right ascension just been set. Nil if hour angle is also nil public mutating func setRightAscensionFrom(lstDecimal: Double) -> Angle? { - guard let hourAngle = self.hourAngle else {return nil} + guard let hourAngle = self.hourAngle else { + return nil + } let hourAngleDecimal = hourAngle.degrees / 15 self.rightAscension = .init(degrees: lstDecimal - hourAngleDecimal) @@ -102,7 +104,7 @@ public struct EquatorialCoordinates: Equatable, Hashable, Codable, Sendable { let tTwoEquatorialToHorizon = tOneEquatorialToHorizon / (cos(latitude.radians) * cos(altitude.radians)) //Step8: var azimuth: Angle = .init(radians: acos(tTwoEquatorialToHorizon)) - if sin(hourAngle!.radians) >= 0{ + if sin(hourAngle!.radians) >= 0 { azimuth.degrees = 360 - azimuth.degrees } @@ -131,7 +133,7 @@ public struct EquatorialCoordinates: Equatable, Hashable, Codable, Sendable { let tTwoEquatorialToHorizon = tOneEquatorialToHorizon / (cos(latitude.radians) * cos(altitude.radians)) //Step8: var azimuth: Angle = .init(radians: acos(tTwoEquatorialToHorizon)) - if sin(hourAngle!.radians) >= 0{ + if sin(hourAngle!.radians) >= 0 { azimuth.degrees = 360 - azimuth.degrees } @@ -139,9 +141,11 @@ public struct EquatorialCoordinates: Equatable, Hashable, Codable, Sendable { } public func equatorial2Ecliptic() -> EclipticCoordinates? { - guard var rightAscension = rightAscension else {return nil} + guard var rightAscension = rightAscension else { + return nil + } - rightAscension.degrees = rightAscension.degrees * 15 //from h format to degrees + rightAscension.degrees = rightAscension.degrees * 15 // from h format to degrees //Step5: let tEquatorialToEcliptic: Angle = .init(radians: sin(declination.radians) * cos(EclipticCoordinates.obliquityOfTheEcliptic.radians) - cos(declination.radians) * sin(EclipticCoordinates.obliquityOfTheEcliptic.radians) * sin(rightAscension.radians)) //Step6: @@ -153,8 +157,7 @@ public struct EquatorialCoordinates: Equatable, Hashable, Codable, Sendable { //Step9: var r: Angle = .init(radians: atan(yEquatorialToEcliptic / xEquatorialToEcliptic)) //Step9: - switch (yEquatorialToEcliptic >= 0,xEquatorialToEcliptic >= 0){ - + switch (yEquatorialToEcliptic >= 0, xEquatorialToEcliptic >= 0) { case (true, true): break case (true,false): From d66cfaf4c92246b316dc6451aae26e40bfae1511 Mon Sep 17 00:00:00 2001 From: Ignas Sireikis Date: Fri, 19 Sep 2025 13:15:42 -0500 Subject: [PATCH 39/91] Removed redundant comments. --- Sources/SunKit/EquatorialCoordinates.swift | 20 ++++---------------- 1 file changed, 4 insertions(+), 16 deletions(-) diff --git a/Sources/SunKit/EquatorialCoordinates.swift b/Sources/SunKit/EquatorialCoordinates.swift index 85678b0..336f02f 100644 --- a/Sources/SunKit/EquatorialCoordinates.swift +++ b/Sources/SunKit/EquatorialCoordinates.swift @@ -94,16 +94,12 @@ public struct EquatorialCoordinates: Equatable, Hashable, Codable, Sendable { return nil } - //Step4: let tZeroEquatorialToHorizon = sin(declination.radians) * sin(latitude.radians) + cos(declination.radians) * cos(latitude.radians) * cos(hourAngle!.radians) - //Step5: let altitude: Angle = .init(radians: asin(tZeroEquatorialToHorizon)) - //Step6: let tOneEquatorialToHorizon = sin(declination.radians) - sin(latitude.radians) * sin(altitude.radians) - //Step7: let tTwoEquatorialToHorizon = tOneEquatorialToHorizon / (cos(latitude.radians) * cos(altitude.radians)) - //Step8: var azimuth: Angle = .init(radians: acos(tTwoEquatorialToHorizon)) + if sin(hourAngle!.radians) >= 0 { azimuth.degrees = 360 - azimuth.degrees } @@ -123,16 +119,12 @@ public struct EquatorialCoordinates: Equatable, Hashable, Codable, Sendable { return nil } - //Step4: let tZeroEquatorialToHorizon = sin(declination.radians) * sin(latitude.radians) + cos(declination.radians) * cos(latitude.radians) * cos(hourAngle!.radians) - //Step5: let altitude: Angle = .init(radians: asin(tZeroEquatorialToHorizon)) - //Step6: let tOneEquatorialToHorizon = sin(declination.radians) - sin(latitude.radians) * sin(altitude.radians) - //Step7: let tTwoEquatorialToHorizon = tOneEquatorialToHorizon / (cos(latitude.radians) * cos(altitude.radians)) - //Step8: var azimuth: Angle = .init(radians: acos(tTwoEquatorialToHorizon)) + if sin(hourAngle!.radians) >= 0 { azimuth.degrees = 360 - azimuth.degrees } @@ -146,17 +138,13 @@ public struct EquatorialCoordinates: Equatable, Hashable, Codable, Sendable { } rightAscension.degrees = rightAscension.degrees * 15 // from h format to degrees - //Step5: + let tEquatorialToEcliptic: Angle = .init(radians: sin(declination.radians) * cos(EclipticCoordinates.obliquityOfTheEcliptic.radians) - cos(declination.radians) * sin(EclipticCoordinates.obliquityOfTheEcliptic.radians) * sin(rightAscension.radians)) - //Step6: let eclipticLatitude: Angle = .init(radians: asin(tEquatorialToEcliptic.radians)) - //Step7: let yEquatorialToEcliptic = sin(rightAscension.radians) * cos(EclipticCoordinates.obliquityOfTheEcliptic.radians) + tan(declination.radians) * sin(EclipticCoordinates.obliquityOfTheEcliptic.radians) - //Step8: let xEquatorialToEcliptic = cos(rightAscension.radians) - //Step9: var r: Angle = .init(radians: atan(yEquatorialToEcliptic / xEquatorialToEcliptic)) - //Step9: + switch (yEquatorialToEcliptic >= 0, xEquatorialToEcliptic >= 0) { case (true, true): break From 9df29620635459cf7450c47bd4ad2902beef3d36 Mon Sep 17 00:00:00 2001 From: Ignas Sireikis Date: Fri, 19 Sep 2025 13:27:02 -0500 Subject: [PATCH 40/91] Extracted calculateHorizonCoordinates function. --- Sources/SunKit/EquatorialCoordinates.swift | 16 +++++----------- 1 file changed, 5 insertions(+), 11 deletions(-) diff --git a/Sources/SunKit/EquatorialCoordinates.swift b/Sources/SunKit/EquatorialCoordinates.swift index 336f02f..4e473ec 100644 --- a/Sources/SunKit/EquatorialCoordinates.swift +++ b/Sources/SunKit/EquatorialCoordinates.swift @@ -94,17 +94,7 @@ public struct EquatorialCoordinates: Equatable, Hashable, Codable, Sendable { return nil } - let tZeroEquatorialToHorizon = sin(declination.radians) * sin(latitude.radians) + cos(declination.radians) * cos(latitude.radians) * cos(hourAngle!.radians) - let altitude: Angle = .init(radians: asin(tZeroEquatorialToHorizon)) - let tOneEquatorialToHorizon = sin(declination.radians) - sin(latitude.radians) * sin(altitude.radians) - let tTwoEquatorialToHorizon = tOneEquatorialToHorizon / (cos(latitude.radians) * cos(altitude.radians)) - var azimuth: Angle = .init(radians: acos(tTwoEquatorialToHorizon)) - - if sin(hourAngle!.radians) >= 0 { - azimuth.degrees = 360 - azimuth.degrees - } - - return .init(altitude: altitude, azimuth: azimuth) + return calculateHorizonCoordinates(from: latitude) } /// Converts Equatorial coordinates to Horizon coordinates. @@ -119,6 +109,10 @@ public struct EquatorialCoordinates: Equatable, Hashable, Codable, Sendable { return nil } + return calculateHorizonCoordinates(from: latitude) + } + + private func calculateHorizonCoordinates(from latitude: Angle) -> HorizonCoordinates { let tZeroEquatorialToHorizon = sin(declination.radians) * sin(latitude.radians) + cos(declination.radians) * cos(latitude.radians) * cos(hourAngle!.radians) let altitude: Angle = .init(radians: asin(tZeroEquatorialToHorizon)) let tOneEquatorialToHorizon = sin(declination.radians) - sin(latitude.radians) * sin(altitude.radians) From 8d8b695aea009515b6d5594382ca18348a303658 Mon Sep 17 00:00:00 2001 From: Ignas Sireikis Date: Fri, 19 Sep 2025 13:47:34 -0500 Subject: [PATCH 41/91] Added tracking to missing file. --- .../SunKit/Sun/Extensions/Sun+debugging.swift | 60 +++++++++++++++++++ 1 file changed, 60 insertions(+) create mode 100644 Sources/SunKit/Sun/Extensions/Sun+debugging.swift diff --git a/Sources/SunKit/Sun/Extensions/Sun+debugging.swift b/Sources/SunKit/Sun/Extensions/Sun+debugging.swift new file mode 100644 index 0000000..dffa9f7 --- /dev/null +++ b/Sources/SunKit/Sun/Extensions/Sun+debugging.swift @@ -0,0 +1,60 @@ +// +// Sun+debugging.swift +// +// +// Copyright 2024 Leonardo Bertinelli, Davide Biancardi, Raffaele Fulgente, Clelia Iovine, Nicolas Mariniello, Fabio Pizzano +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +import Foundation + + +extension Sun { + /// Dumps all the Sun Events dates. + public func dumpDateInfos() { + print("Current Date -> \(dateFormatter.string(from: date))") + print("Sunrise -> \(dateFormatter.string(from: sunrise))") + print("Sunset -> \(dateFormatter.string(from: sunset))") + print("Solar Noon -> \(dateFormatter.string(from: solarNoon))") + print("Solar Midnight -> \(dateFormatter.string(from: solarMidnight))") + print("Evening Golden Hour Start -> \(dateFormatter.string(from: eveningGoldenHourStart))") + print("Evening Golden Hour End -> \(dateFormatter.string(from: eveningGoldenHourEnd))") + print("Morning Golden Hour Start -> \(dateFormatter.string(from: morningGoldenHourStart))") + print("Morning Golden Hour End -> \(dateFormatter.string(from: morningGoldenHourEnd))") + print("Civil dusk -> \(dateFormatter.string(from: civilDusk))") + print("Civil Dawn -> \(dateFormatter.string(from: civilDawn))") + print("Nautical Dusk -> \(dateFormatter.string(from: nauticalDusk))") + print("Nautical Dawn -> \(dateFormatter.string(from: nauticalDawn))") + print("Astronomical Dusk -> \(dateFormatter.string(from: astronomicalDusk))") + print("Astronomical Dawn -> \(dateFormatter.string(from: astronomicalDawn))") + print("Morning Blue Hour Start -> \(dateFormatter.string(from: morningBlueHourStart))") + print("Morning Blue Hour End -> \(dateFormatter.string(from: morningBlueHourEnd))") + print("evening Blue Hour Start -> \(dateFormatter.string(from: eveningBlueHourStart))") + print("evening Blue Hour End -> \(dateFormatter.string(from: eveningBlueHourEnd))") + + print("March Equinox -> \(dateFormatter.string(from: marchEquinox))") + print("June Solstice -> \(dateFormatter.string(from: juneSolstice))") + print("September Equinox -> \(dateFormatter.string(from: septemberEquinox))") + print("December Solstice -> \(dateFormatter.string(from: decemberSolstice))") + } + + private var dateFormatter: DateFormatter { + let dateFormatter = DateFormatter() + dateFormatter.locale = .current + dateFormatter.timeZone = self.timeZone + dateFormatter.timeStyle = .full + dateFormatter.dateStyle = .full + + return dateFormatter + } +} From 6f0a17d593322d8e120d1491d825814338c83da9 Mon Sep 17 00:00:00 2001 From: Ignas Sireikis Date: Fri, 19 Sep 2025 13:51:09 -0500 Subject: [PATCH 42/91] Added initial folder. --- Sources/SunKit/EquatorialCoordinates.swift | 157 ------------------ Sources/SunKit/HorizonCoordinates.swift | 41 ----- .../EclipticCoordinates.swift | 0 3 files changed, 198 deletions(-) delete mode 100644 Sources/SunKit/EquatorialCoordinates.swift delete mode 100644 Sources/SunKit/HorizonCoordinates.swift rename Sources/SunKit/{ => Sun Coordinates}/EclipticCoordinates.swift (100%) diff --git a/Sources/SunKit/EquatorialCoordinates.swift b/Sources/SunKit/EquatorialCoordinates.swift deleted file mode 100644 index 4e473ec..0000000 --- a/Sources/SunKit/EquatorialCoordinates.swift +++ /dev/null @@ -1,157 +0,0 @@ -// -// EquatorialCoordinates.swift -// -// -// Copyright 2024 Leonardo Bertinelli, Davide Biancardi, Raffaele Fulgente, Clelia Iovine, Nicolas Mariniello, Fabio Pizzano -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import Foundation - - -public struct EquatorialCoordinates: Equatable, Hashable, Codable, Sendable { - public private(set) var rightAscension: Angle? // rightAscension.degrees refers to h format - public private(set) var declination: Angle //delta - - private(set) var hourAngle: Angle? - - init(declination: Angle, rightAscension: Angle, hourAngle: Angle) { - self.declination = declination - self.rightAscension = rightAscension - self.hourAngle = hourAngle - } - - init(declination: Angle, rightAscension: Angle) { - self.declination = declination - self.rightAscension = rightAscension - self.hourAngle = nil - } - - init(declination: Angle, hourAngle: Angle) { - self.declination = declination - self.hourAngle = hourAngle - self.rightAscension = nil - } - - init(declination: Angle) { - self.declination = declination - } - - /// To set right ascension we need LST and right ascension. If right ascension is nill we can't set it. - /// - Parameter lstDecimal: Local Sideral Time in decimal - /// - Returns: The value of hour angle just been set. Nil if right ascension is also nil - public mutating func setHourAngleFrom(lstDecimal: Double) -> Angle? { - guard let rightAscension = self.rightAscension else { - return nil - } - - var hourAngleDecimal = lstDecimal - rightAscension.degrees - if hourAngleDecimal < 0 { - hourAngleDecimal += 24 - } - self.hourAngle = .init(degrees: hourAngleDecimal * 15) - - return self.hourAngle - } - - /// To set right ascension we need LST and hour angle. If hour angle is nill we can't set it. - /// - Parameter lstDecimal: Local Sideral Time in decimal - /// - Returns: The value of right ascension just been set. Nil if hour angle is also nil - public mutating func setRightAscensionFrom(lstDecimal: Double) -> Angle? { - guard let hourAngle = self.hourAngle else { - return nil - } - - let hourAngleDecimal = hourAngle.degrees / 15 - self.rightAscension = .init(degrees: lstDecimal - hourAngleDecimal) - - return self.rightAscension - } - - /// Converts Equatorial coordinates to Horizon coordinates. - /// - /// Since horizon coordinates depend on the position, we need also latitude parameter to create an EquatorialCoordinates instance. - /// - /// - Parameters: - /// - lstDecimal: Local Sidereal Time in decimal format. - /// - latitude: Latitude of the observer - /// - Returns: Horizon coordinates for the given latitude and LST. Nil if hour angle cannot be computed due to the miss right ascnsion information - public mutating func equatorial2Horizon( - lstDecimal: Double, - latitude: Angle - ) -> HorizonCoordinates? { - guard let _ = setHourAngleFrom(lstDecimal: lstDecimal) else { - return nil - } - - return calculateHorizonCoordinates(from: latitude) - } - - /// Converts Equatorial coordinates to Horizon coordinates. - /// - /// Since horizon coordinates depend on the position, we need also latitude parameter to create an EquatorialCoordinates instance. - /// - /// - Parameters: - /// - latitude: Latitude of the observer - /// - Returns: Horizon coordinates for the given latitude. Nil if hour angle is not defined. - public func equatorial2Horizon(latitude: Angle) -> HorizonCoordinates? { - guard let _ = self.hourAngle else { - return nil - } - - return calculateHorizonCoordinates(from: latitude) - } - - private func calculateHorizonCoordinates(from latitude: Angle) -> HorizonCoordinates { - let tZeroEquatorialToHorizon = sin(declination.radians) * sin(latitude.radians) + cos(declination.radians) * cos(latitude.radians) * cos(hourAngle!.radians) - let altitude: Angle = .init(radians: asin(tZeroEquatorialToHorizon)) - let tOneEquatorialToHorizon = sin(declination.radians) - sin(latitude.radians) * sin(altitude.radians) - let tTwoEquatorialToHorizon = tOneEquatorialToHorizon / (cos(latitude.radians) * cos(altitude.radians)) - var azimuth: Angle = .init(radians: acos(tTwoEquatorialToHorizon)) - - if sin(hourAngle!.radians) >= 0 { - azimuth.degrees = 360 - azimuth.degrees - } - - return .init(altitude: altitude, azimuth: azimuth) - } - - public func equatorial2Ecliptic() -> EclipticCoordinates? { - guard var rightAscension = rightAscension else { - return nil - } - - rightAscension.degrees = rightAscension.degrees * 15 // from h format to degrees - - let tEquatorialToEcliptic: Angle = .init(radians: sin(declination.radians) * cos(EclipticCoordinates.obliquityOfTheEcliptic.radians) - cos(declination.radians) * sin(EclipticCoordinates.obliquityOfTheEcliptic.radians) * sin(rightAscension.radians)) - let eclipticLatitude: Angle = .init(radians: asin(tEquatorialToEcliptic.radians)) - let yEquatorialToEcliptic = sin(rightAscension.radians) * cos(EclipticCoordinates.obliquityOfTheEcliptic.radians) + tan(declination.radians) * sin(EclipticCoordinates.obliquityOfTheEcliptic.radians) - let xEquatorialToEcliptic = cos(rightAscension.radians) - var r: Angle = .init(radians: atan(yEquatorialToEcliptic / xEquatorialToEcliptic)) - - switch (yEquatorialToEcliptic >= 0, xEquatorialToEcliptic >= 0) { - case (true, true): - break - case (true,false): - r.degrees += 180 - case(false,true): - r.degrees += 360 - case(false,false): - r.degrees += 180 - } - - let eclipticLongitude: Angle = .init(degrees: r.degrees) - - return .init(eclipticLatitude: eclipticLatitude, eclipticLongitude: eclipticLongitude) - } -} diff --git a/Sources/SunKit/HorizonCoordinates.swift b/Sources/SunKit/HorizonCoordinates.swift deleted file mode 100644 index 9baf52a..0000000 --- a/Sources/SunKit/HorizonCoordinates.swift +++ /dev/null @@ -1,41 +0,0 @@ -// -// HoorizonCoordinates.swift -// -// -// Copyright 2024 Leonardo Bertinelli, Davide Biancardi, Raffaele Fulgente, Clelia Iovine, Nicolas Mariniello, Fabio Pizzano -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import Foundation - - -public struct HorizonCoordinates: Equatable, Hashable, Codable, Sendable { - public var altitude: Angle - public var azimuth: Angle - - /// Converts horizon coordinates to equatorial coordinates - /// - Returns: Equatorial coordinates of the instance. - public func horizon2Equatorial(latitude: Angle) -> EquatorialCoordinates { - let tZeroHorizonToEquatorial = sin(altitude.radians) * sin(latitude.radians) + cos(altitude.radians) * cos(latitude.radians) * cos(azimuth.radians) - let declination: Angle = .init(radians:asin(tZeroHorizonToEquatorial)) - let tOneHorizonToEquatorial = sin(altitude.radians) - sin(latitude.radians) * sin(declination.radians) - let tTwoHorizonToEquatorial = tOneHorizonToEquatorial / (cos(latitude.radians) * cos(declination.radians)) - var hourAngle: Angle = .init(radians: acos(tTwoHorizonToEquatorial)) - - if sin(altitude.radians) >= 0 { - hourAngle.degrees = 360 - hourAngle.degrees - } - - return .init(declination: declination, hourAngle: hourAngle) - } -} diff --git a/Sources/SunKit/EclipticCoordinates.swift b/Sources/SunKit/Sun Coordinates/EclipticCoordinates.swift similarity index 100% rename from Sources/SunKit/EclipticCoordinates.swift rename to Sources/SunKit/Sun Coordinates/EclipticCoordinates.swift From 532080abb59a9f0d12bf084f5d04de7e07f6f4c7 Mon Sep 17 00:00:00 2001 From: Ignas Sireikis Date: Fri, 19 Sep 2025 13:51:28 -0500 Subject: [PATCH 43/91] Tracked missing files. --- .../EquatorialCoordinates.swift | 157 ++++++++++++++++++ .../Sun Coordinates/HorizonCoordinates.swift | 41 +++++ 2 files changed, 198 insertions(+) create mode 100644 Sources/SunKit/Sun Coordinates/EquatorialCoordinates.swift create mode 100644 Sources/SunKit/Sun Coordinates/HorizonCoordinates.swift diff --git a/Sources/SunKit/Sun Coordinates/EquatorialCoordinates.swift b/Sources/SunKit/Sun Coordinates/EquatorialCoordinates.swift new file mode 100644 index 0000000..4e473ec --- /dev/null +++ b/Sources/SunKit/Sun Coordinates/EquatorialCoordinates.swift @@ -0,0 +1,157 @@ +// +// EquatorialCoordinates.swift +// +// +// Copyright 2024 Leonardo Bertinelli, Davide Biancardi, Raffaele Fulgente, Clelia Iovine, Nicolas Mariniello, Fabio Pizzano +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +import Foundation + + +public struct EquatorialCoordinates: Equatable, Hashable, Codable, Sendable { + public private(set) var rightAscension: Angle? // rightAscension.degrees refers to h format + public private(set) var declination: Angle //delta + + private(set) var hourAngle: Angle? + + init(declination: Angle, rightAscension: Angle, hourAngle: Angle) { + self.declination = declination + self.rightAscension = rightAscension + self.hourAngle = hourAngle + } + + init(declination: Angle, rightAscension: Angle) { + self.declination = declination + self.rightAscension = rightAscension + self.hourAngle = nil + } + + init(declination: Angle, hourAngle: Angle) { + self.declination = declination + self.hourAngle = hourAngle + self.rightAscension = nil + } + + init(declination: Angle) { + self.declination = declination + } + + /// To set right ascension we need LST and right ascension. If right ascension is nill we can't set it. + /// - Parameter lstDecimal: Local Sideral Time in decimal + /// - Returns: The value of hour angle just been set. Nil if right ascension is also nil + public mutating func setHourAngleFrom(lstDecimal: Double) -> Angle? { + guard let rightAscension = self.rightAscension else { + return nil + } + + var hourAngleDecimal = lstDecimal - rightAscension.degrees + if hourAngleDecimal < 0 { + hourAngleDecimal += 24 + } + self.hourAngle = .init(degrees: hourAngleDecimal * 15) + + return self.hourAngle + } + + /// To set right ascension we need LST and hour angle. If hour angle is nill we can't set it. + /// - Parameter lstDecimal: Local Sideral Time in decimal + /// - Returns: The value of right ascension just been set. Nil if hour angle is also nil + public mutating func setRightAscensionFrom(lstDecimal: Double) -> Angle? { + guard let hourAngle = self.hourAngle else { + return nil + } + + let hourAngleDecimal = hourAngle.degrees / 15 + self.rightAscension = .init(degrees: lstDecimal - hourAngleDecimal) + + return self.rightAscension + } + + /// Converts Equatorial coordinates to Horizon coordinates. + /// + /// Since horizon coordinates depend on the position, we need also latitude parameter to create an EquatorialCoordinates instance. + /// + /// - Parameters: + /// - lstDecimal: Local Sidereal Time in decimal format. + /// - latitude: Latitude of the observer + /// - Returns: Horizon coordinates for the given latitude and LST. Nil if hour angle cannot be computed due to the miss right ascnsion information + public mutating func equatorial2Horizon( + lstDecimal: Double, + latitude: Angle + ) -> HorizonCoordinates? { + guard let _ = setHourAngleFrom(lstDecimal: lstDecimal) else { + return nil + } + + return calculateHorizonCoordinates(from: latitude) + } + + /// Converts Equatorial coordinates to Horizon coordinates. + /// + /// Since horizon coordinates depend on the position, we need also latitude parameter to create an EquatorialCoordinates instance. + /// + /// - Parameters: + /// - latitude: Latitude of the observer + /// - Returns: Horizon coordinates for the given latitude. Nil if hour angle is not defined. + public func equatorial2Horizon(latitude: Angle) -> HorizonCoordinates? { + guard let _ = self.hourAngle else { + return nil + } + + return calculateHorizonCoordinates(from: latitude) + } + + private func calculateHorizonCoordinates(from latitude: Angle) -> HorizonCoordinates { + let tZeroEquatorialToHorizon = sin(declination.radians) * sin(latitude.radians) + cos(declination.radians) * cos(latitude.radians) * cos(hourAngle!.radians) + let altitude: Angle = .init(radians: asin(tZeroEquatorialToHorizon)) + let tOneEquatorialToHorizon = sin(declination.radians) - sin(latitude.radians) * sin(altitude.radians) + let tTwoEquatorialToHorizon = tOneEquatorialToHorizon / (cos(latitude.radians) * cos(altitude.radians)) + var azimuth: Angle = .init(radians: acos(tTwoEquatorialToHorizon)) + + if sin(hourAngle!.radians) >= 0 { + azimuth.degrees = 360 - azimuth.degrees + } + + return .init(altitude: altitude, azimuth: azimuth) + } + + public func equatorial2Ecliptic() -> EclipticCoordinates? { + guard var rightAscension = rightAscension else { + return nil + } + + rightAscension.degrees = rightAscension.degrees * 15 // from h format to degrees + + let tEquatorialToEcliptic: Angle = .init(radians: sin(declination.radians) * cos(EclipticCoordinates.obliquityOfTheEcliptic.radians) - cos(declination.radians) * sin(EclipticCoordinates.obliquityOfTheEcliptic.radians) * sin(rightAscension.radians)) + let eclipticLatitude: Angle = .init(radians: asin(tEquatorialToEcliptic.radians)) + let yEquatorialToEcliptic = sin(rightAscension.radians) * cos(EclipticCoordinates.obliquityOfTheEcliptic.radians) + tan(declination.radians) * sin(EclipticCoordinates.obliquityOfTheEcliptic.radians) + let xEquatorialToEcliptic = cos(rightAscension.radians) + var r: Angle = .init(radians: atan(yEquatorialToEcliptic / xEquatorialToEcliptic)) + + switch (yEquatorialToEcliptic >= 0, xEquatorialToEcliptic >= 0) { + case (true, true): + break + case (true,false): + r.degrees += 180 + case(false,true): + r.degrees += 360 + case(false,false): + r.degrees += 180 + } + + let eclipticLongitude: Angle = .init(degrees: r.degrees) + + return .init(eclipticLatitude: eclipticLatitude, eclipticLongitude: eclipticLongitude) + } +} diff --git a/Sources/SunKit/Sun Coordinates/HorizonCoordinates.swift b/Sources/SunKit/Sun Coordinates/HorizonCoordinates.swift new file mode 100644 index 0000000..9baf52a --- /dev/null +++ b/Sources/SunKit/Sun Coordinates/HorizonCoordinates.swift @@ -0,0 +1,41 @@ +// +// HoorizonCoordinates.swift +// +// +// Copyright 2024 Leonardo Bertinelli, Davide Biancardi, Raffaele Fulgente, Clelia Iovine, Nicolas Mariniello, Fabio Pizzano +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +import Foundation + + +public struct HorizonCoordinates: Equatable, Hashable, Codable, Sendable { + public var altitude: Angle + public var azimuth: Angle + + /// Converts horizon coordinates to equatorial coordinates + /// - Returns: Equatorial coordinates of the instance. + public func horizon2Equatorial(latitude: Angle) -> EquatorialCoordinates { + let tZeroHorizonToEquatorial = sin(altitude.radians) * sin(latitude.radians) + cos(altitude.radians) * cos(latitude.radians) * cos(azimuth.radians) + let declination: Angle = .init(radians:asin(tZeroHorizonToEquatorial)) + let tOneHorizonToEquatorial = sin(altitude.radians) - sin(latitude.radians) * sin(declination.radians) + let tTwoHorizonToEquatorial = tOneHorizonToEquatorial / (cos(latitude.radians) * cos(declination.radians)) + var hourAngle: Angle = .init(radians: acos(tTwoHorizonToEquatorial)) + + if sin(altitude.radians) >= 0 { + hourAngle.degrees = 360 - hourAngle.degrees + } + + return .init(declination: declination, hourAngle: hourAngle) + } +} From c09a2e6997dd7a39a83be8898a60380801a81330 Mon Sep 17 00:00:00 2001 From: Ignas Sireikis Date: Fri, 19 Sep 2025 14:08:45 -0500 Subject: [PATCH 44/91] Created SunCoordinates. --- .../Sun Coordinates/SunCoordinates.swift | 50 +++++++++++++++++++ 1 file changed, 50 insertions(+) create mode 100644 Sources/SunKit/Sun Coordinates/SunCoordinates.swift diff --git a/Sources/SunKit/Sun Coordinates/SunCoordinates.swift b/Sources/SunKit/Sun Coordinates/SunCoordinates.swift new file mode 100644 index 0000000..bfbef0d --- /dev/null +++ b/Sources/SunKit/Sun Coordinates/SunCoordinates.swift @@ -0,0 +1,50 @@ +// +// SunCoordinates.swift +// +// +// Copyright 2024 Leonardo Bertinelli, Davide Biancardi, Raffaele Fulgente, Clelia Iovine, Nicolas Mariniello, Fabio Pizzano +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +import Foundation + + +internal final class SunCoordinates: Sendable { + let eclipticCoordinates: EclipticCoordinates + let equatorialCoordinates: EquatorialCoordinates + let horizonCoordinates: HorizonCoordinates + + internal var azimuth: Angle { + horizonCoordinates.azimuth + } + + internal var altitude: Angle { + horizonCoordinates.altitude + } + + init( + eclipticCoordinates: EclipticCoordinates, + equatorialCoordinates: EquatorialCoordinates, + horizonCoordinates: HorizonCoordinates + ) { + self.eclipticCoordinates = eclipticCoordinates + self.equatorialCoordinates = equatorialCoordinates + self.horizonCoordinates = horizonCoordinates + } + + init() { + self.eclipticCoordinates = EclipticCoordinates(eclipticLatitude: .zero, eclipticLongitude: .zero) + self.equatorialCoordinates = EquatorialCoordinates(declination: .zero) + self.horizonCoordinates = HorizonCoordinates(altitude: .zero, azimuth: .zero) + } +} From d330fc351d0c02c9991e21f806fc800ed0bffbab Mon Sep 17 00:00:00 2001 From: Ignas Sireikis Date: Fri, 19 Sep 2025 14:10:35 -0500 Subject: [PATCH 45/91] Created private backing variable for sun coordinate related properties. --- Sources/SunKit/Sun/Sun.swift | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/Sources/SunKit/Sun/Sun.swift b/Sources/SunKit/Sun/Sun.swift index af38766..a29d204 100644 --- a/Sources/SunKit/Sun/Sun.swift +++ b/Sources/SunKit/Sun/Sun.swift @@ -87,12 +87,13 @@ public struct Sun: Identifiable, Sendable { public private(set) var solarNoonAzimuth: Double = 0 public private(set) var sunsetAzimuth: Double = 0 - // Sun azimuth for (Location,Date) in Self + /// The object backing the public interface for Sun coordinates. + private let sunCoordinates: SunCoordinates = .init() + public var azimuth: Angle { sunHorizonCoordinates.azimuth } - // Sun altitude for (Location,Date) in Self public var altitude: Angle { sunHorizonCoordinates.altitude } From 167399ee87feb9f0cde6c74fa3b18324288f2bd6 Mon Sep 17 00:00:00 2001 From: Ignas Sireikis Date: Fri, 19 Sep 2025 14:29:45 -0500 Subject: [PATCH 46/91] Extracted function calculateSunEclipticLongitude. --- Sources/SunKit/Sun/Sun.swift | 48 ++++++++++++++---------------------- 1 file changed, 19 insertions(+), 29 deletions(-) diff --git a/Sources/SunKit/Sun/Sun.swift b/Sources/SunKit/Sun/Sun.swift index a29d204..d65123d 100644 --- a/Sources/SunKit/Sun/Sun.swift +++ b/Sources/SunKit/Sun/Sun.swift @@ -418,10 +418,26 @@ public struct Sun: Identifiable, Sendable { let gstHMS = uT2GST(self.date) let lstHMS = gST2LST(gstHMS, longitude: longitude) let lstDecimal = lstHMS.hMS2Decimal() + + let sunEclipticLongitude: Angle = calculateSunEclipticLongitude(using: self.date) + + sunEclipticCoordinates = calculateSunEclipticCoordinates(using: sunEclipticLongitude) + // Ecliptic to Equatorial + sunEquatorialCoordinates = calculateSunEquatorialCoordinates(using: sunEclipticCoordinates) + // Equatorial to Horizon + sunHorizonCoordinates = calculateSunHorizonCoordinates(using: lstDecimal) + + // TODO: Move behavior into EquatorialCoordinates, refactor to Object + func calculateSunHorizonCoordinates(using lstDecimal: Double) -> HorizonCoordinates { + sunEquatorialCoordinates.equatorial2Horizon(lstDecimal: lstDecimal, latitude: latitude) ?? .init(altitude: .zero, azimuth: .zero) + } + } + + private func calculateSunEclipticLongitude(using date: Date) -> Angle { // Julian number for standard epoch 2000 let jdEpoch = 2451545.00 // Compute the Julian day number for the desired date using the Greenwich date and TT - let jdTT = jdFromDate(date: self.date) + let jdTT = jdFromDate(date: date) // Compute the total number of elapsed days, including fractional days, since the standard epoch (i.e., JD − JDe) let elapsedDaysSinceStandardEpoch: Double = jdTT - jdEpoch // Use the algorithm from section 6.2.3 to calculate the Sun’s ecliptic longitude and mean anomaly for the given UT date and time. @@ -438,16 +454,7 @@ public struct Sun: Identifiable, Sendable { sunEclipticLongitude.degrees -= 360 } - sunEclipticCoordinates = calculateSunEclipticCoordinates(using: sunEclipticLongitude) - // Ecliptic to Equatorial - sunEquatorialCoordinates = calculateSunEquatorialCoordinates(using: sunEclipticCoordinates) - // Equatorial to Horizon - sunHorizonCoordinates = calculateSunHorizonCoordinates(using: lstDecimal) - - // TODO: Move behavior into EquatorialCoordinates, refactor to Object - func calculateSunHorizonCoordinates(using lstDecimal: Double) -> HorizonCoordinates { - sunEquatorialCoordinates.equatorial2Horizon(lstDecimal: lstDecimal, latitude: latitude) ?? .init(altitude: .zero, azimuth: .zero) - } + return sunEclipticLongitude } // TODO: Move behavior into EclipticCoordinates, refactor to Object @@ -665,25 +672,8 @@ public struct Sun: Identifiable, Sendable { let gstHMS = uT2GST(date) let lstHMS = gST2LST(gstHMS, longitude: longitude) let lstDecimal = lstHMS.hMS2Decimal() - // Julian number for standard epoch 2000 - let jdEpoch = 2451545.00 - // Compute the Julian day number for the desired date using the Greenwich date and TT - let jdTT = jdFromDate(date: date) - // Compute the total number of elapsed days, including fractional days, since the standard epoch (i.e., JD − JDe) - let elapsedDaysSinceStandardEpoch: Double = jdTT - jdEpoch - // Use the algorithm from section 6.2.3 to calculate the Sun’s ecliptic longitude and mean anomaly for the given UT date and time. - let sunMeanAnomaly = getSunMeanAnomaly(from: elapsedDaysSinceStandardEpoch) - // Use Equation 6.2.4 to aproximate the Equation of the center - let equationOfCenter = 360 / Double.pi * sin(sunMeanAnomaly.radians) * 0.016708 - // Add EoC to sun mean anomaly to get the sun true anomaly - var sunTrueAnomaly = sunMeanAnomaly.degrees + equationOfCenter - // Add or subtract multiples of 360° to adjust sun true anomaly to the range of 0° to 360° - sunTrueAnomaly = extendedMod(sunTrueAnomaly, 360) - var sunEclipticLongitude: Angle = .init(degrees: sunTrueAnomaly + sunEclipticLongitudePerigee.degrees) - if sunEclipticLongitude.degrees > 360 { - sunEclipticLongitude.degrees -= 360 - } + let sunEclipticLongitude: Angle = calculateSunEclipticLongitude(using: date) let sunEclipticCoordinates: EclipticCoordinates = calculateSunEclipticCoordinates(using: sunEclipticLongitude) // Ecliptic to Equatorial From 2a5222a178e12714df417fe32544d18fc46af926 Mon Sep 17 00:00:00 2001 From: Ignas Sireikis Date: Fri, 19 Sep 2025 14:35:17 -0500 Subject: [PATCH 47/91] Extracted calculateLSTDecimal function. --- Sources/SunKit/Sun/Sun.swift | 21 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/Sources/SunKit/Sun/Sun.swift b/Sources/SunKit/Sun/Sun.swift index d65123d..32c3afb 100644 --- a/Sources/SunKit/Sun/Sun.swift +++ b/Sources/SunKit/Sun/Sun.swift @@ -414,11 +414,7 @@ public struct Sun: Identifiable, Sendable { /// Updates Horizon coordinates, Ecliptic coordinates and Equatorial coordinates of the Sun private mutating func updateSunCoordinates() { - // Convert LCT to UT, GST, and LST times and adjust the date if needed - let gstHMS = uT2GST(self.date) - let lstHMS = gST2LST(gstHMS, longitude: longitude) - let lstDecimal = lstHMS.hMS2Decimal() - + let lstDecimal = calculateLSTDecimal(using: self.date) let sunEclipticLongitude: Angle = calculateSunEclipticLongitude(using: self.date) sunEclipticCoordinates = calculateSunEclipticCoordinates(using: sunEclipticLongitude) @@ -433,6 +429,15 @@ public struct Sun: Identifiable, Sendable { } } + private func calculateLSTDecimal(using date: Date) -> Double { + // Convert LCT to UT, GST, and LST times and adjust the date if needed + let gstHMS = uT2GST(self.date) + let lstHMS = gST2LST(gstHMS, longitude: longitude) + let lstDecimal = lstHMS.hMS2Decimal() + + return lstDecimal + } + private func calculateSunEclipticLongitude(using date: Date) -> Angle { // Julian number for standard epoch 2000 let jdEpoch = 2451545.00 @@ -668,11 +673,7 @@ public struct Sun: Identifiable, Sendable { // TODO: Extract functions to collapse into updateSunCoordinates public func getSunHorizonCoordinatesFrom(date: Date) -> HorizonCoordinates { - // Convert LCT to UT, GST, and LST times and adjust the date if needed - let gstHMS = uT2GST(date) - let lstHMS = gST2LST(gstHMS, longitude: longitude) - let lstDecimal = lstHMS.hMS2Decimal() - + let lstDecimal = calculateLSTDecimal(using: date) let sunEclipticLongitude: Angle = calculateSunEclipticLongitude(using: date) let sunEclipticCoordinates: EclipticCoordinates = calculateSunEclipticCoordinates(using: sunEclipticLongitude) From 3eac14cfbcb9bd88e2b5ed7c097615ba17a71052 Mon Sep 17 00:00:00 2001 From: Ignas Sireikis Date: Fri, 19 Sep 2025 14:36:25 -0500 Subject: [PATCH 48/91] Moved function closer to use. --- Sources/SunKit/Sun/Sun.swift | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/Sources/SunKit/Sun/Sun.swift b/Sources/SunKit/Sun/Sun.swift index 32c3afb..8905b5d 100644 --- a/Sources/SunKit/Sun/Sun.swift +++ b/Sources/SunKit/Sun/Sun.swift @@ -462,6 +462,13 @@ public struct Sun: Identifiable, Sendable { return sunEclipticLongitude } + private func getSunMeanAnomaly(from elapsedDaysSinceStandardEpoch: Double) -> Angle { + var sunMeanAnomaly: Angle = .init(degrees:(((360.0 * elapsedDaysSinceStandardEpoch) / 365.242191) + sunEclipticLongitudeAtTheEpoch.degrees - sunEclipticLongitudePerigee.degrees)) + sunMeanAnomaly = .init(degrees: extendedMod(sunMeanAnomaly.degrees, 360)) + + return sunMeanAnomaly + } + // TODO: Move behavior into EclipticCoordinates, refactor to Object private func calculateSunEclipticCoordinates(using sunEclipticLongitude: Angle) -> EclipticCoordinates { .init(eclipticLatitude: .zero, eclipticLongitude: sunEclipticLongitude) @@ -471,13 +478,6 @@ public struct Sun: Identifiable, Sendable { private func calculateSunEquatorialCoordinates(using sunEclipticCoordinates: EclipticCoordinates) -> EquatorialCoordinates { sunEclipticCoordinates.ecliptic2Equatorial() } - - private func getSunMeanAnomaly(from elapsedDaysSinceStandardEpoch: Double) -> Angle { - var sunMeanAnomaly: Angle = .init(degrees:(((360.0 * elapsedDaysSinceStandardEpoch) / 365.242191) + sunEclipticLongitudeAtTheEpoch.degrees - sunEclipticLongitudePerigee.degrees)) - sunMeanAnomaly = .init(degrees: extendedMod(sunMeanAnomaly.degrees, 360)) - - return sunMeanAnomaly - } private func getSunEclipticLongitude(from sunMeanAnomaly: Angle) -> Angle { let equationOfCenter = 360 / Double.pi * sin(sunMeanAnomaly.radians) * 0.016708 From 65987542048d54e4ae8aea0424d57d412c1fc9ae Mon Sep 17 00:00:00 2001 From: Ignas Sireikis Date: Fri, 19 Sep 2025 14:36:44 -0500 Subject: [PATCH 49/91] Deleted unused function. --- Sources/SunKit/Sun/Sun.swift | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/Sources/SunKit/Sun/Sun.swift b/Sources/SunKit/Sun/Sun.swift index 8905b5d..1a5680c 100644 --- a/Sources/SunKit/Sun/Sun.swift +++ b/Sources/SunKit/Sun/Sun.swift @@ -479,18 +479,6 @@ public struct Sun: Identifiable, Sendable { sunEclipticCoordinates.ecliptic2Equatorial() } - private func getSunEclipticLongitude(from sunMeanAnomaly: Angle) -> Angle { - let equationOfCenter = 360 / Double.pi * sin(sunMeanAnomaly.radians) * 0.016708 - let trueAnomaly = sunMeanAnomaly.degrees + equationOfCenter - var eclipticLatitude: Angle = .init(degrees: trueAnomaly + sunEclipticLongitudePerigee.degrees) - - if eclipticLatitude.degrees > 360 { - eclipticLatitude.degrees -= 360 - } - - return eclipticLatitude - } - // MARK: - Get Day Events /// Astronomical Dawn is when the Sun reaches -18 degrees of elevation. From 018e588f81142a838ce15f023120d724dee364b3 Mon Sep 17 00:00:00 2001 From: Ignas Sireikis Date: Fri, 19 Sep 2025 14:40:34 -0500 Subject: [PATCH 50/91] Added section marks. --- Sources/SunKit/Sun/Sun.swift | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Sources/SunKit/Sun/Sun.swift b/Sources/SunKit/Sun/Sun.swift index 1a5680c..d7f7047 100644 --- a/Sources/SunKit/Sun/Sun.swift +++ b/Sources/SunKit/Sun/Sun.swift @@ -659,6 +659,8 @@ public struct Sun: Identifiable, Sendable { return newDate } + // MARK: - Get Horizon Coordinates + // TODO: Extract functions to collapse into updateSunCoordinates public func getSunHorizonCoordinatesFrom(date: Date) -> HorizonCoordinates { let lstDecimal = calculateLSTDecimal(using: date) @@ -678,6 +680,8 @@ public struct Sun: Identifiable, Sendable { } } + // MARK: - Get Month Events + private func getMarchEquinox() -> Date? { let year = Double(calendar.component(.year, from: self.date)) let t: Double = year / 1000 From 7d797d9d24312c275ca3cd7ab90dd6e42b850161 Mon Sep 17 00:00:00 2001 From: Ignas Sireikis Date: Fri, 19 Sep 2025 16:13:21 -0500 Subject: [PATCH 51/91] Extracted variables from month events. --- Sources/SunKit/Sun/Sun.swift | 30 ++++++++++++++++++++++++++---- 1 file changed, 26 insertions(+), 4 deletions(-) diff --git a/Sources/SunKit/Sun/Sun.swift b/Sources/SunKit/Sun/Sun.swift index d7f7047..021927d 100644 --- a/Sources/SunKit/Sun/Sun.swift +++ b/Sources/SunKit/Sun/Sun.swift @@ -682,10 +682,17 @@ public struct Sun: Identifiable, Sendable { // MARK: - Get Month Events + // TODO: Move magic numbers into Enum cases or subclasses + private func getMarchEquinox() -> Date? { let year = Double(calendar.component(.year, from: self.date)) let t: Double = year / 1000 - let julianDayMarchEquinox: Double = 1721139.2855 + 365.2421376 * year + 0.0679190 * pow(t, 2) - 0.0027879 * pow(t, 3) + let marchSolsticeJulianDayConstant = 1721139.2855 + let marchSolsticeMeanYearLengthInDays = 365.2421376 + let quadraticCorrection = 0.0679190 + let cubicCorrection = 0.0027879 + + let julianDayMarchEquinox: Double = marchSolsticeJulianDayConstant + marchSolsticeMeanYearLengthInDays * year + quadraticCorrection * pow(t, 2) - cubicCorrection * pow(t, 3) let marchEquinoxUTC = dateFromJd(jd: julianDayMarchEquinox) return marchEquinoxUTC @@ -694,7 +701,12 @@ public struct Sun: Identifiable, Sendable { private func getJuneSolstice() -> Date? { let year = Double(calendar.component(.year, from: self.date)) let t: Double = year / 1000 - let julianDayJuneSolstice: Double = 1721233.2486 + 365.2417284 * year - 0.0530180 * pow(t, 2) + 0.0093320 * pow(t, 3) + let juneSolsticeJulianDayConstant = 1721233.2486 + let juneSolsticeMeanYearLengthInDays = 365.2417284 + let quadraticCorrection = 0.0530180 + let cubicCorrection = 0.0093320 + + let julianDayJuneSolstice: Double = juneSolsticeJulianDayConstant + juneSolsticeMeanYearLengthInDays * year - quadraticCorrection * pow(t, 2) + cubicCorrection * pow(t, 3) let juneSolsticeUTC = dateFromJd(jd: julianDayJuneSolstice) return juneSolsticeUTC @@ -703,7 +715,12 @@ public struct Sun: Identifiable, Sendable { private func getSeptemberEquinox() -> Date? { let year = Double(calendar.component(.year, from: self.date)) let t: Double = year / 1000 - let julianDaySeptemberEquinox: Double = 1721325.6978 + 365.2425055 * year - 0.126689 * pow(t, 2) + 0.0019401 * pow(t, 3) + let septemberSolsticeJulianDayConstant = 1721325.6978 + let septemberSolsticeMeanYearLengthInDays = 365.2425055 + let quadraticCorrection = 0.126689 + let cubicCorrection = 0.0019401 + + let julianDaySeptemberEquinox: Double = septemberSolsticeJulianDayConstant + septemberSolsticeMeanYearLengthInDays * year - quadraticCorrection * pow(t, 2) + cubicCorrection * pow(t, 3) let septemberEquinoxUTC = dateFromJd(jd: julianDaySeptemberEquinox) return septemberEquinoxUTC @@ -712,7 +729,12 @@ public struct Sun: Identifiable, Sendable { private func getDecemberSolstice() -> Date? { let year = Double(calendar.component(.year, from: self.date)) let t: Double = year / 1000 - let julianDayDecemberSolstice: Double = 1721414.3920 + 365.2428898 * year - 0.0109650 * pow(t, 2) - 0.0084885 * pow(t, 3) + let decemberSolsticeJulianDayConstant = 1721414.3920 + let decemberSolsticeMeanYearLengthInDays = 365.2428898 + let quadraticCorrection = 0.0109650 + let cubicCorrection = 0.0084885 + + let julianDayDecemberSolstice: Double = decemberSolsticeJulianDayConstant + decemberSolsticeMeanYearLengthInDays * year - quadraticCorrection * pow(t, 2) - cubicCorrection * pow(t, 3) let decemberSolsticeUTC = dateFromJd(jd: julianDayDecemberSolstice) return decemberSolsticeUTC From 1a5495c90ff4561f89d201f95391960adfd688b1 Mon Sep 17 00:00:00 2001 From: Ignas Sireikis Date: Fri, 19 Sep 2025 16:25:42 -0500 Subject: [PATCH 52/91] Extracted calculateCosineofSolarHourAngle function. --- Sources/SunKit/Sun/Sun.swift | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/Sources/SunKit/Sun/Sun.swift b/Sources/SunKit/Sun/Sun.swift index 021927d..27bec12 100644 --- a/Sources/SunKit/Sun/Sun.swift +++ b/Sources/SunKit/Sun/Sun.swift @@ -519,8 +519,7 @@ public struct Sun: Identifiable, Sendable { /// Sunrise is when the Sun reaches 0 degrees of elevation, aka the horizon, at the start of the day. private func getSunrise() -> Date? { - var haArg = (cos(Angle.degrees(90.833).radians)) / (cos(latitude.radians) * cos(sunEquatorialCoordinates.declination.radians)) - tan(latitude.radians) * tan(sunEquatorialCoordinates.declination.radians) - + var haArg = calculateCosineofSolarHourAngle() haArg = clamp(lower: -1, upper: 1, number: haArg) let ha: Angle = .radians(acos(haArg)) let sunriseUTCMinutes = 720 - 4 * (location.coordinate.longitude + ha.degrees) - equationOfTime @@ -537,6 +536,16 @@ public struct Sun: Identifiable, Sendable { return sunriseDate } + private func calculateCosineofSolarHourAngle() -> Double { + let cosSolarZenithAngle = cos(Angle.degrees(90.833).radians) + let cosLatitude = cos(latitude.radians) + let cosSunDeclination = cos(sunEquatorialCoordinates.declination.radians) + let tanLatitude = tan(latitude.radians) + let tanSunDeclination = tan(sunEquatorialCoordinates.declination.radians) + + return cosSolarZenithAngle / (cosLatitude * cosSunDeclination) - tanLatitude * tanSunDeclination + } + /// Morning Golden Hour ends when Sun reaches 6 degrees of elevation. private func getMorningGoldenHourEnd() -> Date? { guard let morningGoldenHourEnd = getDateFrom(sunEvent: .morningGoldenHourEnd, morning: true) else { @@ -567,7 +576,7 @@ public struct Sun: Identifiable, Sendable { /// Sunset is when the Sun reaches 0 degrees of elevation, aka the horizon, at the end of the day. private func getSunset() -> Date? { - var haArg = (cos(Angle.degrees(90.833).radians)) / (cos(latitude.radians) * cos(sunEquatorialCoordinates.declination.radians)) - tan(latitude.radians) * tan(sunEquatorialCoordinates.declination.radians) + var haArg = calculateCosineofSolarHourAngle() haArg = clamp(lower: -1, upper: 1, number: haArg) let ha: Angle = .radians(-acos(haArg)) From 0c0574c163dbd7a6cffc57e9abb2ac75c5b36a0e Mon Sep 17 00:00:00 2001 From: Ignas Sireikis Date: Fri, 19 Sep 2025 16:29:24 -0500 Subject: [PATCH 53/91] Extracted clampCosineOfSolarHourAngle function. --- Sources/SunKit/Sun/Sun.swift | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/Sources/SunKit/Sun/Sun.swift b/Sources/SunKit/Sun/Sun.swift index 27bec12..eaa8819 100644 --- a/Sources/SunKit/Sun/Sun.swift +++ b/Sources/SunKit/Sun/Sun.swift @@ -519,8 +519,7 @@ public struct Sun: Identifiable, Sendable { /// Sunrise is when the Sun reaches 0 degrees of elevation, aka the horizon, at the start of the day. private func getSunrise() -> Date? { - var haArg = calculateCosineofSolarHourAngle() - haArg = clamp(lower: -1, upper: 1, number: haArg) + let haArg = clampCosineOfSolarHourAngle() let ha: Angle = .radians(acos(haArg)) let sunriseUTCMinutes = 720 - 4 * (location.coordinate.longitude + ha.degrees) - equationOfTime var sunriseSeconds = (Int(sunriseUTCMinutes) * 60) + timeZoneInSeconds @@ -536,8 +535,15 @@ public struct Sun: Identifiable, Sendable { return sunriseDate } - private func calculateCosineofSolarHourAngle() -> Double { - let cosSolarZenithAngle = cos(Angle.degrees(90.833).radians) + private func clampCosineOfSolarHourAngle() -> Double { + let cosSolarHourAngle = calculateCosineOfSolarHourAngle() + + return clamp(lower: -1, upper: 1, number: cosSolarHourAngle) + } + + private func calculateCosineOfSolarHourAngle() -> Double { + let solarZenithAngle = 90.833 + let cosSolarZenithAngle = cos(Angle.degrees(solarZenithAngle).radians) let cosLatitude = cos(latitude.radians) let cosSunDeclination = cos(sunEquatorialCoordinates.declination.radians) let tanLatitude = tan(latitude.radians) @@ -576,9 +582,7 @@ public struct Sun: Identifiable, Sendable { /// Sunset is when the Sun reaches 0 degrees of elevation, aka the horizon, at the end of the day. private func getSunset() -> Date? { - var haArg = calculateCosineofSolarHourAngle() - - haArg = clamp(lower: -1, upper: 1, number: haArg) + let haArg = clampCosineOfSolarHourAngle() let ha: Angle = .radians(-acos(haArg)) let sunsetUTCMinutes = 720 - 4 * (location.coordinate.longitude + ha.degrees) - equationOfTime var sunsetSeconds = (Int(sunsetUTCMinutes) * 60) + timeZoneInSeconds From 36f28e7353e5bd293fa384764c16cf5c1fa6d3d6 Mon Sep 17 00:00:00 2001 From: Ignas Sireikis Date: Fri, 19 Sep 2025 16:32:24 -0500 Subject: [PATCH 54/91] Extracted calculateSunriseSolarHourAngle function. --- Sources/SunKit/Sun/Sun.swift | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/Sources/SunKit/Sun/Sun.swift b/Sources/SunKit/Sun/Sun.swift index eaa8819..5cd5adb 100644 --- a/Sources/SunKit/Sun/Sun.swift +++ b/Sources/SunKit/Sun/Sun.swift @@ -519,9 +519,8 @@ public struct Sun: Identifiable, Sendable { /// Sunrise is when the Sun reaches 0 degrees of elevation, aka the horizon, at the start of the day. private func getSunrise() -> Date? { - let haArg = clampCosineOfSolarHourAngle() - let ha: Angle = .radians(acos(haArg)) - let sunriseUTCMinutes = 720 - 4 * (location.coordinate.longitude + ha.degrees) - equationOfTime + let solarHourAngle: Angle = calculateSunriseSolarHourAngle() + let sunriseUTCMinutes = 720 - 4 * (location.coordinate.longitude + solarHourAngle.degrees) - equationOfTime var sunriseSeconds = (Int(sunriseUTCMinutes) * 60) + timeZoneInSeconds let startOfDay = calendar.startOfDay(for: date) @@ -535,6 +534,13 @@ public struct Sun: Identifiable, Sendable { return sunriseDate } + private func calculateSunriseSolarHourAngle() -> Angle { + let cosSolarHourAngle = clampCosineOfSolarHourAngle() + let solarHourAngle: Angle = .radians(acos(cosSolarHourAngle)) + + return solarHourAngle + } + private func clampCosineOfSolarHourAngle() -> Double { let cosSolarHourAngle = calculateCosineOfSolarHourAngle() From bbcb7411940464977888ca6664b26a80fe611cc3 Mon Sep 17 00:00:00 2001 From: Ignas Sireikis Date: Fri, 19 Sep 2025 16:35:19 -0500 Subject: [PATCH 55/91] Extracted calculateSunsetSolarHourAngle function. --- Sources/SunKit/Sun/Sun.swift | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/Sources/SunKit/Sun/Sun.swift b/Sources/SunKit/Sun/Sun.swift index 5cd5adb..4bb90b8 100644 --- a/Sources/SunKit/Sun/Sun.swift +++ b/Sources/SunKit/Sun/Sun.swift @@ -519,7 +519,7 @@ public struct Sun: Identifiable, Sendable { /// Sunrise is when the Sun reaches 0 degrees of elevation, aka the horizon, at the start of the day. private func getSunrise() -> Date? { - let solarHourAngle: Angle = calculateSunriseSolarHourAngle() + let solarHourAngle = calculateSunriseSolarHourAngle() let sunriseUTCMinutes = 720 - 4 * (location.coordinate.longitude + solarHourAngle.degrees) - equationOfTime var sunriseSeconds = (Int(sunriseUTCMinutes) * 60) + timeZoneInSeconds let startOfDay = calendar.startOfDay(for: date) @@ -588,9 +588,8 @@ public struct Sun: Identifiable, Sendable { /// Sunset is when the Sun reaches 0 degrees of elevation, aka the horizon, at the end of the day. private func getSunset() -> Date? { - let haArg = clampCosineOfSolarHourAngle() - let ha: Angle = .radians(-acos(haArg)) - let sunsetUTCMinutes = 720 - 4 * (location.coordinate.longitude + ha.degrees) - equationOfTime + let solarHourAngle = calculateSunsetSolarHourAngle() + let sunsetUTCMinutes = 720 - 4 * (location.coordinate.longitude + solarHourAngle.degrees) - equationOfTime var sunsetSeconds = (Int(sunsetUTCMinutes) * 60) + timeZoneInSeconds let startOfDay = calendar.startOfDay(for: date) @@ -604,6 +603,13 @@ public struct Sun: Identifiable, Sendable { return sunsetDate } + private func calculateSunsetSolarHourAngle() -> Angle { + let cosSolarHourAngle = clampCosineOfSolarHourAngle() + let solarHourAngle: Angle = .radians(-acos(cosSolarHourAngle)) + + return solarHourAngle + } + /// Evening Golden Hour ends when the sun reaches -4 degrees of elevation. private func getEveningGoldenHourEnd() -> Date? { guard let goldenHourFinish = getDateFrom(sunEvent: .eveningGoldenHourEnd) else { From b10edaf7bdde5341b8aaf56d3f1365830daac647 Mon Sep 17 00:00:00 2001 From: Ignas Sireikis Date: Fri, 19 Sep 2025 16:37:53 -0500 Subject: [PATCH 56/91] Slide variable closer to site of use. --- Sources/SunKit/Sun/Sun.swift | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Sources/SunKit/Sun/Sun.swift b/Sources/SunKit/Sun/Sun.swift index 4bb90b8..5687fdd 100644 --- a/Sources/SunKit/Sun/Sun.swift +++ b/Sources/SunKit/Sun/Sun.swift @@ -522,13 +522,13 @@ public struct Sun: Identifiable, Sendable { let solarHourAngle = calculateSunriseSolarHourAngle() let sunriseUTCMinutes = 720 - 4 * (location.coordinate.longitude + solarHourAngle.degrees) - equationOfTime var sunriseSeconds = (Int(sunriseUTCMinutes) * 60) + timeZoneInSeconds - let startOfDay = calendar.startOfDay(for: date) if sunriseSeconds < Int(SECONDS_IN_ONE_HOUR) { sunriseSeconds = 0 } let hoursMinutesSeconds: (Int, Int, Int) = secondsToHoursMinutesSeconds(Int(sunriseSeconds)) + let startOfDay = calendar.startOfDay(for: date) let sunriseDate = calendar.date(bySettingHour: hoursMinutesSeconds.0, minute: hoursMinutesSeconds.1, second: hoursMinutesSeconds.2, of: startOfDay) return sunriseDate @@ -591,13 +591,13 @@ public struct Sun: Identifiable, Sendable { let solarHourAngle = calculateSunsetSolarHourAngle() let sunsetUTCMinutes = 720 - 4 * (location.coordinate.longitude + solarHourAngle.degrees) - equationOfTime var sunsetSeconds = (Int(sunsetUTCMinutes) * 60) + timeZoneInSeconds - let startOfDay = calendar.startOfDay(for: date) if sunsetSeconds > SECONDS_IN_ONE_DAY { sunsetSeconds = SECONDS_IN_ONE_DAY } let hoursMinutesSeconds: (Int, Int, Int) = secondsToHoursMinutesSeconds(Int(sunsetSeconds)) + let startOfDay = calendar.startOfDay(for: date) let sunsetDate = calendar.date(bySettingHour: hoursMinutesSeconds.0, minute: hoursMinutesSeconds.1, second: hoursMinutesSeconds.2, of: startOfDay) return sunsetDate From 7f60f959a1f4aab47becf984275bc508afbf2bae Mon Sep 17 00:00:00 2001 From: Ignas Sireikis Date: Fri, 19 Sep 2025 16:41:29 -0500 Subject: [PATCH 57/91] Extracted calculateSeconds function. --- Sources/SunKit/Sun/Sun.swift | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/Sources/SunKit/Sun/Sun.swift b/Sources/SunKit/Sun/Sun.swift index 5687fdd..f69ec87 100644 --- a/Sources/SunKit/Sun/Sun.swift +++ b/Sources/SunKit/Sun/Sun.swift @@ -521,7 +521,7 @@ public struct Sun: Identifiable, Sendable { private func getSunrise() -> Date? { let solarHourAngle = calculateSunriseSolarHourAngle() let sunriseUTCMinutes = 720 - 4 * (location.coordinate.longitude + solarHourAngle.degrees) - equationOfTime - var sunriseSeconds = (Int(sunriseUTCMinutes) * 60) + timeZoneInSeconds + var sunriseSeconds = calculateSeconds(using: sunriseUTCMinutes) if sunriseSeconds < Int(SECONDS_IN_ONE_HOUR) { sunriseSeconds = 0 @@ -534,6 +534,12 @@ public struct Sun: Identifiable, Sendable { return sunriseDate } + private func calculateSeconds(using UTCMinutes: Double) -> Int { + let UTCSeconds = Int(UTCMinutes) * 60 + + return UTCSeconds + timeZoneInSeconds + } + private func calculateSunriseSolarHourAngle() -> Angle { let cosSolarHourAngle = clampCosineOfSolarHourAngle() let solarHourAngle: Angle = .radians(acos(cosSolarHourAngle)) @@ -590,7 +596,7 @@ public struct Sun: Identifiable, Sendable { private func getSunset() -> Date? { let solarHourAngle = calculateSunsetSolarHourAngle() let sunsetUTCMinutes = 720 - 4 * (location.coordinate.longitude + solarHourAngle.degrees) - equationOfTime - var sunsetSeconds = (Int(sunsetUTCMinutes) * 60) + timeZoneInSeconds + var sunsetSeconds = calculateSeconds(using: sunsetUTCMinutes) if sunsetSeconds > SECONDS_IN_ONE_DAY { sunsetSeconds = SECONDS_IN_ONE_DAY From ff95652657d7fb01be6ec5cb4f59331cd6fde7cb Mon Sep 17 00:00:00 2001 From: Ignas Sireikis Date: Fri, 19 Sep 2025 16:44:25 -0500 Subject: [PATCH 58/91] Extracted calculateUTCMinutes function. --- Sources/SunKit/Sun/Sun.swift | 21 +++++++++++++-------- 1 file changed, 13 insertions(+), 8 deletions(-) diff --git a/Sources/SunKit/Sun/Sun.swift b/Sources/SunKit/Sun/Sun.swift index f69ec87..31c4f71 100644 --- a/Sources/SunKit/Sun/Sun.swift +++ b/Sources/SunKit/Sun/Sun.swift @@ -520,7 +520,7 @@ public struct Sun: Identifiable, Sendable { /// Sunrise is when the Sun reaches 0 degrees of elevation, aka the horizon, at the start of the day. private func getSunrise() -> Date? { let solarHourAngle = calculateSunriseSolarHourAngle() - let sunriseUTCMinutes = 720 - 4 * (location.coordinate.longitude + solarHourAngle.degrees) - equationOfTime + let sunriseUTCMinutes = calculateUTCMinutes(using: solarHourAngle) var sunriseSeconds = calculateSeconds(using: sunriseUTCMinutes) if sunriseSeconds < Int(SECONDS_IN_ONE_HOUR) { @@ -534,12 +534,6 @@ public struct Sun: Identifiable, Sendable { return sunriseDate } - private func calculateSeconds(using UTCMinutes: Double) -> Int { - let UTCSeconds = Int(UTCMinutes) * 60 - - return UTCSeconds + timeZoneInSeconds - } - private func calculateSunriseSolarHourAngle() -> Angle { let cosSolarHourAngle = clampCosineOfSolarHourAngle() let solarHourAngle: Angle = .radians(acos(cosSolarHourAngle)) @@ -564,6 +558,17 @@ public struct Sun: Identifiable, Sendable { return cosSolarZenithAngle / (cosLatitude * cosSunDeclination) - tanLatitude * tanSunDeclination } + private func calculateUTCMinutes(using solarHourAngle: Angle) -> Double { + let longitude = location.coordinate.longitude + return 720 - 4 * (longitude + solarHourAngle.degrees) - equationOfTime + } + + private func calculateSeconds(using UTCMinutes: Double) -> Int { + let UTCSeconds = Int(UTCMinutes) * 60 + + return UTCSeconds + timeZoneInSeconds + } + /// Morning Golden Hour ends when Sun reaches 6 degrees of elevation. private func getMorningGoldenHourEnd() -> Date? { guard let morningGoldenHourEnd = getDateFrom(sunEvent: .morningGoldenHourEnd, morning: true) else { @@ -595,7 +600,7 @@ public struct Sun: Identifiable, Sendable { /// Sunset is when the Sun reaches 0 degrees of elevation, aka the horizon, at the end of the day. private func getSunset() -> Date? { let solarHourAngle = calculateSunsetSolarHourAngle() - let sunsetUTCMinutes = 720 - 4 * (location.coordinate.longitude + solarHourAngle.degrees) - equationOfTime + let sunsetUTCMinutes = calculateUTCMinutes(using: solarHourAngle) var sunsetSeconds = calculateSeconds(using: sunsetUTCMinutes) if sunsetSeconds > SECONDS_IN_ONE_DAY { From 8699e80224580e5edcb70ff5a634e26c68ef8595 Mon Sep 17 00:00:00 2001 From: Ignas Sireikis Date: Fri, 19 Sep 2025 16:46:08 -0500 Subject: [PATCH 59/91] Extracted calculateSunriseSeconds function. --- Sources/SunKit/Sun/Sun.swift | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/Sources/SunKit/Sun/Sun.swift b/Sources/SunKit/Sun/Sun.swift index 31c4f71..217aa6a 100644 --- a/Sources/SunKit/Sun/Sun.swift +++ b/Sources/SunKit/Sun/Sun.swift @@ -519,9 +519,7 @@ public struct Sun: Identifiable, Sendable { /// Sunrise is when the Sun reaches 0 degrees of elevation, aka the horizon, at the start of the day. private func getSunrise() -> Date? { - let solarHourAngle = calculateSunriseSolarHourAngle() - let sunriseUTCMinutes = calculateUTCMinutes(using: solarHourAngle) - var sunriseSeconds = calculateSeconds(using: sunriseUTCMinutes) + var sunriseSeconds = calculateSunriseSeconds() if sunriseSeconds < Int(SECONDS_IN_ONE_HOUR) { sunriseSeconds = 0 @@ -534,6 +532,14 @@ public struct Sun: Identifiable, Sendable { return sunriseDate } + private func calculateSunriseSeconds() -> Int { + let solarHourAngle = calculateSunriseSolarHourAngle() + let sunriseUTCMinutes = calculateUTCMinutes(using: solarHourAngle) + let sunriseSeconds = calculateSeconds(using: sunriseUTCMinutes) + + return sunriseSeconds + } + private func calculateSunriseSolarHourAngle() -> Angle { let cosSolarHourAngle = clampCosineOfSolarHourAngle() let solarHourAngle: Angle = .radians(acos(cosSolarHourAngle)) From e4cc2bc21ce1f4a270830a5e21008e1b665955ad Mon Sep 17 00:00:00 2001 From: Ignas Sireikis Date: Fri, 19 Sep 2025 16:47:30 -0500 Subject: [PATCH 60/91] Extracted calculateSunsetSeconds function. --- Sources/SunKit/Sun/Sun.swift | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/Sources/SunKit/Sun/Sun.swift b/Sources/SunKit/Sun/Sun.swift index 217aa6a..06ee30c 100644 --- a/Sources/SunKit/Sun/Sun.swift +++ b/Sources/SunKit/Sun/Sun.swift @@ -605,9 +605,7 @@ public struct Sun: Identifiable, Sendable { /// Sunset is when the Sun reaches 0 degrees of elevation, aka the horizon, at the end of the day. private func getSunset() -> Date? { - let solarHourAngle = calculateSunsetSolarHourAngle() - let sunsetUTCMinutes = calculateUTCMinutes(using: solarHourAngle) - var sunsetSeconds = calculateSeconds(using: sunsetUTCMinutes) + var sunsetSeconds = calculateSunsetSeconds() if sunsetSeconds > SECONDS_IN_ONE_DAY { sunsetSeconds = SECONDS_IN_ONE_DAY @@ -620,6 +618,14 @@ public struct Sun: Identifiable, Sendable { return sunsetDate } + private func calculateSunsetSeconds() -> Int { + let solarHourAngle = calculateSunsetSolarHourAngle() + let sunsetUTCMinutes = calculateUTCMinutes(using: solarHourAngle) + let sunsetSeconds = calculateSeconds(using: sunsetUTCMinutes) + + return sunsetSeconds + } + private func calculateSunsetSolarHourAngle() -> Angle { let cosSolarHourAngle = clampCosineOfSolarHourAngle() let solarHourAngle: Angle = .radians(-acos(cosSolarHourAngle)) From cf82f5104c2b3d65a502a19123b123a0b6f2b343 Mon Sep 17 00:00:00 2001 From: Ignas Sireikis Date: Fri, 19 Sep 2025 16:51:50 -0500 Subject: [PATCH 61/91] Extracted calculateDate function. --- Sources/SunKit/Sun/Sun.swift | 23 ++++++++++++++++------- 1 file changed, 16 insertions(+), 7 deletions(-) diff --git a/Sources/SunKit/Sun/Sun.swift b/Sources/SunKit/Sun/Sun.swift index 06ee30c..5f0b0de 100644 --- a/Sources/SunKit/Sun/Sun.swift +++ b/Sources/SunKit/Sun/Sun.swift @@ -524,10 +524,8 @@ public struct Sun: Identifiable, Sendable { if sunriseSeconds < Int(SECONDS_IN_ONE_HOUR) { sunriseSeconds = 0 } - - let hoursMinutesSeconds: (Int, Int, Int) = secondsToHoursMinutesSeconds(Int(sunriseSeconds)) - let startOfDay = calendar.startOfDay(for: date) - let sunriseDate = calendar.date(bySettingHour: hoursMinutesSeconds.0, minute: hoursMinutesSeconds.1, second: hoursMinutesSeconds.2, of: startOfDay) + + let sunriseDate = calculateDate(using: sunriseSeconds) return sunriseDate } @@ -575,6 +573,19 @@ public struct Sun: Identifiable, Sendable { return UTCSeconds + timeZoneInSeconds } + private func calculateDate(using seconds: Int) -> Date? { + let hoursMinutesSeconds: (Int, Int, Int) = secondsToHoursMinutesSeconds(Int(seconds)) + let startOfDay = calendar.startOfDay(for: date) + let date = calendar.date( + bySettingHour: hoursMinutesSeconds.0, + minute: hoursMinutesSeconds.1, + second: hoursMinutesSeconds.2, + of: startOfDay + ) + + return date + } + /// Morning Golden Hour ends when Sun reaches 6 degrees of elevation. private func getMorningGoldenHourEnd() -> Date? { guard let morningGoldenHourEnd = getDateFrom(sunEvent: .morningGoldenHourEnd, morning: true) else { @@ -611,9 +622,7 @@ public struct Sun: Identifiable, Sendable { sunsetSeconds = SECONDS_IN_ONE_DAY } - let hoursMinutesSeconds: (Int, Int, Int) = secondsToHoursMinutesSeconds(Int(sunsetSeconds)) - let startOfDay = calendar.startOfDay(for: date) - let sunsetDate = calendar.date(bySettingHour: hoursMinutesSeconds.0, minute: hoursMinutesSeconds.1, second: hoursMinutesSeconds.2, of: startOfDay) + let sunsetDate = calculateDate(using: sunsetSeconds) return sunsetDate } From 6185b638648bbd7457eae4c236f1f859520d53d7 Mon Sep 17 00:00:00 2001 From: Ignas Sireikis Date: Fri, 19 Sep 2025 16:54:06 -0500 Subject: [PATCH 62/91] Moved additional code into calculate seconds functions. --- Sources/SunKit/Sun/Sun.swift | 27 +++++++++++++-------------- 1 file changed, 13 insertions(+), 14 deletions(-) diff --git a/Sources/SunKit/Sun/Sun.swift b/Sources/SunKit/Sun/Sun.swift index 5f0b0de..da2203e 100644 --- a/Sources/SunKit/Sun/Sun.swift +++ b/Sources/SunKit/Sun/Sun.swift @@ -519,12 +519,7 @@ public struct Sun: Identifiable, Sendable { /// Sunrise is when the Sun reaches 0 degrees of elevation, aka the horizon, at the start of the day. private func getSunrise() -> Date? { - var sunriseSeconds = calculateSunriseSeconds() - - if sunriseSeconds < Int(SECONDS_IN_ONE_HOUR) { - sunriseSeconds = 0 - } - + let sunriseSeconds = calculateSunriseSeconds() let sunriseDate = calculateDate(using: sunriseSeconds) return sunriseDate @@ -533,7 +528,11 @@ public struct Sun: Identifiable, Sendable { private func calculateSunriseSeconds() -> Int { let solarHourAngle = calculateSunriseSolarHourAngle() let sunriseUTCMinutes = calculateUTCMinutes(using: solarHourAngle) - let sunriseSeconds = calculateSeconds(using: sunriseUTCMinutes) + var sunriseSeconds = calculateSeconds(using: sunriseUTCMinutes) + + if sunriseSeconds < Int(SECONDS_IN_ONE_HOUR) { + sunriseSeconds = 0 + } return sunriseSeconds } @@ -564,6 +563,7 @@ public struct Sun: Identifiable, Sendable { private func calculateUTCMinutes(using solarHourAngle: Angle) -> Double { let longitude = location.coordinate.longitude + return 720 - 4 * (longitude + solarHourAngle.degrees) - equationOfTime } @@ -616,12 +616,7 @@ public struct Sun: Identifiable, Sendable { /// Sunset is when the Sun reaches 0 degrees of elevation, aka the horizon, at the end of the day. private func getSunset() -> Date? { - var sunsetSeconds = calculateSunsetSeconds() - - if sunsetSeconds > SECONDS_IN_ONE_DAY { - sunsetSeconds = SECONDS_IN_ONE_DAY - } - + let sunsetSeconds = calculateSunsetSeconds() let sunsetDate = calculateDate(using: sunsetSeconds) return sunsetDate @@ -630,7 +625,11 @@ public struct Sun: Identifiable, Sendable { private func calculateSunsetSeconds() -> Int { let solarHourAngle = calculateSunsetSolarHourAngle() let sunsetUTCMinutes = calculateUTCMinutes(using: solarHourAngle) - let sunsetSeconds = calculateSeconds(using: sunsetUTCMinutes) + var sunsetSeconds = calculateSeconds(using: sunsetUTCMinutes) + + if sunsetSeconds > SECONDS_IN_ONE_DAY { + sunsetSeconds = SECONDS_IN_ONE_DAY + } return sunsetSeconds } From 1ac8cb7718a965b9b14a93fd5c2cd055d39f722c Mon Sep 17 00:00:00 2001 From: Ignas Sireikis Date: Fri, 19 Sep 2025 16:56:08 -0500 Subject: [PATCH 63/91] Added TODO. --- Sources/SunKit/Sun/Sun.swift | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/Sources/SunKit/Sun/Sun.swift b/Sources/SunKit/Sun/Sun.swift index da2203e..b8448d9 100644 --- a/Sources/SunKit/Sun/Sun.swift +++ b/Sources/SunKit/Sun/Sun.swift @@ -97,9 +97,8 @@ public struct Sun: Identifiable, Sendable { public var altitude: Angle { sunHorizonCoordinates.altitude } - + // TODO: Use SunCoordinates as backing object for these Coordinates. private var sunHorizonCoordinates: HorizonCoordinates = .init(altitude: .zero, azimuth: .zero) - public private(set) var sunEquatorialCoordinates: EquatorialCoordinates = .init(declination: .zero) public private(set) var sunEclipticCoordinates: EclipticCoordinates = .init(eclipticLatitude: .zero, eclipticLongitude: .zero) From c28e2ef73efae6f0c66a8ccd9ed54572d0ff153a Mon Sep 17 00:00:00 2001 From: Ignas Sireikis Date: Fri, 19 Sep 2025 17:13:38 -0500 Subject: [PATCH 64/91] Created extension folder. Move date strideable extension into folder. --- Sources/SunKit/Extensions.swift | 22 ---------- .../SunKit/Extensions/Date+Strideable.swift | 44 +++++++++++++++++++ 2 files changed, 44 insertions(+), 22 deletions(-) create mode 100644 Sources/SunKit/Extensions/Date+Strideable.swift diff --git a/Sources/SunKit/Extensions.swift b/Sources/SunKit/Extensions.swift index ed4b710..9813dae 100644 --- a/Sources/SunKit/Extensions.swift +++ b/Sources/SunKit/Extensions.swift @@ -19,28 +19,6 @@ import Foundation -//It consents us too loop between two dates for n as interval time -extension Date: @retroactive Strideable { - public func distance(to other: Date) -> TimeInterval { - return other.timeIntervalSinceReferenceDate - self.timeIntervalSinceReferenceDate - } - - func toString(_ timeZone: TimeZone) -> String { - let df = DateFormatter() - df.timeZone = timeZone - let custom = DateFormatter.dateFormat(fromTemplate: "MMdd HH:mm", - options: 0, - locale: Locale(identifier: "en")) - df.dateFormat = custom - - return df.string(from: self) - } - - public func advanced(by n: TimeInterval) -> Date { - return self + n - } -} - extension Calendar { func numberOfDaysSinceStartOfTheYear(for date: Date) -> Int { let startOfTheYear: Date = startOfYear(date) diff --git a/Sources/SunKit/Extensions/Date+Strideable.swift b/Sources/SunKit/Extensions/Date+Strideable.swift new file mode 100644 index 0000000..a6b6d5d --- /dev/null +++ b/Sources/SunKit/Extensions/Date+Strideable.swift @@ -0,0 +1,44 @@ +// +// Date+Strideable.swift +// +// +// Copyright 2024 Leonardo Bertinelli, Davide Biancardi, Raffaele Fulgente, Clelia Iovine, Nicolas Mariniello, Fabio Pizzano +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +import Foundation + + +// Allows us to loop between two dates using interval time n. +extension Date: @retroactive Strideable { + public func distance(to other: Date) -> TimeInterval { + return other.timeIntervalSinceReferenceDate - self.timeIntervalSinceReferenceDate + } + + func toString(_ timeZone: TimeZone) -> String { + let df = DateFormatter() + df.timeZone = timeZone + let custom = DateFormatter.dateFormat( + fromTemplate: "MMdd HH:mm", + options: 0, + locale: Locale(identifier: "en") + ) + df.dateFormat = custom + + return df.string(from: self) + } + + public func advanced(by n: TimeInterval) -> Date { + return self + n + } +} From d57b6013280567f83998e5462738a47df35e8c70 Mon Sep 17 00:00:00 2001 From: Ignas Sireikis Date: Fri, 19 Sep 2025 17:15:10 -0500 Subject: [PATCH 65/91] Moved Calendar extension into folder. --- Sources/SunKit/Extensions.swift | 14 -------- .../Extensions/Calendar+startOfYear.swift | 34 +++++++++++++++++++ 2 files changed, 34 insertions(+), 14 deletions(-) create mode 100644 Sources/SunKit/Extensions/Calendar+startOfYear.swift diff --git a/Sources/SunKit/Extensions.swift b/Sources/SunKit/Extensions.swift index 9813dae..dadf459 100644 --- a/Sources/SunKit/Extensions.swift +++ b/Sources/SunKit/Extensions.swift @@ -19,20 +19,6 @@ import Foundation -extension Calendar { - func numberOfDaysSinceStartOfTheYear(for date: Date) -> Int { - let startOfTheYear: Date = startOfYear(date) - let startOfTheDay = startOfDay(for: date) - let numberOfDays = dateComponents([.day], from: startOfTheYear, to: startOfTheDay) - - return numberOfDays.day! + 1 - } - - func startOfYear(_ date: Date) -> Date { - return self.date(from: self.dateComponents([.year], from: date))! - } -} - extension TimeZone { func offset(_ date: Date) -> Double { let res = diff --git a/Sources/SunKit/Extensions/Calendar+startOfYear.swift b/Sources/SunKit/Extensions/Calendar+startOfYear.swift new file mode 100644 index 0000000..449300d --- /dev/null +++ b/Sources/SunKit/Extensions/Calendar+startOfYear.swift @@ -0,0 +1,34 @@ +// +// Calendar+startOfYear.swift +// +// +// Copyright 2024 Leonardo Bertinelli, Davide Biancardi, Raffaele Fulgente, Clelia Iovine, Nicolas Mariniello, Fabio Pizzano +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +import Foundation + + +extension Calendar { + func numberOfDaysSinceStartOfTheYear(for date: Date) -> Int { + let startOfTheYear: Date = startOfYear(date) + let startOfTheDay = startOfDay(for: date) + let numberOfDays = dateComponents([.day], from: startOfTheYear, to: startOfTheDay) + + return numberOfDays.day! + 1 + } + + func startOfYear(_ date: Date) -> Date { + return self.date(from: self.dateComponents([.year], from: date))! + } +} From dfafa3e704090c046ee7af6e8f1beae35beaafe9 Mon Sep 17 00:00:00 2001 From: Ignas Sireikis Date: Fri, 19 Sep 2025 17:16:25 -0500 Subject: [PATCH 66/91] Renamed and moved file. --- .../{Extensions.swift => Extensions/TimeZone+offset.swift} | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) rename Sources/SunKit/{Extensions.swift => Extensions/TimeZone+offset.swift} (88%) diff --git a/Sources/SunKit/Extensions.swift b/Sources/SunKit/Extensions/TimeZone+offset.swift similarity index 88% rename from Sources/SunKit/Extensions.swift rename to Sources/SunKit/Extensions/TimeZone+offset.swift index dadf459..b550ccf 100644 --- a/Sources/SunKit/Extensions.swift +++ b/Sources/SunKit/Extensions/TimeZone+offset.swift @@ -1,5 +1,5 @@ // -// Extensions.swift +// TimeZone+offset.swift // // // Copyright 2024 Leonardo Bertinelli, Davide Biancardi, Raffaele Fulgente, Clelia Iovine, Nicolas Mariniello, Fabio Pizzano @@ -21,12 +21,11 @@ import Foundation extension TimeZone { func offset(_ date: Date) -> Double { - let res = - Int(self.secondsFromGMT(for: date)) + let res = Int(self.secondsFromGMT(for: date)) + Int(self.daylightSavingTimeOffset(for: date)) - Int(Calendar.current.timeZone.secondsFromGMT(for: date)) - Int(Calendar.current.timeZone.daylightSavingTimeOffset(for: date)) - return Double(res)/SECONDS_IN_ONE_HOUR + return Double(res) / SECONDS_IN_ONE_HOUR } } From d5caadb886a638aa50340c59bdd77fde61fec665 Mon Sep 17 00:00:00 2001 From: Ignas Sireikis Date: Sun, 21 Sep 2025 15:02:34 -0500 Subject: [PATCH 67/91] Added methods to SunCoordinates in preparation for sun methods refactor. --- .../Sun Coordinates/SunCoordinates.swift | 83 ++++++++++++++++++- Sources/SunKit/Sun/Sun.swift | 53 +++++++++++- 2 files changed, 131 insertions(+), 5 deletions(-) diff --git a/Sources/SunKit/Sun Coordinates/SunCoordinates.swift b/Sources/SunKit/Sun Coordinates/SunCoordinates.swift index bfbef0d..696b769 100644 --- a/Sources/SunKit/Sun Coordinates/SunCoordinates.swift +++ b/Sources/SunKit/Sun Coordinates/SunCoordinates.swift @@ -19,10 +19,10 @@ import Foundation -internal final class SunCoordinates: Sendable { - let eclipticCoordinates: EclipticCoordinates - let equatorialCoordinates: EquatorialCoordinates - let horizonCoordinates: HorizonCoordinates +internal struct SunCoordinates: Sendable { + internal var eclipticCoordinates: EclipticCoordinates + internal var equatorialCoordinates: EquatorialCoordinates + internal var horizonCoordinates: HorizonCoordinates internal var azimuth: Angle { horizonCoordinates.azimuth @@ -47,4 +47,79 @@ internal final class SunCoordinates: Sendable { self.equatorialCoordinates = EquatorialCoordinates(declination: .zero) self.horizonCoordinates = HorizonCoordinates(altitude: .zero, azimuth: .zero) } + + // MARK: - Coordinates + + internal mutating func setCoordinates( + sunEclipticLongitude: Angle, + lstDecimal: Double, + latitude: Angle + ) { + setSunEclipticCoordinates(using: sunEclipticLongitude) + setSunEquatorialCoordinates(using: eclipticCoordinates) + setSunHorizonCoordinates(using: lstDecimal, latitude: latitude) + } + + // TODO: Remove mutating func, need to refactor EquatorialCoordinates + internal mutating func getSunHorizonCoordinatesGiven( + sunEclipticLongitude: Angle, + lstDecimal: Double, + latitude: Angle + ) -> HorizonCoordinates { + let eclipticCoordinates = calculateSunEclipticCoordinates(using: sunEclipticLongitude) + let equatorialCoordinates = calculateSunEquatorialCoordinates(using: eclipticCoordinates) + let horizonCoordinates = calculateSunHorizonCoordinates( + using: equatorialCoordinates, + lstDecimal: lstDecimal, + latitude: latitude + ) + + return horizonCoordinates + } + + // MARK: Ecliptic Coordinates + + internal mutating func setSunEclipticCoordinates(using sunEclipticLongitude: Angle) { + self.eclipticCoordinates = calculateSunEclipticCoordinates(using: sunEclipticLongitude) + } + + internal func calculateSunEclipticCoordinates(using sunEclipticLongitude: Angle) -> EclipticCoordinates { + EclipticCoordinates(eclipticLatitude: .zero, eclipticLongitude: sunEclipticLongitude) + } + + // MARK: Equatorial Coordinates + + internal mutating func setSunEquatorialCoordinates(using sunEclipticCoordinates: EclipticCoordinates) { + self.equatorialCoordinates = calculateSunEquatorialCoordinates(using: sunEclipticCoordinates) + } + + // TODO: Move behavior into EclipticCoordinates + internal func calculateSunEquatorialCoordinates(using sunEclipticCoordinates: EclipticCoordinates) -> EquatorialCoordinates { + sunEclipticCoordinates.ecliptic2Equatorial() + } + + // MARK: Horizon Coordinates + + internal mutating func setSunHorizonCoordinates( + using lstDecimal: Double, + latitude: Angle + ) { +// self.horizonCoordinates = calculateSunHorizonCoordinates( +// using: equatorialCoordinates, +// lstDecimal: lstDecimal, +// latitude: latitude +// ) + } + + // TODO: Remove mutating func, need to refactor EquatorialCoordinates + internal mutating func calculateSunHorizonCoordinates( + using sunEquatorialCoordinates: EquatorialCoordinates, + lstDecimal: Double, + latitude: Angle + ) -> HorizonCoordinates { +// let horizonCoordinates = sunEquatorialCoordinates.getHorizonCoordinates(lstDecimal: lstDecimal, latitude: latitude) + +// return horizonCoordinates ?? .init(altitude: .zero, azimuth: .zero) + return .init(altitude: .zero, azimuth: .zero) + } } diff --git a/Sources/SunKit/Sun/Sun.swift b/Sources/SunKit/Sun/Sun.swift index b8448d9..ce591e5 100644 --- a/Sources/SunKit/Sun/Sun.swift +++ b/Sources/SunKit/Sun/Sun.swift @@ -101,7 +101,28 @@ public struct Sun: Identifiable, Sendable { private var sunHorizonCoordinates: HorizonCoordinates = .init(altitude: .zero, azimuth: .zero) public private(set) var sunEquatorialCoordinates: EquatorialCoordinates = .init(declination: .zero) public private(set) var sunEclipticCoordinates: EclipticCoordinates = .init(eclipticLatitude: .zero, eclipticLongitude: .zero) - + + /* + public var azimuth: Angle { + sunCoordinates.azimuth + } + + public var altitude: Angle { + sunCoordinates.altitude + } + + private var sunHorizonCoordinates: HorizonCoordinates { + sunCoordinates.horizonCoordinates + } + + public var sunEquatorialCoordinates: EquatorialCoordinates { + sunCoordinates.equatorialCoordinates + } + + public var sunEclipticCoordinates: EclipticCoordinates { + sunCoordinates.eclipticCoordinates + } + */ /*-------------------------------------------------------------------- Sun Events during the year *-------------------------------------------------------------------*/ @@ -428,6 +449,21 @@ public struct Sun: Identifiable, Sendable { } } + /* + // TODO: Move calculateLSTDecimal and calculateSunEclipticLongitude into SunCoordinates + /// Updates Horizon coordinates, Ecliptic coordinates and Equatorial coordinates of the Sun + private mutating func updateSunCoordinates() { + let lstDecimal = calculateLSTDecimal(using: self.date) + let sunEclipticLongitude: Angle = calculateSunEclipticLongitude(using: self.date) + + sunCoordinates.setCoordinates( + sunEclipticLongitude: sunEclipticLongitude, + lstDecimal: lstDecimal, + latitude: latitude + ) + } + */ + private func calculateLSTDecimal(using date: Date) -> Double { // Convert LCT to UT, GST, and LST times and adjust the date if needed let gstHMS = uT2GST(self.date) @@ -468,6 +504,7 @@ public struct Sun: Identifiable, Sendable { return sunMeanAnomaly } + // TODO: Delete // TODO: Move behavior into EclipticCoordinates, refactor to Object private func calculateSunEclipticCoordinates(using sunEclipticLongitude: Angle) -> EclipticCoordinates { .init(eclipticLatitude: .zero, eclipticLongitude: sunEclipticLongitude) @@ -735,6 +772,20 @@ public struct Sun: Identifiable, Sendable { } } + /* + public mutating func getSunHorizonCoordinatesFrom(date: Date) -> HorizonCoordinates { + let lstDecimal = calculateLSTDecimal(using: date) + let sunEclipticLongitude: Angle = calculateSunEclipticLongitude(using: date) + let sunHorizonCoordinates = sunCoordinates.getSunHorizonCoordinatesGiven( + sunEclipticLongitude: sunEclipticLongitude, + lstDecimal: lstDecimal, + latitude: latitude + ) + + return sunHorizonCoordinates + } + */ + // MARK: - Get Month Events // TODO: Move magic numbers into Enum cases or subclasses From 41939d39341d112a2cbee66f696602c450836e8b Mon Sep 17 00:00:00 2001 From: Ignas Sireikis Date: Sun, 21 Sep 2025 15:04:35 -0500 Subject: [PATCH 68/91] Made code style in EclipticCoordinates consistent. --- .../SunKit/Sun Coordinates/EclipticCoordinates.swift | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/Sources/SunKit/Sun Coordinates/EclipticCoordinates.swift b/Sources/SunKit/Sun Coordinates/EclipticCoordinates.swift index c8a75df..b0dba98 100644 --- a/Sources/SunKit/Sun Coordinates/EclipticCoordinates.swift +++ b/Sources/SunKit/Sun Coordinates/EclipticCoordinates.swift @@ -28,18 +28,13 @@ public struct EclipticCoordinates: Equatable, Hashable, Codable, Sendable { /// Converts ecliptic coordinatates to equatorial coordinates /// - Returns: Equatorial coordinates of the instance public func ecliptic2Equatorial() -> EquatorialCoordinates { - //Step4: let tEclipticToEquatorial: Angle = .init(radians: sin(eclipticLatitude.radians) * cos(EclipticCoordinates.obliquityOfTheEcliptic.radians) + cos(eclipticLatitude.radians) * sin(EclipticCoordinates.obliquityOfTheEcliptic.radians) * sin(eclipticLongitude.radians)) - //Step5: let moonDeclination: Angle = .init(radians: asin(tEclipticToEquatorial.radians)) - //Step6: let yEclipticToEquatorial = sin(eclipticLongitude.radians) * cos(EclipticCoordinates.obliquityOfTheEcliptic.radians) - tan(eclipticLatitude.radians) * sin(EclipticCoordinates.obliquityOfTheEcliptic.radians) - //Step7: let xEclipticToEquatorial = cos(eclipticLongitude.radians) - //Step8: var r: Angle = .init(radians: atan(yEclipticToEquatorial / xEclipticToEquatorial)) - //Step9: - switch (yEclipticToEquatorial >= 0,xEclipticToEquatorial >= 0){ + + switch (yEclipticToEquatorial >= 0, xEclipticToEquatorial >= 0) { case (true, true): break @@ -51,7 +46,7 @@ public struct EclipticCoordinates: Equatable, Hashable, Codable, Sendable { r.degrees += 180 } - let alfa: Angle = .init(degrees: r.degrees / 15) + let alfa: Angle = .init(degrees: r.degrees / 15) let delta: Angle = .init(degrees: moonDeclination.degrees) return .init(declination: delta, rightAscension: alfa) From 54f6329848b51303883d6b0b2bf7816ade54c092 Mon Sep 17 00:00:00 2001 From: Ignas Sireikis Date: Sun, 21 Sep 2025 15:10:56 -0500 Subject: [PATCH 69/91] Reorganized EquatorialCoordinates methods to follow a high to low level concept organization. --- .../EquatorialCoordinates.swift | 106 +++++++++--------- 1 file changed, 53 insertions(+), 53 deletions(-) diff --git a/Sources/SunKit/Sun Coordinates/EquatorialCoordinates.swift b/Sources/SunKit/Sun Coordinates/EquatorialCoordinates.swift index 4e473ec..262df49 100644 --- a/Sources/SunKit/Sun Coordinates/EquatorialCoordinates.swift +++ b/Sources/SunKit/Sun Coordinates/EquatorialCoordinates.swift @@ -47,35 +47,33 @@ public struct EquatorialCoordinates: Equatable, Hashable, Codable, Sendable { self.declination = declination } - /// To set right ascension we need LST and right ascension. If right ascension is nill we can't set it. - /// - Parameter lstDecimal: Local Sideral Time in decimal - /// - Returns: The value of hour angle just been set. Nil if right ascension is also nil - public mutating func setHourAngleFrom(lstDecimal: Double) -> Angle? { - guard let rightAscension = self.rightAscension else { + public func equatorial2Ecliptic() -> EclipticCoordinates? { + guard var rightAscension = rightAscension else { return nil } - var hourAngleDecimal = lstDecimal - rightAscension.degrees - if hourAngleDecimal < 0 { - hourAngleDecimal += 24 - } - self.hourAngle = .init(degrees: hourAngleDecimal * 15) + rightAscension.degrees = rightAscension.degrees * 15 // from h format to degrees - return self.hourAngle - } - - /// To set right ascension we need LST and hour angle. If hour angle is nill we can't set it. - /// - Parameter lstDecimal: Local Sideral Time in decimal - /// - Returns: The value of right ascension just been set. Nil if hour angle is also nil - public mutating func setRightAscensionFrom(lstDecimal: Double) -> Angle? { - guard let hourAngle = self.hourAngle else { - return nil + let tEquatorialToEcliptic: Angle = .init(radians: sin(declination.radians) * cos(EclipticCoordinates.obliquityOfTheEcliptic.radians) - cos(declination.radians) * sin(EclipticCoordinates.obliquityOfTheEcliptic.radians) * sin(rightAscension.radians)) + let eclipticLatitude: Angle = .init(radians: asin(tEquatorialToEcliptic.radians)) + let yEquatorialToEcliptic = sin(rightAscension.radians) * cos(EclipticCoordinates.obliquityOfTheEcliptic.radians) + tan(declination.radians) * sin(EclipticCoordinates.obliquityOfTheEcliptic.radians) + let xEquatorialToEcliptic = cos(rightAscension.radians) + var r: Angle = .init(radians: atan(yEquatorialToEcliptic / xEquatorialToEcliptic)) + + switch (yEquatorialToEcliptic >= 0, xEquatorialToEcliptic >= 0) { + case (true, true): + break + case (true,false): + r.degrees += 180 + case(false,true): + r.degrees += 360 + case(false,false): + r.degrees += 180 } - let hourAngleDecimal = hourAngle.degrees / 15 - self.rightAscension = .init(degrees: lstDecimal - hourAngleDecimal) + let eclipticLongitude: Angle = .init(degrees: r.degrees) - return self.rightAscension + return .init(eclipticLatitude: eclipticLatitude, eclipticLongitude: eclipticLongitude) } /// Converts Equatorial coordinates to Horizon coordinates. @@ -97,19 +95,21 @@ public struct EquatorialCoordinates: Equatable, Hashable, Codable, Sendable { return calculateHorizonCoordinates(from: latitude) } - /// Converts Equatorial coordinates to Horizon coordinates. - /// - /// Since horizon coordinates depend on the position, we need also latitude parameter to create an EquatorialCoordinates instance. - /// - /// - Parameters: - /// - latitude: Latitude of the observer - /// - Returns: Horizon coordinates for the given latitude. Nil if hour angle is not defined. - public func equatorial2Horizon(latitude: Angle) -> HorizonCoordinates? { - guard let _ = self.hourAngle else { + /// To set right ascension we need LST and right ascension. If right ascension is nill we can't set it. + /// - Parameter lstDecimal: Local Sideral Time in decimal + /// - Returns: The value of hour angle just been set. Nil if right ascension is also nil + public mutating func setHourAngleFrom(lstDecimal: Double) -> Angle? { + guard let rightAscension = self.rightAscension else { return nil } - return calculateHorizonCoordinates(from: latitude) + var hourAngleDecimal = lstDecimal - rightAscension.degrees + if hourAngleDecimal < 0 { + hourAngleDecimal += 24 + } + self.hourAngle = .init(degrees: hourAngleDecimal * 15) + + return self.hourAngle } private func calculateHorizonCoordinates(from latitude: Angle) -> HorizonCoordinates { @@ -126,32 +126,32 @@ public struct EquatorialCoordinates: Equatable, Hashable, Codable, Sendable { return .init(altitude: altitude, azimuth: azimuth) } - public func equatorial2Ecliptic() -> EclipticCoordinates? { - guard var rightAscension = rightAscension else { + /// Converts Equatorial coordinates to Horizon coordinates. + /// + /// Since horizon coordinates depend on the position, we need also latitude parameter to create an EquatorialCoordinates instance. + /// + /// - Parameters: + /// - latitude: Latitude of the observer + /// - Returns: Horizon coordinates for the given latitude. Nil if hour angle is not defined. + public func equatorial2Horizon(latitude: Angle) -> HorizonCoordinates? { + guard let _ = self.hourAngle else { return nil } - rightAscension.degrees = rightAscension.degrees * 15 // from h format to degrees - - let tEquatorialToEcliptic: Angle = .init(radians: sin(declination.radians) * cos(EclipticCoordinates.obliquityOfTheEcliptic.radians) - cos(declination.radians) * sin(EclipticCoordinates.obliquityOfTheEcliptic.radians) * sin(rightAscension.radians)) - let eclipticLatitude: Angle = .init(radians: asin(tEquatorialToEcliptic.radians)) - let yEquatorialToEcliptic = sin(rightAscension.radians) * cos(EclipticCoordinates.obliquityOfTheEcliptic.radians) + tan(declination.radians) * sin(EclipticCoordinates.obliquityOfTheEcliptic.radians) - let xEquatorialToEcliptic = cos(rightAscension.radians) - var r: Angle = .init(radians: atan(yEquatorialToEcliptic / xEquatorialToEcliptic)) - - switch (yEquatorialToEcliptic >= 0, xEquatorialToEcliptic >= 0) { - case (true, true): - break - case (true,false): - r.degrees += 180 - case(false,true): - r.degrees += 360 - case(false,false): - r.degrees += 180 + return calculateHorizonCoordinates(from: latitude) + } + + /// To set right ascension we need LST and hour angle. If hour angle is nill we can't set it. + /// - Parameter lstDecimal: Local Sideral Time in decimal + /// - Returns: The value of right ascension just been set. Nil if hour angle is also nil + public mutating func setRightAscensionFrom(lstDecimal: Double) -> Angle? { + guard let hourAngle = self.hourAngle else { + return nil } - let eclipticLongitude: Angle = .init(degrees: r.degrees) + let hourAngleDecimal = hourAngle.degrees / 15 + self.rightAscension = .init(degrees: lstDecimal - hourAngleDecimal) - return .init(eclipticLatitude: eclipticLatitude, eclipticLongitude: eclipticLongitude) + return self.rightAscension } } From 3fd1f22ec52c9d0926551eea738834a77369b861 Mon Sep 17 00:00:00 2001 From: Ignas Sireikis Date: Mon, 22 Sep 2025 11:43:29 -0500 Subject: [PATCH 70/91] Moved sun constant sunEclipticLongitudePerigee into SunCoordinates. --- Sources/SunKit/Sun Coordinates/SunCoordinates.swift | 2 ++ Sources/SunKit/Sun/Sun.swift | 7 ++++--- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/Sources/SunKit/Sun Coordinates/SunCoordinates.swift b/Sources/SunKit/Sun Coordinates/SunCoordinates.swift index 696b769..959e846 100644 --- a/Sources/SunKit/Sun Coordinates/SunCoordinates.swift +++ b/Sources/SunKit/Sun Coordinates/SunCoordinates.swift @@ -24,6 +24,8 @@ internal struct SunCoordinates: Sendable { internal var equatorialCoordinates: EquatorialCoordinates internal var horizonCoordinates: HorizonCoordinates + internal let sunEclipticLongitudePerigee: Angle = .init(degrees: 282.938346) + internal var azimuth: Angle { horizonCoordinates.azimuth } diff --git a/Sources/SunKit/Sun/Sun.swift b/Sources/SunKit/Sun/Sun.swift index ce591e5..a4b337b 100644 --- a/Sources/SunKit/Sun/Sun.swift +++ b/Sources/SunKit/Sun/Sun.swift @@ -333,7 +333,6 @@ public struct Sun: Identifiable, Sendable { // Sun constants private let sunEclipticLongitudeAtTheEpoch: Angle = .init(degrees: 280.466069) - private let sunEclipticLongitudePerigee: Angle = .init(degrees: 282.938346) /// Number of the days passed since the start of the year for the self.date private var daysPassedFromStartOfTheYear: Int { @@ -432,6 +431,8 @@ public struct Sun: Identifiable, Sendable { self.decemberSolstice = getDecemberSolstice() ?? Date() } + // TODO: SunCoordinates implicitly depends on self.date and self.longitude (location) + /// Updates Horizon coordinates, Ecliptic coordinates and Equatorial coordinates of the Sun private mutating func updateSunCoordinates() { let lstDecimal = calculateLSTDecimal(using: self.date) @@ -488,7 +489,7 @@ public struct Sun: Identifiable, Sendable { var sunTrueAnomaly = sunMeanAnomaly.degrees + equationOfCenter // Add or subtract multiples of 360° to adjust sun true anomaly to the range of 0° to 360° sunTrueAnomaly = extendedMod(sunTrueAnomaly, 360) - var sunEclipticLongitude: Angle = .init(degrees: sunTrueAnomaly + sunEclipticLongitudePerigee.degrees) + var sunEclipticLongitude: Angle = .init(degrees: sunTrueAnomaly + sunCoordinates.sunEclipticLongitudePerigee.degrees) if sunEclipticLongitude.degrees > 360 { sunEclipticLongitude.degrees -= 360 @@ -498,7 +499,7 @@ public struct Sun: Identifiable, Sendable { } private func getSunMeanAnomaly(from elapsedDaysSinceStandardEpoch: Double) -> Angle { - var sunMeanAnomaly: Angle = .init(degrees:(((360.0 * elapsedDaysSinceStandardEpoch) / 365.242191) + sunEclipticLongitudeAtTheEpoch.degrees - sunEclipticLongitudePerigee.degrees)) + var sunMeanAnomaly: Angle = .init(degrees:(((360.0 * elapsedDaysSinceStandardEpoch) / 365.242191) + sunEclipticLongitudeAtTheEpoch.degrees - sunCoordinates.sunEclipticLongitudePerigee.degrees)) sunMeanAnomaly = .init(degrees: extendedMod(sunMeanAnomaly.degrees, 360)) return sunMeanAnomaly From fdf4409e60d7210fc84f3040104dc1d8707ea885 Mon Sep 17 00:00:00 2001 From: Ignas Sireikis Date: Mon, 22 Sep 2025 11:44:34 -0500 Subject: [PATCH 71/91] Moved sun constant sunEclipticLongitudeAtTheEpoch into SunCoordinates. --- Sources/SunKit/Sun Coordinates/SunCoordinates.swift | 1 + Sources/SunKit/Sun/Sun.swift | 5 +---- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/Sources/SunKit/Sun Coordinates/SunCoordinates.swift b/Sources/SunKit/Sun Coordinates/SunCoordinates.swift index 959e846..09592af 100644 --- a/Sources/SunKit/Sun Coordinates/SunCoordinates.swift +++ b/Sources/SunKit/Sun Coordinates/SunCoordinates.swift @@ -24,6 +24,7 @@ internal struct SunCoordinates: Sendable { internal var equatorialCoordinates: EquatorialCoordinates internal var horizonCoordinates: HorizonCoordinates + internal let sunEclipticLongitudeAtTheEpoch: Angle = .init(degrees: 280.466069) internal let sunEclipticLongitudePerigee: Angle = .init(degrees: 282.938346) internal var azimuth: Angle { diff --git a/Sources/SunKit/Sun/Sun.swift b/Sources/SunKit/Sun/Sun.swift index a4b337b..440b5c5 100644 --- a/Sources/SunKit/Sun/Sun.swift +++ b/Sources/SunKit/Sun/Sun.swift @@ -331,9 +331,6 @@ public struct Sun: Identifiable, Sendable { timeZone.secondsFromGMT(for: self.date) } - // Sun constants - private let sunEclipticLongitudeAtTheEpoch: Angle = .init(degrees: 280.466069) - /// Number of the days passed since the start of the year for the self.date private var daysPassedFromStartOfTheYear: Int { let year = calendar.component(.year, from: date) @@ -499,7 +496,7 @@ public struct Sun: Identifiable, Sendable { } private func getSunMeanAnomaly(from elapsedDaysSinceStandardEpoch: Double) -> Angle { - var sunMeanAnomaly: Angle = .init(degrees:(((360.0 * elapsedDaysSinceStandardEpoch) / 365.242191) + sunEclipticLongitudeAtTheEpoch.degrees - sunCoordinates.sunEclipticLongitudePerigee.degrees)) + var sunMeanAnomaly: Angle = .init(degrees:(((360.0 * elapsedDaysSinceStandardEpoch) / 365.242191) + sunCoordinates.sunEclipticLongitudeAtTheEpoch.degrees - sunCoordinates.sunEclipticLongitudePerigee.degrees)) sunMeanAnomaly = .init(degrees: extendedMod(sunMeanAnomaly.degrees, 360)) return sunMeanAnomaly From 84808cd737794dc793b840d861fa78d41af6214f Mon Sep 17 00:00:00 2001 From: Ignas Sireikis Date: Mon, 22 Sep 2025 11:47:02 -0500 Subject: [PATCH 72/91] Moved function getSunMeanAnomaly into SunCoordinates. --- Sources/SunKit/Sun Coordinates/SunCoordinates.swift | 9 +++++++++ Sources/SunKit/Sun/Sun.swift | 9 +-------- 2 files changed, 10 insertions(+), 8 deletions(-) diff --git a/Sources/SunKit/Sun Coordinates/SunCoordinates.swift b/Sources/SunKit/Sun Coordinates/SunCoordinates.swift index 09592af..3585d31 100644 --- a/Sources/SunKit/Sun Coordinates/SunCoordinates.swift +++ b/Sources/SunKit/Sun Coordinates/SunCoordinates.swift @@ -125,4 +125,13 @@ internal struct SunCoordinates: Sendable { // return horizonCoordinates ?? .init(altitude: .zero, azimuth: .zero) return .init(altitude: .zero, azimuth: .zero) } + + // MARK: - Helpers + + internal func getSunMeanAnomaly(from elapsedDaysSinceStandardEpoch: Double) -> Angle { + var sunMeanAnomaly: Angle = .init(degrees: (((360.0 * elapsedDaysSinceStandardEpoch) / 365.242191) + sunEclipticLongitudeAtTheEpoch.degrees - sunEclipticLongitudePerigee.degrees)) + sunMeanAnomaly = .init(degrees: extendedMod(sunMeanAnomaly.degrees, 360)) + + return sunMeanAnomaly + } } diff --git a/Sources/SunKit/Sun/Sun.swift b/Sources/SunKit/Sun/Sun.swift index 440b5c5..340a327 100644 --- a/Sources/SunKit/Sun/Sun.swift +++ b/Sources/SunKit/Sun/Sun.swift @@ -479,7 +479,7 @@ public struct Sun: Identifiable, Sendable { // Compute the total number of elapsed days, including fractional days, since the standard epoch (i.e., JD − JDe) let elapsedDaysSinceStandardEpoch: Double = jdTT - jdEpoch // Use the algorithm from section 6.2.3 to calculate the Sun’s ecliptic longitude and mean anomaly for the given UT date and time. - let sunMeanAnomaly = getSunMeanAnomaly(from: elapsedDaysSinceStandardEpoch) + let sunMeanAnomaly = sunCoordinates.getSunMeanAnomaly(from: elapsedDaysSinceStandardEpoch) // Use Equation 6.2.4 to aproximate the Equation of the center let equationOfCenter = 360 / Double.pi * sin(sunMeanAnomaly.radians) * 0.016708 // Add EoC to sun mean anomaly to get the sun true anomaly @@ -495,13 +495,6 @@ public struct Sun: Identifiable, Sendable { return sunEclipticLongitude } - private func getSunMeanAnomaly(from elapsedDaysSinceStandardEpoch: Double) -> Angle { - var sunMeanAnomaly: Angle = .init(degrees:(((360.0 * elapsedDaysSinceStandardEpoch) / 365.242191) + sunCoordinates.sunEclipticLongitudeAtTheEpoch.degrees - sunCoordinates.sunEclipticLongitudePerigee.degrees)) - sunMeanAnomaly = .init(degrees: extendedMod(sunMeanAnomaly.degrees, 360)) - - return sunMeanAnomaly - } - // TODO: Delete // TODO: Move behavior into EclipticCoordinates, refactor to Object private func calculateSunEclipticCoordinates(using sunEclipticLongitude: Angle) -> EclipticCoordinates { From bc2c1fe514b8cbd36c350786d1f5aba8f8889116 Mon Sep 17 00:00:00 2001 From: Ignas Sireikis Date: Mon, 22 Sep 2025 11:48:43 -0500 Subject: [PATCH 73/91] Moved function calculateSunEclipticLongitude into SunCoordinates. --- .../Sun Coordinates/SunCoordinates.swift | 24 ++++++++++++++++ Sources/SunKit/Sun/Sun.swift | 28 ++----------------- 2 files changed, 26 insertions(+), 26 deletions(-) diff --git a/Sources/SunKit/Sun Coordinates/SunCoordinates.swift b/Sources/SunKit/Sun Coordinates/SunCoordinates.swift index 3585d31..6b8caf3 100644 --- a/Sources/SunKit/Sun Coordinates/SunCoordinates.swift +++ b/Sources/SunKit/Sun Coordinates/SunCoordinates.swift @@ -128,6 +128,30 @@ internal struct SunCoordinates: Sendable { // MARK: - Helpers + internal func calculateSunEclipticLongitude(using date: Date) -> Angle { + // Julian number for standard epoch 2000 + let jdEpoch = 2451545.00 + // Compute the Julian day number for the desired date using the Greenwich date and TT + let jdTT = jdFromDate(date: date) + // Compute the total number of elapsed days, including fractional days, since the standard epoch (i.e., JD − JDe) + let elapsedDaysSinceStandardEpoch: Double = jdTT - jdEpoch + // Use the algorithm from section 6.2.3 to calculate the Sun’s ecliptic longitude and mean anomaly for the given UT date and time. + let sunMeanAnomaly = getSunMeanAnomaly(from: elapsedDaysSinceStandardEpoch) + // Use Equation 6.2.4 to aproximate the Equation of the center + let equationOfCenter = 360 / Double.pi * sin(sunMeanAnomaly.radians) * 0.016708 + // Add EoC to sun mean anomaly to get the sun true anomaly + var sunTrueAnomaly = sunMeanAnomaly.degrees + equationOfCenter + // Add or subtract multiples of 360° to adjust sun true anomaly to the range of 0° to 360° + sunTrueAnomaly = extendedMod(sunTrueAnomaly, 360) + var sunEclipticLongitude: Angle = .init(degrees: sunTrueAnomaly + sunEclipticLongitudePerigee.degrees) + + if sunEclipticLongitude.degrees > 360 { + sunEclipticLongitude.degrees -= 360 + } + + return sunEclipticLongitude + } + internal func getSunMeanAnomaly(from elapsedDaysSinceStandardEpoch: Double) -> Angle { var sunMeanAnomaly: Angle = .init(degrees: (((360.0 * elapsedDaysSinceStandardEpoch) / 365.242191) + sunEclipticLongitudeAtTheEpoch.degrees - sunEclipticLongitudePerigee.degrees)) sunMeanAnomaly = .init(degrees: extendedMod(sunMeanAnomaly.degrees, 360)) diff --git a/Sources/SunKit/Sun/Sun.swift b/Sources/SunKit/Sun/Sun.swift index 340a327..be6d3a0 100644 --- a/Sources/SunKit/Sun/Sun.swift +++ b/Sources/SunKit/Sun/Sun.swift @@ -433,7 +433,7 @@ public struct Sun: Identifiable, Sendable { /// Updates Horizon coordinates, Ecliptic coordinates and Equatorial coordinates of the Sun private mutating func updateSunCoordinates() { let lstDecimal = calculateLSTDecimal(using: self.date) - let sunEclipticLongitude: Angle = calculateSunEclipticLongitude(using: self.date) + let sunEclipticLongitude: Angle = sunCoordinates.calculateSunEclipticLongitude(using: self.date) sunEclipticCoordinates = calculateSunEclipticCoordinates(using: sunEclipticLongitude) // Ecliptic to Equatorial @@ -471,30 +471,6 @@ public struct Sun: Identifiable, Sendable { return lstDecimal } - private func calculateSunEclipticLongitude(using date: Date) -> Angle { - // Julian number for standard epoch 2000 - let jdEpoch = 2451545.00 - // Compute the Julian day number for the desired date using the Greenwich date and TT - let jdTT = jdFromDate(date: date) - // Compute the total number of elapsed days, including fractional days, since the standard epoch (i.e., JD − JDe) - let elapsedDaysSinceStandardEpoch: Double = jdTT - jdEpoch - // Use the algorithm from section 6.2.3 to calculate the Sun’s ecliptic longitude and mean anomaly for the given UT date and time. - let sunMeanAnomaly = sunCoordinates.getSunMeanAnomaly(from: elapsedDaysSinceStandardEpoch) - // Use Equation 6.2.4 to aproximate the Equation of the center - let equationOfCenter = 360 / Double.pi * sin(sunMeanAnomaly.radians) * 0.016708 - // Add EoC to sun mean anomaly to get the sun true anomaly - var sunTrueAnomaly = sunMeanAnomaly.degrees + equationOfCenter - // Add or subtract multiples of 360° to adjust sun true anomaly to the range of 0° to 360° - sunTrueAnomaly = extendedMod(sunTrueAnomaly, 360) - var sunEclipticLongitude: Angle = .init(degrees: sunTrueAnomaly + sunCoordinates.sunEclipticLongitudePerigee.degrees) - - if sunEclipticLongitude.degrees > 360 { - sunEclipticLongitude.degrees -= 360 - } - - return sunEclipticLongitude - } - // TODO: Delete // TODO: Move behavior into EclipticCoordinates, refactor to Object private func calculateSunEclipticCoordinates(using sunEclipticLongitude: Angle) -> EclipticCoordinates { @@ -747,7 +723,7 @@ public struct Sun: Identifiable, Sendable { // TODO: Extract functions to collapse into updateSunCoordinates public func getSunHorizonCoordinatesFrom(date: Date) -> HorizonCoordinates { let lstDecimal = calculateLSTDecimal(using: date) - let sunEclipticLongitude: Angle = calculateSunEclipticLongitude(using: date) + let sunEclipticLongitude: Angle = sunCoordinates.calculateSunEclipticLongitude(using: date) let sunEclipticCoordinates: EclipticCoordinates = calculateSunEclipticCoordinates(using: sunEclipticLongitude) // Ecliptic to Equatorial From c3861f73ebff35b03ee8e0f3185ba17e181c1cca Mon Sep 17 00:00:00 2001 From: Ignas Sireikis Date: Mon, 22 Sep 2025 11:49:31 -0500 Subject: [PATCH 74/91] Adjust privacy level in SunCoordinates. --- Sources/SunKit/Sun Coordinates/SunCoordinates.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Sources/SunKit/Sun Coordinates/SunCoordinates.swift b/Sources/SunKit/Sun Coordinates/SunCoordinates.swift index 6b8caf3..2c7995c 100644 --- a/Sources/SunKit/Sun Coordinates/SunCoordinates.swift +++ b/Sources/SunKit/Sun Coordinates/SunCoordinates.swift @@ -152,7 +152,7 @@ internal struct SunCoordinates: Sendable { return sunEclipticLongitude } - internal func getSunMeanAnomaly(from elapsedDaysSinceStandardEpoch: Double) -> Angle { + private func getSunMeanAnomaly(from elapsedDaysSinceStandardEpoch: Double) -> Angle { var sunMeanAnomaly: Angle = .init(degrees: (((360.0 * elapsedDaysSinceStandardEpoch) / 365.242191) + sunEclipticLongitudeAtTheEpoch.degrees - sunEclipticLongitudePerigee.degrees)) sunMeanAnomaly = .init(degrees: extendedMod(sunMeanAnomaly.degrees, 360)) From 1ef3f5c3d9bdeb87438c7c37acf722b10f775cd3 Mon Sep 17 00:00:00 2001 From: Ignas Sireikis Date: Mon, 22 Sep 2025 11:52:42 -0500 Subject: [PATCH 75/91] Parameterized function calculateLSTDecimal and moved it into SunCoordinates. --- Sources/SunKit/Sun Coordinates/SunCoordinates.swift | 9 +++++++++ Sources/SunKit/Sun/Sun.swift | 13 ++----------- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/Sources/SunKit/Sun Coordinates/SunCoordinates.swift b/Sources/SunKit/Sun Coordinates/SunCoordinates.swift index 2c7995c..3f74650 100644 --- a/Sources/SunKit/Sun Coordinates/SunCoordinates.swift +++ b/Sources/SunKit/Sun Coordinates/SunCoordinates.swift @@ -128,6 +128,15 @@ internal struct SunCoordinates: Sendable { // MARK: - Helpers + internal func calculateLSTDecimal(using date: Date, longitude: Angle) -> Double { + // Convert LCT to UT, GST, and LST times and adjust the date if needed + let gstHMS = uT2GST(date) + let lstHMS = gST2LST(gstHMS, longitude: longitude) + let lstDecimal = lstHMS.hMS2Decimal() + + return lstDecimal + } + internal func calculateSunEclipticLongitude(using date: Date) -> Angle { // Julian number for standard epoch 2000 let jdEpoch = 2451545.00 diff --git a/Sources/SunKit/Sun/Sun.swift b/Sources/SunKit/Sun/Sun.swift index be6d3a0..cdfd8b1 100644 --- a/Sources/SunKit/Sun/Sun.swift +++ b/Sources/SunKit/Sun/Sun.swift @@ -432,7 +432,7 @@ public struct Sun: Identifiable, Sendable { /// Updates Horizon coordinates, Ecliptic coordinates and Equatorial coordinates of the Sun private mutating func updateSunCoordinates() { - let lstDecimal = calculateLSTDecimal(using: self.date) + let lstDecimal = sunCoordinates.calculateLSTDecimal(using: self.date, longitude: self.longitude) let sunEclipticLongitude: Angle = sunCoordinates.calculateSunEclipticLongitude(using: self.date) sunEclipticCoordinates = calculateSunEclipticCoordinates(using: sunEclipticLongitude) @@ -462,15 +462,6 @@ public struct Sun: Identifiable, Sendable { } */ - private func calculateLSTDecimal(using date: Date) -> Double { - // Convert LCT to UT, GST, and LST times and adjust the date if needed - let gstHMS = uT2GST(self.date) - let lstHMS = gST2LST(gstHMS, longitude: longitude) - let lstDecimal = lstHMS.hMS2Decimal() - - return lstDecimal - } - // TODO: Delete // TODO: Move behavior into EclipticCoordinates, refactor to Object private func calculateSunEclipticCoordinates(using sunEclipticLongitude: Angle) -> EclipticCoordinates { @@ -722,7 +713,7 @@ public struct Sun: Identifiable, Sendable { // TODO: Extract functions to collapse into updateSunCoordinates public func getSunHorizonCoordinatesFrom(date: Date) -> HorizonCoordinates { - let lstDecimal = calculateLSTDecimal(using: date) + let lstDecimal = sunCoordinates.calculateLSTDecimal(using: self.date, longitude: self.longitude) let sunEclipticLongitude: Angle = sunCoordinates.calculateSunEclipticLongitude(using: date) let sunEclipticCoordinates: EclipticCoordinates = calculateSunEclipticCoordinates(using: sunEclipticLongitude) From ec193f1af9ae5d135dcb3caed74c909fe1bcd313 Mon Sep 17 00:00:00 2001 From: Ignas Sireikis Date: Mon, 22 Sep 2025 11:56:39 -0500 Subject: [PATCH 76/91] Moved getSunHorizonCoordinatesFrom function closer to where it's used. --- Sources/SunKit/Sun/Sun.swift | 69 +++++++++++++++++------------------- 1 file changed, 33 insertions(+), 36 deletions(-) diff --git a/Sources/SunKit/Sun/Sun.swift b/Sources/SunKit/Sun/Sun.swift index cdfd8b1..e742110 100644 --- a/Sources/SunKit/Sun/Sun.swift +++ b/Sources/SunKit/Sun/Sun.swift @@ -448,7 +448,6 @@ public struct Sun: Identifiable, Sendable { } /* - // TODO: Move calculateLSTDecimal and calculateSunEclipticLongitude into SunCoordinates /// Updates Horizon coordinates, Ecliptic coordinates and Equatorial coordinates of the Sun private mutating func updateSunCoordinates() { let lstDecimal = calculateLSTDecimal(using: self.date) @@ -473,6 +472,39 @@ public struct Sun: Identifiable, Sendable { sunEclipticCoordinates.ecliptic2Equatorial() } + // TODO: Extract functions to collapse into updateSunCoordinates + public func getSunHorizonCoordinatesFrom(date: Date) -> HorizonCoordinates { + let lstDecimal = sunCoordinates.calculateLSTDecimal(using: self.date, longitude: self.longitude) + let sunEclipticLongitude: Angle = sunCoordinates.calculateSunEclipticLongitude(using: date) + + let sunEclipticCoordinates: EclipticCoordinates = calculateSunEclipticCoordinates(using: sunEclipticLongitude) + // Ecliptic to Equatorial + var sunEquatorialCoordinates: EquatorialCoordinates = calculateSunEquatorialCoordinates(using: sunEclipticCoordinates) + // Equatorial to Horizon + let sunHorizonCoordinates: HorizonCoordinates = calculateSunHorizonCoordinates(using: lstDecimal) + + return .init(altitude: sunHorizonCoordinates.altitude, azimuth: sunHorizonCoordinates.azimuth) + + // TODO: Move behavior intoEquatorialCoordinates, refactor to Object + func calculateSunHorizonCoordinates(using lstDecimal: Double) -> HorizonCoordinates { + sunEquatorialCoordinates.equatorial2Horizon(lstDecimal: lstDecimal, latitude: latitude) ?? .init(altitude: .zero, azimuth: .zero) + } + } + + /* + public mutating func getSunHorizonCoordinatesFrom(date: Date) -> HorizonCoordinates { + let lstDecimal = calculateLSTDecimal(using: date) + let sunEclipticLongitude: Angle = calculateSunEclipticLongitude(using: date) + let sunHorizonCoordinates = sunCoordinates.getSunHorizonCoordinatesGiven( + sunEclipticLongitude: sunEclipticLongitude, + lstDecimal: lstDecimal, + latitude: latitude + ) + + return sunHorizonCoordinates + } + */ + // MARK: - Get Day Events /// Astronomical Dawn is when the Sun reaches -18 degrees of elevation. @@ -709,41 +741,6 @@ public struct Sun: Identifiable, Sendable { return newDate } - // MARK: - Get Horizon Coordinates - - // TODO: Extract functions to collapse into updateSunCoordinates - public func getSunHorizonCoordinatesFrom(date: Date) -> HorizonCoordinates { - let lstDecimal = sunCoordinates.calculateLSTDecimal(using: self.date, longitude: self.longitude) - let sunEclipticLongitude: Angle = sunCoordinates.calculateSunEclipticLongitude(using: date) - - let sunEclipticCoordinates: EclipticCoordinates = calculateSunEclipticCoordinates(using: sunEclipticLongitude) - // Ecliptic to Equatorial - var sunEquatorialCoordinates: EquatorialCoordinates = calculateSunEquatorialCoordinates(using: sunEclipticCoordinates) - // Equatorial to Horizon - let sunHorizonCoordinates: HorizonCoordinates = calculateSunHorizonCoordinates(using: lstDecimal) - - return .init(altitude: sunHorizonCoordinates.altitude, azimuth: sunHorizonCoordinates.azimuth) - - // TODO: Move behavior intoEquatorialCoordinates, refactor to Object - func calculateSunHorizonCoordinates(using lstDecimal: Double) -> HorizonCoordinates { - sunEquatorialCoordinates.equatorial2Horizon(lstDecimal: lstDecimal, latitude: latitude) ?? .init(altitude: .zero, azimuth: .zero) - } - } - - /* - public mutating func getSunHorizonCoordinatesFrom(date: Date) -> HorizonCoordinates { - let lstDecimal = calculateLSTDecimal(using: date) - let sunEclipticLongitude: Angle = calculateSunEclipticLongitude(using: date) - let sunHorizonCoordinates = sunCoordinates.getSunHorizonCoordinatesGiven( - sunEclipticLongitude: sunEclipticLongitude, - lstDecimal: lstDecimal, - latitude: latitude - ) - - return sunHorizonCoordinates - } - */ - // MARK: - Get Month Events // TODO: Move magic numbers into Enum cases or subclasses From 03ad789752b7b02b8217159aa36e32cba4c38527 Mon Sep 17 00:00:00 2001 From: Ignas Sireikis Date: Mon, 22 Sep 2025 12:01:37 -0500 Subject: [PATCH 77/91] Extracted updateCoordinates method. --- Sources/SunKit/Sun/Sun.swift | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/Sources/SunKit/Sun/Sun.swift b/Sources/SunKit/Sun/Sun.swift index e742110..d04073d 100644 --- a/Sources/SunKit/Sun/Sun.swift +++ b/Sources/SunKit/Sun/Sun.swift @@ -435,6 +435,14 @@ public struct Sun: Identifiable, Sendable { let lstDecimal = sunCoordinates.calculateLSTDecimal(using: self.date, longitude: self.longitude) let sunEclipticLongitude: Angle = sunCoordinates.calculateSunEclipticLongitude(using: self.date) + updateCoordinates(sunEclipticLongitude: sunEclipticLongitude, lstDecimal: lstDecimal, latitude: self.latitude) + } + + private mutating func updateCoordinates( + sunEclipticLongitude: Angle, + lstDecimal: Double, + latitude: Angle + ) { sunEclipticCoordinates = calculateSunEclipticCoordinates(using: sunEclipticLongitude) // Ecliptic to Equatorial sunEquatorialCoordinates = calculateSunEquatorialCoordinates(using: sunEclipticCoordinates) From acbe3069c2fbdc29ffbac3e2c9c179f5d3ebdd39 Mon Sep 17 00:00:00 2001 From: Ignas Sireikis Date: Mon, 22 Sep 2025 12:03:09 -0500 Subject: [PATCH 78/91] Parameterized updateSunCoordinates method. --- Sources/SunKit/Sun/Sun.swift | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/Sources/SunKit/Sun/Sun.swift b/Sources/SunKit/Sun/Sun.swift index d04073d..e31d335 100644 --- a/Sources/SunKit/Sun/Sun.swift +++ b/Sources/SunKit/Sun/Sun.swift @@ -397,7 +397,7 @@ public struct Sun: Identifiable, Sendable { /// /// - Parameter needToComputeAgainSunEvents: True if Sunrise,Sunset and all the others daily sun events have to be computed. private mutating func refresh(needToComputeSunEvents: Bool = true) { - updateSunCoordinates() + updateSunCoordinates(using: self.date, longitude: self.longitude, latitude: self.latitude) if (needToComputeSunEvents) { self.astronomicalDawn = getAstronomicalDawn() ?? Date() @@ -431,11 +431,11 @@ public struct Sun: Identifiable, Sendable { // TODO: SunCoordinates implicitly depends on self.date and self.longitude (location) /// Updates Horizon coordinates, Ecliptic coordinates and Equatorial coordinates of the Sun - private mutating func updateSunCoordinates() { - let lstDecimal = sunCoordinates.calculateLSTDecimal(using: self.date, longitude: self.longitude) - let sunEclipticLongitude: Angle = sunCoordinates.calculateSunEclipticLongitude(using: self.date) + private mutating func updateSunCoordinates(using date: Date, longitude: Angle, latitude: Angle) { + let lstDecimal = sunCoordinates.calculateLSTDecimal(using: date, longitude: longitude) + let sunEclipticLongitude: Angle = sunCoordinates.calculateSunEclipticLongitude(using: date) - updateCoordinates(sunEclipticLongitude: sunEclipticLongitude, lstDecimal: lstDecimal, latitude: self.latitude) + updateCoordinates(sunEclipticLongitude: sunEclipticLongitude, lstDecimal: lstDecimal, latitude: latitude) } private mutating func updateCoordinates( From 83e68dc05a7545a4946a85c9a9e3214454c323b1 Mon Sep 17 00:00:00 2001 From: Ignas Sireikis Date: Mon, 22 Sep 2025 12:08:32 -0500 Subject: [PATCH 79/91] Moved calculateSunEclipticCoordinates into SunCoordinates. --- Sources/SunKit/Sun/Sun.swift | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/Sources/SunKit/Sun/Sun.swift b/Sources/SunKit/Sun/Sun.swift index e31d335..636a953 100644 --- a/Sources/SunKit/Sun/Sun.swift +++ b/Sources/SunKit/Sun/Sun.swift @@ -443,7 +443,7 @@ public struct Sun: Identifiable, Sendable { lstDecimal: Double, latitude: Angle ) { - sunEclipticCoordinates = calculateSunEclipticCoordinates(using: sunEclipticLongitude) + sunEclipticCoordinates = sunCoordinates.calculateSunEclipticCoordinates(using: sunEclipticLongitude) // Ecliptic to Equatorial sunEquatorialCoordinates = calculateSunEquatorialCoordinates(using: sunEclipticCoordinates) // Equatorial to Horizon @@ -469,12 +469,6 @@ public struct Sun: Identifiable, Sendable { } */ - // TODO: Delete - // TODO: Move behavior into EclipticCoordinates, refactor to Object - private func calculateSunEclipticCoordinates(using sunEclipticLongitude: Angle) -> EclipticCoordinates { - .init(eclipticLatitude: .zero, eclipticLongitude: sunEclipticLongitude) - } - // TODO: Move behavior into EclipticCoordinates private func calculateSunEquatorialCoordinates(using sunEclipticCoordinates: EclipticCoordinates) -> EquatorialCoordinates { sunEclipticCoordinates.ecliptic2Equatorial() @@ -485,7 +479,7 @@ public struct Sun: Identifiable, Sendable { let lstDecimal = sunCoordinates.calculateLSTDecimal(using: self.date, longitude: self.longitude) let sunEclipticLongitude: Angle = sunCoordinates.calculateSunEclipticLongitude(using: date) - let sunEclipticCoordinates: EclipticCoordinates = calculateSunEclipticCoordinates(using: sunEclipticLongitude) + let sunEclipticCoordinates: EclipticCoordinates = sunCoordinates.calculateSunEclipticCoordinates(using: sunEclipticLongitude) // Ecliptic to Equatorial var sunEquatorialCoordinates: EquatorialCoordinates = calculateSunEquatorialCoordinates(using: sunEclipticCoordinates) // Equatorial to Horizon From c02b12280d0b10ea12ed1ba1ab4074b8b7c05752 Mon Sep 17 00:00:00 2001 From: Ignas Sireikis Date: Mon, 22 Sep 2025 12:09:46 -0500 Subject: [PATCH 80/91] Moved calculateSunEquatorialCoordinates into SunCoordinates. --- Sources/SunKit/Sun/Sun.swift | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/Sources/SunKit/Sun/Sun.swift b/Sources/SunKit/Sun/Sun.swift index 636a953..2ef28cb 100644 --- a/Sources/SunKit/Sun/Sun.swift +++ b/Sources/SunKit/Sun/Sun.swift @@ -445,7 +445,7 @@ public struct Sun: Identifiable, Sendable { ) { sunEclipticCoordinates = sunCoordinates.calculateSunEclipticCoordinates(using: sunEclipticLongitude) // Ecliptic to Equatorial - sunEquatorialCoordinates = calculateSunEquatorialCoordinates(using: sunEclipticCoordinates) + sunEquatorialCoordinates = sunCoordinates.calculateSunEquatorialCoordinates(using: sunEclipticCoordinates) // Equatorial to Horizon sunHorizonCoordinates = calculateSunHorizonCoordinates(using: lstDecimal) @@ -469,11 +469,6 @@ public struct Sun: Identifiable, Sendable { } */ - // TODO: Move behavior into EclipticCoordinates - private func calculateSunEquatorialCoordinates(using sunEclipticCoordinates: EclipticCoordinates) -> EquatorialCoordinates { - sunEclipticCoordinates.ecliptic2Equatorial() - } - // TODO: Extract functions to collapse into updateSunCoordinates public func getSunHorizonCoordinatesFrom(date: Date) -> HorizonCoordinates { let lstDecimal = sunCoordinates.calculateLSTDecimal(using: self.date, longitude: self.longitude) @@ -481,7 +476,7 @@ public struct Sun: Identifiable, Sendable { let sunEclipticCoordinates: EclipticCoordinates = sunCoordinates.calculateSunEclipticCoordinates(using: sunEclipticLongitude) // Ecliptic to Equatorial - var sunEquatorialCoordinates: EquatorialCoordinates = calculateSunEquatorialCoordinates(using: sunEclipticCoordinates) + var sunEquatorialCoordinates: EquatorialCoordinates = sunCoordinates.calculateSunEquatorialCoordinates(using: sunEclipticCoordinates) // Equatorial to Horizon let sunHorizonCoordinates: HorizonCoordinates = calculateSunHorizonCoordinates(using: lstDecimal) From 17a5018ac570d88d5b93a5115c79067f98544364 Mon Sep 17 00:00:00 2001 From: Ignas Sireikis Date: Mon, 22 Sep 2025 12:14:51 -0500 Subject: [PATCH 81/91] Extracted nested function calculateSunHorizonCoordinates. --- Sources/SunKit/Sun/Sun.swift | 21 ++++++++++++++++----- 1 file changed, 16 insertions(+), 5 deletions(-) diff --git a/Sources/SunKit/Sun/Sun.swift b/Sources/SunKit/Sun/Sun.swift index 2ef28cb..2195669 100644 --- a/Sources/SunKit/Sun/Sun.swift +++ b/Sources/SunKit/Sun/Sun.swift @@ -447,12 +447,23 @@ public struct Sun: Identifiable, Sendable { // Ecliptic to Equatorial sunEquatorialCoordinates = sunCoordinates.calculateSunEquatorialCoordinates(using: sunEclipticCoordinates) // Equatorial to Horizon - sunHorizonCoordinates = calculateSunHorizonCoordinates(using: lstDecimal) + sunHorizonCoordinates = calculateSunHorizonCoordinates( + using: sunEquatorialCoordinates, + lstDecimal: lstDecimal, + latitude: latitude + ) + } + + // TODO: Move behavior into EquatorialCoordinates, refactor to Object + private func calculateSunHorizonCoordinates( + using sunEquatorialCoordinates: EquatorialCoordinates, + lstDecimal: Double, + latitude: Angle + ) -> HorizonCoordinates { + var _sunEquatorialCoordinates = sunEquatorialCoordinates + let horizonCoordinates = _sunEquatorialCoordinates.equatorial2Horizon(lstDecimal: lstDecimal, latitude: latitude) - // TODO: Move behavior into EquatorialCoordinates, refactor to Object - func calculateSunHorizonCoordinates(using lstDecimal: Double) -> HorizonCoordinates { - sunEquatorialCoordinates.equatorial2Horizon(lstDecimal: lstDecimal, latitude: latitude) ?? .init(altitude: .zero, azimuth: .zero) - } + return horizonCoordinates ?? .init(altitude: .zero, azimuth: .zero) } /* From 1b5478c704a7b530cde816569fe35ebbe996d561 Mon Sep 17 00:00:00 2001 From: Ignas Sireikis Date: Mon, 22 Sep 2025 12:18:39 -0500 Subject: [PATCH 82/91] Updated getSunHorizonCoordinatesFrom to use extracted calculateSunHorizonCoordinates function. --- Sources/SunKit/Sun/Sun.swift | 17 +++++++---------- 1 file changed, 7 insertions(+), 10 deletions(-) diff --git a/Sources/SunKit/Sun/Sun.swift b/Sources/SunKit/Sun/Sun.swift index 2195669..15334ee 100644 --- a/Sources/SunKit/Sun/Sun.swift +++ b/Sources/SunKit/Sun/Sun.swift @@ -428,8 +428,6 @@ public struct Sun: Identifiable, Sendable { self.decemberSolstice = getDecemberSolstice() ?? Date() } - // TODO: SunCoordinates implicitly depends on self.date and self.longitude (location) - /// Updates Horizon coordinates, Ecliptic coordinates and Equatorial coordinates of the Sun private mutating func updateSunCoordinates(using date: Date, longitude: Angle, latitude: Angle) { let lstDecimal = sunCoordinates.calculateLSTDecimal(using: date, longitude: longitude) @@ -482,21 +480,20 @@ public struct Sun: Identifiable, Sendable { // TODO: Extract functions to collapse into updateSunCoordinates public func getSunHorizonCoordinatesFrom(date: Date) -> HorizonCoordinates { - let lstDecimal = sunCoordinates.calculateLSTDecimal(using: self.date, longitude: self.longitude) + let lstDecimal = sunCoordinates.calculateLSTDecimal(using: date, longitude: self.longitude) let sunEclipticLongitude: Angle = sunCoordinates.calculateSunEclipticLongitude(using: date) let sunEclipticCoordinates: EclipticCoordinates = sunCoordinates.calculateSunEclipticCoordinates(using: sunEclipticLongitude) // Ecliptic to Equatorial - var sunEquatorialCoordinates: EquatorialCoordinates = sunCoordinates.calculateSunEquatorialCoordinates(using: sunEclipticCoordinates) + let sunEquatorialCoordinates: EquatorialCoordinates = sunCoordinates.calculateSunEquatorialCoordinates(using: sunEclipticCoordinates) // Equatorial to Horizon - let sunHorizonCoordinates: HorizonCoordinates = calculateSunHorizonCoordinates(using: lstDecimal) + let sunHorizonCoordinates: HorizonCoordinates = calculateSunHorizonCoordinates( + using: sunEquatorialCoordinates, + lstDecimal: lstDecimal, + latitude: latitude + ) return .init(altitude: sunHorizonCoordinates.altitude, azimuth: sunHorizonCoordinates.azimuth) - - // TODO: Move behavior intoEquatorialCoordinates, refactor to Object - func calculateSunHorizonCoordinates(using lstDecimal: Double) -> HorizonCoordinates { - sunEquatorialCoordinates.equatorial2Horizon(lstDecimal: lstDecimal, latitude: latitude) ?? .init(altitude: .zero, azimuth: .zero) - } } /* From ea7df77d5af4351778e39c3c45a464afdee6e221 Mon Sep 17 00:00:00 2001 From: Ignas Sireikis Date: Mon, 22 Sep 2025 12:23:52 -0500 Subject: [PATCH 83/91] Extracted calculateSunHorizonCoordinates into SunCoordinates. --- .../Sun Coordinates/SunCoordinates.swift | 9 ++-- Sources/SunKit/Sun/Sun.swift | 44 +------------------ 2 files changed, 6 insertions(+), 47 deletions(-) diff --git a/Sources/SunKit/Sun Coordinates/SunCoordinates.swift b/Sources/SunKit/Sun Coordinates/SunCoordinates.swift index 3f74650..57f5758 100644 --- a/Sources/SunKit/Sun Coordinates/SunCoordinates.swift +++ b/Sources/SunKit/Sun Coordinates/SunCoordinates.swift @@ -114,16 +114,15 @@ internal struct SunCoordinates: Sendable { // ) } - // TODO: Remove mutating func, need to refactor EquatorialCoordinates - internal mutating func calculateSunHorizonCoordinates( + internal func calculateSunHorizonCoordinates( using sunEquatorialCoordinates: EquatorialCoordinates, lstDecimal: Double, latitude: Angle ) -> HorizonCoordinates { -// let horizonCoordinates = sunEquatorialCoordinates.getHorizonCoordinates(lstDecimal: lstDecimal, latitude: latitude) + var _sunEquatorialCoordinates = sunEquatorialCoordinates + let horizonCoordinates = _sunEquatorialCoordinates.equatorial2Horizon(lstDecimal: lstDecimal, latitude: latitude) -// return horizonCoordinates ?? .init(altitude: .zero, azimuth: .zero) - return .init(altitude: .zero, azimuth: .zero) + return horizonCoordinates ?? .init(altitude: .zero, azimuth: .zero) } // MARK: - Helpers diff --git a/Sources/SunKit/Sun/Sun.swift b/Sources/SunKit/Sun/Sun.swift index 15334ee..301cb90 100644 --- a/Sources/SunKit/Sun/Sun.swift +++ b/Sources/SunKit/Sun/Sun.swift @@ -445,39 +445,13 @@ public struct Sun: Identifiable, Sendable { // Ecliptic to Equatorial sunEquatorialCoordinates = sunCoordinates.calculateSunEquatorialCoordinates(using: sunEclipticCoordinates) // Equatorial to Horizon - sunHorizonCoordinates = calculateSunHorizonCoordinates( + sunHorizonCoordinates = sunCoordinates.calculateSunHorizonCoordinates( using: sunEquatorialCoordinates, lstDecimal: lstDecimal, latitude: latitude ) } - // TODO: Move behavior into EquatorialCoordinates, refactor to Object - private func calculateSunHorizonCoordinates( - using sunEquatorialCoordinates: EquatorialCoordinates, - lstDecimal: Double, - latitude: Angle - ) -> HorizonCoordinates { - var _sunEquatorialCoordinates = sunEquatorialCoordinates - let horizonCoordinates = _sunEquatorialCoordinates.equatorial2Horizon(lstDecimal: lstDecimal, latitude: latitude) - - return horizonCoordinates ?? .init(altitude: .zero, azimuth: .zero) - } - - /* - /// Updates Horizon coordinates, Ecliptic coordinates and Equatorial coordinates of the Sun - private mutating func updateSunCoordinates() { - let lstDecimal = calculateLSTDecimal(using: self.date) - let sunEclipticLongitude: Angle = calculateSunEclipticLongitude(using: self.date) - - sunCoordinates.setCoordinates( - sunEclipticLongitude: sunEclipticLongitude, - lstDecimal: lstDecimal, - latitude: latitude - ) - } - */ - // TODO: Extract functions to collapse into updateSunCoordinates public func getSunHorizonCoordinatesFrom(date: Date) -> HorizonCoordinates { let lstDecimal = sunCoordinates.calculateLSTDecimal(using: date, longitude: self.longitude) @@ -487,7 +461,7 @@ public struct Sun: Identifiable, Sendable { // Ecliptic to Equatorial let sunEquatorialCoordinates: EquatorialCoordinates = sunCoordinates.calculateSunEquatorialCoordinates(using: sunEclipticCoordinates) // Equatorial to Horizon - let sunHorizonCoordinates: HorizonCoordinates = calculateSunHorizonCoordinates( + let sunHorizonCoordinates: HorizonCoordinates = sunCoordinates.calculateSunHorizonCoordinates( using: sunEquatorialCoordinates, lstDecimal: lstDecimal, latitude: latitude @@ -496,20 +470,6 @@ public struct Sun: Identifiable, Sendable { return .init(altitude: sunHorizonCoordinates.altitude, azimuth: sunHorizonCoordinates.azimuth) } - /* - public mutating func getSunHorizonCoordinatesFrom(date: Date) -> HorizonCoordinates { - let lstDecimal = calculateLSTDecimal(using: date) - let sunEclipticLongitude: Angle = calculateSunEclipticLongitude(using: date) - let sunHorizonCoordinates = sunCoordinates.getSunHorizonCoordinatesGiven( - sunEclipticLongitude: sunEclipticLongitude, - lstDecimal: lstDecimal, - latitude: latitude - ) - - return sunHorizonCoordinates - } - */ - // MARK: - Get Day Events /// Astronomical Dawn is when the Sun reaches -18 degrees of elevation. From 7f9cfe52e6a5b85f0f192ac2e73f560c6786b2ad Mon Sep 17 00:00:00 2001 From: Ignas Sireikis Date: Mon, 22 Sep 2025 12:27:48 -0500 Subject: [PATCH 84/91] Renamed function in SunCoordinates. Uncommented implementation of setSunHorizonCoordinates. --- .../SunKit/Sun Coordinates/SunCoordinates.swift | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/Sources/SunKit/Sun Coordinates/SunCoordinates.swift b/Sources/SunKit/Sun Coordinates/SunCoordinates.swift index 57f5758..67c3859 100644 --- a/Sources/SunKit/Sun Coordinates/SunCoordinates.swift +++ b/Sources/SunKit/Sun Coordinates/SunCoordinates.swift @@ -63,9 +63,8 @@ internal struct SunCoordinates: Sendable { setSunHorizonCoordinates(using: lstDecimal, latitude: latitude) } - // TODO: Remove mutating func, need to refactor EquatorialCoordinates - internal mutating func getSunHorizonCoordinatesGiven( - sunEclipticLongitude: Angle, + internal func getSunHorizonCoordinates( + given sunEclipticLongitude: Angle, lstDecimal: Double, latitude: Angle ) -> HorizonCoordinates { @@ -107,11 +106,11 @@ internal struct SunCoordinates: Sendable { using lstDecimal: Double, latitude: Angle ) { -// self.horizonCoordinates = calculateSunHorizonCoordinates( -// using: equatorialCoordinates, -// lstDecimal: lstDecimal, -// latitude: latitude -// ) + self.horizonCoordinates = calculateSunHorizonCoordinates( + using: equatorialCoordinates, + lstDecimal: lstDecimal, + latitude: latitude + ) } internal func calculateSunHorizonCoordinates( From 0b58d6d5a6ac1d91b9cd7ef372a8c33c280eccae Mon Sep 17 00:00:00 2001 From: Ignas Sireikis Date: Mon, 22 Sep 2025 12:31:03 -0500 Subject: [PATCH 85/91] Replaced duplicated code in getSunHorizonCoordinatesFrom with getSunHorizonCoordinates in SunCoordinates. --- Sources/SunKit/Sun/Sun.swift | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/Sources/SunKit/Sun/Sun.swift b/Sources/SunKit/Sun/Sun.swift index 301cb90..f77a9e9 100644 --- a/Sources/SunKit/Sun/Sun.swift +++ b/Sources/SunKit/Sun/Sun.swift @@ -452,19 +452,14 @@ public struct Sun: Identifiable, Sendable { ) } - // TODO: Extract functions to collapse into updateSunCoordinates public func getSunHorizonCoordinatesFrom(date: Date) -> HorizonCoordinates { let lstDecimal = sunCoordinates.calculateLSTDecimal(using: date, longitude: self.longitude) let sunEclipticLongitude: Angle = sunCoordinates.calculateSunEclipticLongitude(using: date) - let sunEclipticCoordinates: EclipticCoordinates = sunCoordinates.calculateSunEclipticCoordinates(using: sunEclipticLongitude) - // Ecliptic to Equatorial - let sunEquatorialCoordinates: EquatorialCoordinates = sunCoordinates.calculateSunEquatorialCoordinates(using: sunEclipticCoordinates) - // Equatorial to Horizon - let sunHorizonCoordinates: HorizonCoordinates = sunCoordinates.calculateSunHorizonCoordinates( - using: sunEquatorialCoordinates, + let sunHorizonCoordinates = sunCoordinates.getSunHorizonCoordinates( + given: sunEclipticLongitude, lstDecimal: lstDecimal, - latitude: latitude + latitude: self.latitude ) return .init(altitude: sunHorizonCoordinates.altitude, azimuth: sunHorizonCoordinates.azimuth) From 91952249f3c10fb0bbe389551d3a882fcb32d962 Mon Sep 17 00:00:00 2001 From: Ignas Sireikis Date: Mon, 22 Sep 2025 12:49:26 -0500 Subject: [PATCH 86/91] Extracted function getSunHorizonCoordinates into SunCoordinates. --- .../SunKit/Sun Coordinates/SunCoordinates.swift | 16 ++++++++++++++++ Sources/SunKit/Sun/Sun.swift | 11 ++++------- 2 files changed, 20 insertions(+), 7 deletions(-) diff --git a/Sources/SunKit/Sun Coordinates/SunCoordinates.swift b/Sources/SunKit/Sun Coordinates/SunCoordinates.swift index 67c3859..999224f 100644 --- a/Sources/SunKit/Sun Coordinates/SunCoordinates.swift +++ b/Sources/SunKit/Sun Coordinates/SunCoordinates.swift @@ -64,6 +64,22 @@ internal struct SunCoordinates: Sendable { } internal func getSunHorizonCoordinates( + given date: Date, + longitude: Angle, + latitude: Angle + ) -> HorizonCoordinates { + let lstDecimal = calculateLSTDecimal(using: date, longitude: longitude) + let sunEclipticLongitude: Angle = calculateSunEclipticLongitude(using: date) + let sunHorizonCoordinates = getSunHorizonCoordinates( + given: sunEclipticLongitude, + lstDecimal: lstDecimal, + latitude: latitude + ) + + return .init(altitude: sunHorizonCoordinates.altitude, azimuth: sunHorizonCoordinates.azimuth) + } + + private func getSunHorizonCoordinates( given sunEclipticLongitude: Angle, lstDecimal: Double, latitude: Angle diff --git a/Sources/SunKit/Sun/Sun.swift b/Sources/SunKit/Sun/Sun.swift index f77a9e9..58a1217 100644 --- a/Sources/SunKit/Sun/Sun.swift +++ b/Sources/SunKit/Sun/Sun.swift @@ -88,7 +88,7 @@ public struct Sun: Identifiable, Sendable { public private(set) var sunsetAzimuth: Double = 0 /// The object backing the public interface for Sun coordinates. - private let sunCoordinates: SunCoordinates = .init() + private var sunCoordinates: SunCoordinates = .init() public var azimuth: Angle { sunHorizonCoordinates.azimuth @@ -453,16 +453,13 @@ public struct Sun: Identifiable, Sendable { } public func getSunHorizonCoordinatesFrom(date: Date) -> HorizonCoordinates { - let lstDecimal = sunCoordinates.calculateLSTDecimal(using: date, longitude: self.longitude) - let sunEclipticLongitude: Angle = sunCoordinates.calculateSunEclipticLongitude(using: date) - let sunHorizonCoordinates = sunCoordinates.getSunHorizonCoordinates( - given: sunEclipticLongitude, - lstDecimal: lstDecimal, + given: date, + longitude: self.longitude, latitude: self.latitude ) - return .init(altitude: sunHorizonCoordinates.altitude, azimuth: sunHorizonCoordinates.azimuth) + return sunHorizonCoordinates } // MARK: - Get Day Events From 6b416f470c2626b374e57afe5d5ec19d08e056c0 Mon Sep 17 00:00:00 2001 From: Ignas Sireikis Date: Mon, 22 Sep 2025 12:51:26 -0500 Subject: [PATCH 87/91] Adjusted privacy levels in SunCoordinates. --- Sources/SunKit/Sun Coordinates/SunCoordinates.swift | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Sources/SunKit/Sun Coordinates/SunCoordinates.swift b/Sources/SunKit/Sun Coordinates/SunCoordinates.swift index 999224f..f25c5e6 100644 --- a/Sources/SunKit/Sun Coordinates/SunCoordinates.swift +++ b/Sources/SunKit/Sun Coordinates/SunCoordinates.swift @@ -97,7 +97,7 @@ internal struct SunCoordinates: Sendable { // MARK: Ecliptic Coordinates - internal mutating func setSunEclipticCoordinates(using sunEclipticLongitude: Angle) { + private mutating func setSunEclipticCoordinates(using sunEclipticLongitude: Angle) { self.eclipticCoordinates = calculateSunEclipticCoordinates(using: sunEclipticLongitude) } @@ -107,7 +107,7 @@ internal struct SunCoordinates: Sendable { // MARK: Equatorial Coordinates - internal mutating func setSunEquatorialCoordinates(using sunEclipticCoordinates: EclipticCoordinates) { + private mutating func setSunEquatorialCoordinates(using sunEclipticCoordinates: EclipticCoordinates) { self.equatorialCoordinates = calculateSunEquatorialCoordinates(using: sunEclipticCoordinates) } @@ -118,7 +118,7 @@ internal struct SunCoordinates: Sendable { // MARK: Horizon Coordinates - internal mutating func setSunHorizonCoordinates( + private mutating func setSunHorizonCoordinates( using lstDecimal: Double, latitude: Angle ) { From bcc1716536c206df33cba69a5ff7794413cb904f Mon Sep 17 00:00:00 2001 From: Ignas Sireikis Date: Mon, 22 Sep 2025 12:57:55 -0500 Subject: [PATCH 88/91] Changed the coordinate variables to be backed by the SunCoordinates object. Now using the setCoordinates method on SunCoordinates. --- Sources/SunKit/Sun/Sun.swift | 55 +++++++++--------------------------- 1 file changed, 13 insertions(+), 42 deletions(-) diff --git a/Sources/SunKit/Sun/Sun.swift b/Sources/SunKit/Sun/Sun.swift index 58a1217..f213932 100644 --- a/Sources/SunKit/Sun/Sun.swift +++ b/Sources/SunKit/Sun/Sun.swift @@ -91,38 +91,21 @@ public struct Sun: Identifiable, Sendable { private var sunCoordinates: SunCoordinates = .init() public var azimuth: Angle { - sunHorizonCoordinates.azimuth + sunCoordinates.azimuth } public var altitude: Angle { - sunHorizonCoordinates.altitude + sunCoordinates.altitude } - // TODO: Use SunCoordinates as backing object for these Coordinates. - private var sunHorizonCoordinates: HorizonCoordinates = .init(altitude: .zero, azimuth: .zero) - public private(set) var sunEquatorialCoordinates: EquatorialCoordinates = .init(declination: .zero) - public private(set) var sunEclipticCoordinates: EclipticCoordinates = .init(eclipticLatitude: .zero, eclipticLongitude: .zero) - - /* - public var azimuth: Angle { - sunCoordinates.azimuth - } - - public var altitude: Angle { - sunCoordinates.altitude - } - - private var sunHorizonCoordinates: HorizonCoordinates { - sunCoordinates.horizonCoordinates - } - - public var sunEquatorialCoordinates: EquatorialCoordinates { - sunCoordinates.equatorialCoordinates - } - - public var sunEclipticCoordinates: EclipticCoordinates { - sunCoordinates.eclipticCoordinates - } - */ + + public var sunEquatorialCoordinates: EquatorialCoordinates { + sunCoordinates.equatorialCoordinates + } + + public var sunEclipticCoordinates: EclipticCoordinates { + sunCoordinates.eclipticCoordinates + } + /*-------------------------------------------------------------------- Sun Events during the year *-------------------------------------------------------------------*/ @@ -433,20 +416,8 @@ public struct Sun: Identifiable, Sendable { let lstDecimal = sunCoordinates.calculateLSTDecimal(using: date, longitude: longitude) let sunEclipticLongitude: Angle = sunCoordinates.calculateSunEclipticLongitude(using: date) - updateCoordinates(sunEclipticLongitude: sunEclipticLongitude, lstDecimal: lstDecimal, latitude: latitude) - } - - private mutating func updateCoordinates( - sunEclipticLongitude: Angle, - lstDecimal: Double, - latitude: Angle - ) { - sunEclipticCoordinates = sunCoordinates.calculateSunEclipticCoordinates(using: sunEclipticLongitude) - // Ecliptic to Equatorial - sunEquatorialCoordinates = sunCoordinates.calculateSunEquatorialCoordinates(using: sunEclipticCoordinates) - // Equatorial to Horizon - sunHorizonCoordinates = sunCoordinates.calculateSunHorizonCoordinates( - using: sunEquatorialCoordinates, + sunCoordinates.setCoordinates( + sunEclipticLongitude: sunEclipticLongitude, lstDecimal: lstDecimal, latitude: latitude ) From e545e735c0118ef050af23cb50651522d2c72c16 Mon Sep 17 00:00:00 2001 From: Ignas Sireikis Date: Mon, 22 Sep 2025 13:00:10 -0500 Subject: [PATCH 89/91] Extracted setSunCoordinates function into SunCoordinates. --- Sources/SunKit/Sun Coordinates/SunCoordinates.swift | 13 ++++++++++++- Sources/SunKit/Sun/Sun.swift | 9 +-------- 2 files changed, 13 insertions(+), 9 deletions(-) diff --git a/Sources/SunKit/Sun Coordinates/SunCoordinates.swift b/Sources/SunKit/Sun Coordinates/SunCoordinates.swift index f25c5e6..8014ca3 100644 --- a/Sources/SunKit/Sun Coordinates/SunCoordinates.swift +++ b/Sources/SunKit/Sun Coordinates/SunCoordinates.swift @@ -53,7 +53,18 @@ internal struct SunCoordinates: Sendable { // MARK: - Coordinates - internal mutating func setCoordinates( + internal mutating func setSunCoordinates(using date: Date, longitude: Angle, latitude: Angle) { + let lstDecimal = calculateLSTDecimal(using: date, longitude: longitude) + let sunEclipticLongitude: Angle = calculateSunEclipticLongitude(using: date) + + setCoordinates( + sunEclipticLongitude: sunEclipticLongitude, + lstDecimal: lstDecimal, + latitude: latitude + ) + } + + private mutating func setCoordinates( sunEclipticLongitude: Angle, lstDecimal: Double, latitude: Angle diff --git a/Sources/SunKit/Sun/Sun.swift b/Sources/SunKit/Sun/Sun.swift index f213932..5523de4 100644 --- a/Sources/SunKit/Sun/Sun.swift +++ b/Sources/SunKit/Sun/Sun.swift @@ -413,14 +413,7 @@ public struct Sun: Identifiable, Sendable { /// Updates Horizon coordinates, Ecliptic coordinates and Equatorial coordinates of the Sun private mutating func updateSunCoordinates(using date: Date, longitude: Angle, latitude: Angle) { - let lstDecimal = sunCoordinates.calculateLSTDecimal(using: date, longitude: longitude) - let sunEclipticLongitude: Angle = sunCoordinates.calculateSunEclipticLongitude(using: date) - - sunCoordinates.setCoordinates( - sunEclipticLongitude: sunEclipticLongitude, - lstDecimal: lstDecimal, - latitude: latitude - ) + sunCoordinates.setSunCoordinates(using: date, longitude: longitude, latitude: latitude) } public func getSunHorizonCoordinatesFrom(date: Date) -> HorizonCoordinates { From cacd9327b398416a765b5080cb6507daa5954daf Mon Sep 17 00:00:00 2001 From: Ignas Sireikis Date: Mon, 22 Sep 2025 13:03:10 -0500 Subject: [PATCH 90/91] Removed TODO comment. --- Sources/SunKit/Sun Coordinates/SunCoordinates.swift | 1 - 1 file changed, 1 deletion(-) diff --git a/Sources/SunKit/Sun Coordinates/SunCoordinates.swift b/Sources/SunKit/Sun Coordinates/SunCoordinates.swift index 8014ca3..dc5be5f 100644 --- a/Sources/SunKit/Sun Coordinates/SunCoordinates.swift +++ b/Sources/SunKit/Sun Coordinates/SunCoordinates.swift @@ -122,7 +122,6 @@ internal struct SunCoordinates: Sendable { self.equatorialCoordinates = calculateSunEquatorialCoordinates(using: sunEclipticCoordinates) } - // TODO: Move behavior into EclipticCoordinates internal func calculateSunEquatorialCoordinates(using sunEclipticCoordinates: EclipticCoordinates) -> EquatorialCoordinates { sunEclipticCoordinates.ecliptic2Equatorial() } From b15550e9636f909781d9d1485d9e1695652e70dd Mon Sep 17 00:00:00 2001 From: Ignas Sireikis Date: Mon, 22 Sep 2025 13:07:10 -0500 Subject: [PATCH 91/91] Removed TODO comment. --- Sources/SunKit/Sun/Sun.swift | 2 -- 1 file changed, 2 deletions(-) diff --git a/Sources/SunKit/Sun/Sun.swift b/Sources/SunKit/Sun/Sun.swift index 5523de4..9b3eb0a 100644 --- a/Sources/SunKit/Sun/Sun.swift +++ b/Sources/SunKit/Sun/Sun.swift @@ -664,8 +664,6 @@ public struct Sun: Identifiable, Sendable { // MARK: - Get Month Events - // TODO: Move magic numbers into Enum cases or subclasses - private func getMarchEquinox() -> Date? { let year = Double(calendar.component(.year, from: self.date)) let t: Double = year / 1000