Skip to content

Commit

Permalink
Merge branch 'dev'
Browse files Browse the repository at this point in the history
  • Loading branch information
tiagohm committed Aug 25, 2024
2 parents 71e794f + 5a69e05 commit 4ef43d9
Show file tree
Hide file tree
Showing 27 changed files with 442 additions and 102 deletions.
1 change: 1 addition & 0 deletions api/src/main/kotlin/nebulosa/api/atlas/MoonPhase.kt
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ data class MoonPhase(
@field:JsonAlias("subearth_lon") @JvmField val subEarthLon: Double = 0.0,
@field:JsonAlias("subearth_lat") @JvmField val subEarthLat: Double = 0.0,
@field:JsonAlias("posangle") @JvmField val posAngle: Double = 0.0,
@JvmField var lunation: Int = 0,
) {

companion object {
Expand Down
6 changes: 4 additions & 2 deletions api/src/main/kotlin/nebulosa/api/atlas/SkyAtlasService.kt
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import nebulosa.math.evenlySpacedNumbers
import nebulosa.math.toLightYears
import nebulosa.math.toMas
import nebulosa.nova.almanac.findDiscrete
import nebulosa.nova.almanac.lunation
import nebulosa.nova.astrometry.Body
import nebulosa.nova.astrometry.Constellation
import nebulosa.nova.astrometry.ELPMPP02
Expand All @@ -21,7 +22,7 @@ import nebulosa.nova.position.Geoid
import nebulosa.sbd.SmallBodyDatabaseService
import nebulosa.skycatalog.SkyObject
import nebulosa.skycatalog.SkyObjectType
import nebulosa.time.SystemClock
import nebulosa.time.TimeYMDHMS
import nebulosa.time.TimeZonedInSeconds
import okhttp3.OkHttpClient
import okhttp3.Request
Expand All @@ -34,7 +35,6 @@ import java.awt.image.BufferedImage
import java.io.ByteArrayOutputStream
import java.time.LocalDate
import java.time.LocalDateTime
import java.time.LocalTime
import java.time.format.DateTimeFormatter
import java.time.temporal.ChronoUnit
import java.util.concurrent.TimeUnit
Expand Down Expand Up @@ -254,6 +254,8 @@ class SkyAtlasService(
moonPhase = dateTime.withMinute(0).withSecond(0).withNano(0) to (body ?: return null)
}

moonPhase?.second?.lunation = TimeYMDHMS(dateTime).lunation()

val date = dateTime.toLocalDate().withDayOfMonth(1)
val phases = if (date in cachedMoonPhases) {
cachedMoonPhases[date]!!
Expand Down
1 change: 1 addition & 0 deletions api/src/main/kotlin/nebulosa/api/image/ImageAnnotation.kt
Original file line number Diff line number Diff line change
Expand Up @@ -49,5 +49,6 @@ data class ImageAnnotation(
@field:JsonSerialize(using = DeclinationSerializer::class) override val declinationJ2000: Angle = 0.0,
override val magnitude: Double = SkyObject.UNKNOWN_MAGNITUDE,
@JvmField val constellation: Constellation = Constellation.find(ICRF.equatorial(rightAscensionJ2000, declinationJ2000)),
@JvmField val type: String = "MINOR_PLANET",
) : SkyObject
}
18 changes: 18 additions & 0 deletions api/src/main/kotlin/nebulosa/api/stacker/StackerEvent.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package nebulosa.api.stacker

import nebulosa.api.message.MessageEvent

data class StackerEvent(
@JvmField val state: StackerState = StackerState.IDLE,
@JvmField val type: StackerGroupType = StackerGroupType.MONO,
@JvmField val stackCount: Int = 0,
@JvmField val numberOfTargets: Int = 0,
) : MessageEvent {

override val eventName = "STACKER.ELAPSED"

companion object {

@JvmStatic val IDLE = StackerEvent()
}
}
2 changes: 2 additions & 0 deletions api/src/main/kotlin/nebulosa/api/stacker/StackerGroupType.kt
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package nebulosa.api.stacker

import nebulosa.fits.filter
import nebulosa.fits.naxis
import nebulosa.image.format.ReadableHeader

enum class StackerGroupType {
Expand All @@ -26,6 +27,7 @@ enum class StackerGroupType {
else if (it.contains("GREEN", true) || it.equals("G", true)) GREEN
else if (it.contains("BLUE", true) || it.equals("B", true)) BLUE
else if (it.contains("LUMINANCE", true) || it.equals("L", true)) LUMINANCE
else if (header.naxis >= 3) RGB
else MONO
} ?: MONO
}
Expand Down
142 changes: 102 additions & 40 deletions api/src/main/kotlin/nebulosa/api/stacker/StackerService.kt
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
package nebulosa.api.stacker

import nebulosa.api.message.MessageService
import nebulosa.common.concurrency.cancel.CancellationToken
import nebulosa.fits.fits
import nebulosa.fits.isFits
import nebulosa.image.format.ImageHdu
import nebulosa.stacker.AutoStacker
import nebulosa.stacker.AutoStackerListener
import nebulosa.xisf.isXisf
import nebulosa.xisf.xisf
import org.springframework.stereotype.Service
Expand All @@ -14,8 +16,9 @@ import kotlin.io.path.isDirectory
import kotlin.io.path.isRegularFile

@Service
class StackerService {
class StackerService(private val messageService: MessageService) {

@Synchronized
fun stack(request: StackingRequest, cancellationToken: CancellationToken = CancellationToken.NONE): Path? {
require(request.outputDirectory != null && request.outputDirectory.exists() && request.outputDirectory.isDirectory())

Expand All @@ -31,54 +34,76 @@ class StackerService {
// Combined LRGB
return if (red.size + green.size + blue.size >= 1) {
val stacker = request.get()

cancellationToken.listen { stacker.stop() }

val stackedLuminancePath = luminance.stack(request, stacker, name, StackerGroupType.LUMINANCE, cancellationToken)
val stackedRedPath = red.stack(request, stacker, name, StackerGroupType.RED, cancellationToken)
val stackedGreenPath = green.stack(request, stacker, name, StackerGroupType.GREEN, cancellationToken)
val stackedBluePath = blue.stack(request, stacker, name, StackerGroupType.BLUE, cancellationToken)

if (cancellationToken.isCancelled) {
null
} else {
val combinedPath = Path.of("${request.outputDirectory}", "$name.LRGB.fits")
stacker.combineLRGB(combinedPath, stackedLuminancePath, stackedRedPath, stackedGreenPath, stackedBluePath)
combinedPath
val autoStackerMessageHandler = AutoStackerMessageHandler(luminance, red, green, blue)

try {
stacker.registerAutoStackerListener(autoStackerMessageHandler)

val stackedLuminancePath = luminance.stack(request, stacker, name, StackerGroupType.LUMINANCE, cancellationToken)
val stackedRedPath = red.stack(request, stacker, name, StackerGroupType.RED, cancellationToken)
val stackedGreenPath = green.stack(request, stacker, name, StackerGroupType.GREEN, cancellationToken)
val stackedBluePath = blue.stack(request, stacker, name, StackerGroupType.BLUE, cancellationToken)

if (cancellationToken.isCancelled) {
null
} else {
val combinedPath = Path.of("${request.outputDirectory}", "$name.LRGB.fits")
stacker.combineLRGB(combinedPath, stackedLuminancePath, stackedRedPath, stackedGreenPath, stackedBluePath)
combinedPath
}
} finally {
messageService.sendMessage(StackerEvent.IDLE)
stacker.unregisterAutoStackerListener(autoStackerMessageHandler)
}
}
// LRGB
else if (rgb.isNotEmpty()) {
val stacker = request.get()

val stackedLuminancePath = luminance.stack(request, stacker, name, StackerGroupType.LUMINANCE, cancellationToken)
val stackedRGBPath = rgb.stack(request, stacker, name, StackerGroupType.RGB, cancellationToken)

if (cancellationToken.isCancelled) {
null
} else if (stackedLuminancePath != null && stackedRGBPath != null) {
val combinedPath = Path.of("${request.outputDirectory}", "$name.LRGB.fits")
stacker.combineLuminance(combinedPath, stackedLuminancePath, stackedRGBPath, false)
combinedPath
} else {
stackedLuminancePath ?: stackedRGBPath
val autoStackerMessageHandler = AutoStackerMessageHandler(luminance, rgb = rgb)

try {
stacker.registerAutoStackerListener(autoStackerMessageHandler)

val stackedLuminancePath = luminance.stack(request, stacker, name, StackerGroupType.LUMINANCE, cancellationToken)
val stackedRGBPath = rgb.stack(request, stacker, name, StackerGroupType.RGB, cancellationToken)

if (cancellationToken.isCancelled) {
null
} else if (stackedLuminancePath != null && stackedRGBPath != null) {
val combinedPath = Path.of("${request.outputDirectory}", "$name.LRGB.fits")
stacker.combineLuminance(combinedPath, stackedLuminancePath, stackedRGBPath, false)
combinedPath
} else {
stackedLuminancePath ?: stackedRGBPath
}
} finally {
messageService.sendMessage(StackerEvent.IDLE)
stacker.unregisterAutoStackerListener(autoStackerMessageHandler)
}
}
// MONO
else if (mono.isNotEmpty() || luminance.isNotEmpty()) {
val stacker = request.get()

val stackedLuminancePath = luminance.stack(request, stacker, name, StackerGroupType.LUMINANCE, cancellationToken)
val stackedMonoPath = mono.stack(request, stacker, name, StackerGroupType.MONO, cancellationToken)

if (cancellationToken.isCancelled) {
null
} else if (stackedLuminancePath != null && stackedMonoPath != null) {
val combinedPath = Path.of("${request.outputDirectory}", "$name.LRGB.fits")
stacker.combineLuminance(combinedPath, stackedLuminancePath, stackedMonoPath, true)
combinedPath
} else {
stackedLuminancePath ?: stackedMonoPath
val autoStackerMessageHandler = AutoStackerMessageHandler(luminance, mono = mono)

try {
stacker.registerAutoStackerListener(autoStackerMessageHandler)

val stackedLuminancePath = luminance.stack(request, stacker, name, StackerGroupType.LUMINANCE, cancellationToken)
val stackedMonoPath = mono.stack(request, stacker, name, StackerGroupType.MONO, cancellationToken)

if (cancellationToken.isCancelled) {
null
} else if (stackedLuminancePath != null && stackedMonoPath != null) {
val combinedPath = Path.of("${request.outputDirectory}", "$name.LRGB.fits")
stacker.combineLuminance(combinedPath, stackedLuminancePath, stackedMonoPath, true)
combinedPath
} else {
stackedLuminancePath ?: stackedMonoPath
}
} finally {
messageService.sendMessage(StackerEvent.IDLE)
stacker.unregisterAutoStackerListener(autoStackerMessageHandler)
}
} else {
null
Expand All @@ -93,7 +118,7 @@ class StackerService {
null
} else if (size > 1) {
val outputPath = Path.of("${request.outputDirectory}", "$name.$group.fits")
if (stacker.stack(map { it.path!! }, outputPath, request.referencePath!!)) outputPath else null
if (stacker.stack(map { it.path!! }, outputPath, request.referencePath!!, cancellationToken)) outputPath else null
} else if (isNotEmpty()) {
val outputPath = Path.of("${request.outputDirectory}", "$name.$group.fits")
if (stacker.align(request.referencePath!!, this[0].path!!, outputPath)) outputPath else null
Expand All @@ -111,4 +136,41 @@ class StackerService {

return image.use { it.firstOrNull { it is ImageHdu }?.header }?.let(::AnalyzedTarget)
}

private inner class AutoStackerMessageHandler(
private val luminance: Collection<StackingTarget> = emptyList(),
private val red: Collection<StackingTarget> = emptyList(),
private val green: Collection<StackingTarget> = emptyList(),
private val blue: Collection<StackingTarget> = emptyList(),
private val mono: Collection<StackingTarget> = emptyList(),
private val rgb: Collection<StackingTarget> = emptyList(),
) : AutoStackerListener {

private val numberOfTargets = luminance.size + red.size + green.size + blue.size + mono.size + rgb.size

override fun onCalibrated(stackCount: Int, path: Path, calibratedPath: Path) {
sendNotification(StackerState.CALIBRATING, path, stackCount)
}

override fun onAligned(stackCount: Int, path: Path, alignedPath: Path) {
sendNotification(StackerState.ALIGNING, path, stackCount)
}

override fun onIntegrated(stackCount: Int, path: Path, alignedPath: Path) {
sendNotification(StackerState.INTEGRATING, path, stackCount)
}

fun sendNotification(state: StackerState, path: Path, stackCount: Int) {
val type = if (luminance.any { it.path === path }) StackerGroupType.LUMINANCE
else if (red.any { it.path === path }) StackerGroupType.RED
else if (green.any { it.path === path }) StackerGroupType.GREEN
else if (blue.any { it.path === path }) StackerGroupType.BLUE
else if (mono.any { it.path === path }) StackerGroupType.MONO
else if (rgb.any { it.path === path }) StackerGroupType.RGB
else return

val message = StackerEvent(state, type, stackCount + 1, numberOfTargets)
messageService.sendMessage(message)
}
}
}
8 changes: 8 additions & 0 deletions api/src/main/kotlin/nebulosa/api/stacker/StackerState.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package nebulosa.api.stacker

enum class StackerState {
IDLE,
CALIBRATING,
ALIGNING,
INTEGRATING,
}
8 changes: 4 additions & 4 deletions api/src/test/kotlin/SkyAtlasServiceTest.kt
Original file line number Diff line number Diff line change
Expand Up @@ -214,16 +214,16 @@ class SkyAtlasServiceTest {

phases shouldHaveSize 4

phases[0].dateTime.toString() shouldBe "2024-08-04T09:45"
phases[0].dateTime.toString() shouldBe "2024-08-04T06:45"
phases[0].name shouldBe MoonPhaseName.NEW_MOON

phases[1].dateTime.toString() shouldBe "2024-08-12T13:51"
phases[1].dateTime.toString() shouldBe "2024-08-12T10:51"
phases[1].name shouldBe MoonPhaseName.FIRST_QUARTER

phases[2].dateTime.toString() shouldBe "2024-08-19T17:17"
phases[2].dateTime.toString() shouldBe "2024-08-19T14:17"
phases[2].name shouldBe MoonPhaseName.FULL_MOON

phases[3].dateTime.toString() shouldBe "2024-08-26T09:28"
phases[3].dateTime.toString() shouldBe "2024-08-26T06:28"
phases[3].name shouldBe MoonPhaseName.LAST_QUARTER
}

Expand Down
8 changes: 5 additions & 3 deletions desktop/app/window.manager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -202,9 +202,11 @@ export class WindowManager {
}

saveWindowData(window: ApplicationWindow) {
const [x, y] = window.browserWindow.getPosition()
const [width, height] = window.browserWindow.getSize()
store.set(`window.${window.data.id}`, { x, y, width, height })
if (!window.data.id.endsWith('!')) {
const [x, y] = window.browserWindow.getPosition()
const [width, height] = window.browserWindow.getSize()
store.set(`window.${window.data.id}`, { x, y, width, height })
}
}

async createMainWindow(apiProcess?: ChildProcessWithoutNullStreams, port: number = this.port, host: string = this.host) {
Expand Down
6 changes: 5 additions & 1 deletion desktop/src/app/atlas/atlas.component.html
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,10 @@
<span class="font-bold">Age</span>
<span>{{ moon.phase.current.age }} d</span>
</div>
<div class="flex flex-column">
<span class="font-bold">Lunation</span>
<span>{{ moon.phase.current.lunation }}</span>
</div>
<div class="flex flex-column">
<span class="font-bold">Sub-Solar Lon / Lat</span>
<span>{{ moon.phase.current.subSolarLon }}° / {{ moon.phase.current.subSolarLat }}°</span>
Expand Down Expand Up @@ -607,7 +611,7 @@
</div>
<div
[class.invisible]="tab === 4"
class="col-12 justify-content-center pt-0">
class="col-12 justify-content-center pt-0 text-xs">
<a
target="_blank"
href="https://ssd.jpl.nasa.gov/horizons/">
Expand Down
Loading

0 comments on commit 4ef43d9

Please sign in to comment.