Skip to content

Commit

Permalink
initial
Browse files Browse the repository at this point in the history
  • Loading branch information
tib committed Feb 11, 2019
0 parents commit ae93767
Show file tree
Hide file tree
Showing 13 changed files with 549 additions and 0 deletions.
4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
.DS_Store
/.build
/Packages
/*.xcodeproj
5 changes: 5 additions & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
FROM swift
WORKDIR /app
COPY . ./
CMD swift package clean
CMD swift test --parallel
17 changes: 17 additions & 0 deletions LICENSE
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
Version 2, December 2004

Copyright (C) 2018-2019 Binary Birds

Authors:

Tibor Bodecs <mail.tib@gmail.com>

Everyone is permitted to copy and distribute verbatim or modified
copies of this license document, and changing it is allowed as long
as the name is changed.

DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION

0. You just DO WHAT THE FUCK YOU WANT TO.
21 changes: 21 additions & 0 deletions Package.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
// swift-tools-version:4.2
import PackageDescription

let package = Package(
name: "Sunlight",
products: [
.library(name: "Sunlight", targets: ["Sunlight"]),
],
dependencies: [

],
targets: [
.target(
name: "Sunlight",
dependencies: [],
path: "./Sources/"),
.testTarget(
name: "SunlightTests",
dependencies: ["Sunlight"]),
]
)
82 changes: 82 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
# Sunlight (☀️)

Calculate dawn, dusk, golden and blue hour times by using various algorithms.



### Twilight Types

![](https://www.photopills.com/sites/default/files/tutorials/2014/twilights-magic-hours.jpg)



- Civil Twilight
- Nautical Twilight
- Astronomical Twilight
- Dawn (official)
- Dusk (official)
- The Golden Hour
- The Blue Hour



## Usage

Some examples:

```swift
import Sunlight

let sunlight = SunlightCalculator(latitude: 47.49801, longitude: 19.03991)

let officialDawn = sunlight.calculate(.dawn, twilight: .official)
let officialDusk = sunlight.calculate(.dusk, twilight: .official)

let civilDawn = sunlight.calculate(.dawn, twilight: .civil)
let civilDusk = sunlight.calculate(.dusk, twilight: .civil)

let astronomicalDawn = sunlight.calculate(.dawn, twilight: .astronomical)
let astronomicalDusk = sunlight.calculate(.dusk, twilight: .astronomical)

let nauticalDawn = sunlight.calculate(.dawn, twilight: .nautical)
let nauticalDusk = sunlight.calculate(.dusk, twilight: .nautical)

let blueHourStart = sunlight.calculate(.dawn, twilight: .custom(-8))
let blueHourEndGoldenHourStart = sunlight.calculate(.dusk, twilight: .custom(-4))
let goldenHourEnd = sunlight.calculate(.dusk, twilight: .custom(6))

```



## Install

Just use the [Swift Package Manager](https://theswiftdev.com/2017/11/09/swift-package-manager-tutorial/) as usual:

```swift
.package(url: "https://github.com/binarybirds/sunlight", from: "1.0.0"),
```

⚠️ Don't forget to add "Sunlight" to your target as a dependency!



## License

[WTFPL](LICENSE) - Do what the fuck you want to.



## Other sources

- https://en.wikipedia.org/wiki/Position_of_the_Sun
- https://en.wikipedia.org/wiki/Sunrise_equation

- https://www.codeproject.com/Articles/100174/Calculate-and-Draw-Moon-Phase

- http://lamminet.fi/jarmo/rscalc.cc

- https://www.timeanddate.com/astronomy/different-types-twilight.html

- https://www.photopills.com/articles/understanding-golden-hour-blue-hour-and-twilights

42 changes: 42 additions & 0 deletions Sources/FloatingPoint+Extensions.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
//
// FloatingPoint+Extensions.swift
// Sunlight
//
// Created by Tibor Bödecs on 2019. 02. 07..
// Copyright © 2018-2019. Tibor Bödecs. All rights reserved.
//

import Foundation

extension FloatingPoint {

var radians: Self {
return self * .pi / 180
}

var degrees: Self {
return self * 180 / .pi
}

// Reduce angle to within 0..360 degrees
var reduceAngle: Self {
return self - 360 * floor(self / 360) as Self
}

// Reduce angle to within -180..+180 degrees
var reduceAngle180: Self {
let value = self / 360 + 1 / 2
return self - 360 * floor(value)
}

func normalise(withMaximum maximum: Self) -> Self {
var value = self
if value < 0 {
value += maximum
}
if value > maximum {
value -= maximum
}
return value
}
}
101 changes: 101 additions & 0 deletions Sources/SchlyterAlgorithm.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
//
// SchlyterAlgorithm.swift
// Sunlight
//
// Created by Tibor Bödecs on 2018. 01. 16..
// Copyright © 2018-2019. Tibor Bödecs. All rights reserved.
//

import Foundation

// http://stjarnhimlen.se/comp/sunriset.c
public struct SchlyterAlgorithm: SunlightCalculatorAlgorithm {

public init() {

}

private func daysSince2000Jan0(_ y: Int, _ m: Int, _ d: Int) -> Int {
return (367 * y - ((7 * (y + ((m + 9) / 12))) / 4) + (275 * m / 9) + d - 730_530)
}

private func GMST0(_ d: Double) -> Double {
return ((180.0 + 356.047_0 + 282.940_4) + (0.985_600_258_5 + 4.70935e-5) * d).reduceAngle
}

private func sunposAtDay(_ d: Double, lon: inout Double, r: inout Double) {
let M = (356.047_0 + 0.985_600_258_5 * d).reduceAngle
let w = 282.940_4 + 4.70935e-5 * d
let e = 0.016_709 - 1.151e-9 * d

let E = M + e.degrees * sin(M.radians) * (1.0 + e * cos(M.radians))
let x = cos(E.radians) - e
let y = sqrt(1.0 - e * e) * sin(E.radians)
r = sqrt(x * x + y * y)
let v = atan2(y, x).degrees
lon = v + w
if lon >= 360.0 {
lon -= 360.0
}
}

private func sun_RA_decAtDay(_ d: Double, RA: inout Double, dec: inout Double, r: inout Double) {
var lon: Double = 0

self.sunposAtDay(d, lon: &lon, r: &r)

let xs = r * cos(lon.radians)
let ys = r * sin(lon.radians)
let obl_ecl = 23.439_3 - 3.563E-7 * d
let xe = xs
let ye = ys * cos(obl_ecl.radians)
let ze = ys * sin(obl_ecl.radians)
RA = atan2(ye, xe).degrees
dec = atan2(ze, sqrt(xe * xe + ye * ye)).degrees
}

public func calculate(_ transition: Transition,
on date: Date,
latitude: Double,
longitude: Double,
twilight: Twilight) -> Date? {

var calendar = Calendar(identifier: .gregorian)
calendar.timeZone = TimeZone(abbreviation: "UTC")!

let dcs = calendar.dateComponents([.year, .month, .day], from: date)
let year = calendar.component(.year, from: date)
let month = calendar.component(.month, from: date)
let day = calendar.component(.day, from: date)
let newDate = calendar.date(from: dcs)!

var sRA: Double = 0
var sdec: Double = 0
var sr: Double = 0

let d = Double(self.daysSince2000Jan0(year, month, day)) + 0.5 - longitude / 360.0
let sidtime = (self.GMST0(d) + 180.0 + longitude).reduceAngle

self.sun_RA_decAtDay(d, RA: &sRA, dec: &sdec, r: &sr)

let tsouth = 12.0 - (sidtime - sRA).reduceAngle180 / 15.0
let sradius = 0.266_6 / sr

var alt = twilight.degrees
if case .official = twilight { //upper_limb = 1
alt -= sradius
}

let cost = (sin(alt.radians) - sin(latitude.radians) * sin(sdec.radians)) / (cos(latitude.radians) * cos(sdec.radians))
guard cost < 1, cost > -1 else {
return nil
}
let t = acos(cost).degrees / 15.0

var val = tsouth + t
if transition == .dawn {
val = tsouth - t
}
return newDate.addingTimeInterval(val * 3_600)
}
}
35 changes: 35 additions & 0 deletions Sources/SunlightCalculator.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
//
// Sunlight.swift
// Sunlight
//
// Created by Tibor Bödecs on 2019. 02. 10..
// Copyright © 2018-2019. Tibor Bödecs. All rights reserved.
//

import Foundation

public struct SunlightCalculator {

public let algorithm: SunlightCalculatorAlgorithm
public let latitude: Double
public let longitude: Double
public let date: Date

public init(using algorithm: SunlightCalculatorAlgorithm = SchlyterAlgorithm(),
date: Date = Date(),
latitude: Double,
longitude: Double) {
self.algorithm = algorithm
self.latitude = latitude
self.longitude = longitude
self.date = date
}

public func calculate(_ transition: Transition, twilight: Twilight) -> Date? {
return self.algorithm.calculate(transition,
on: self.date,
latitude: self.latitude,
longitude: self.longitude,
twilight: twilight)
}
}
47 changes: 47 additions & 0 deletions Sources/SunlightProtocols.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
//
// SunlightProtocols.swift
// Sunlight
//
// Created by Tibor Bödecs on 2019. 02. 10..
// Copyright © 2018-2019. Tibor Bödecs. All rights reserved.
//

import Foundation

public enum Transition {
case dawn
case dusk
}

public enum Twilight {

case official
case civil
case nautical
case astronomical
case custom(Double)

public var degrees: Double {
switch self {
case .official:
return -35.0 / 60.0
case .civil:
return -6
case .nautical:
return -12
case .astronomical:
return -18
case .custom(let value):
return value
}
}
}

public protocol SunlightCalculatorAlgorithm {
func calculate(_ transition: Transition,
on date: Date,
latitude: Double,
longitude: Double,
twilight: Twilight) -> Date?
}

Loading

0 comments on commit ae93767

Please sign in to comment.