Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Siril Plate Solver #433

Merged
merged 5 commits into from
Jun 13, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
package nebulosa.api.livestacker

import com.fasterxml.jackson.databind.annotation.JsonDeserialize
import nebulosa.api.beans.converters.angle.DegreesDeserializer
import nebulosa.livestacker.LiveStacker
import nebulosa.pixinsight.livestacker.PixInsightLiveStacker
import nebulosa.pixinsight.script.PixInsightIsRunning
Expand All @@ -20,7 +18,6 @@ data class LiveStackingRequest(
@JvmField val dark: Path? = null,
@JvmField val flat: Path? = null,
@JvmField val bias: Path? = null,
@JvmField @field:JsonDeserialize(using = DegreesDeserializer::class) val rotate: Double = 0.0,
@JvmField val use32Bits: Boolean = false,
@JvmField val slot: Int = 1,
) : Supplier<LiveStacker> {
Expand All @@ -29,7 +26,7 @@ data class LiveStackingRequest(
val workingDirectory = Files.createTempDirectory("ls-")

return when (type) {
LiveStackerType.SIRIL -> SirilLiveStacker(executablePath!!, workingDirectory, dark, flat, rotate, use32Bits)
LiveStackerType.SIRIL -> SirilLiveStacker(executablePath!!, workingDirectory, dark, flat, use32Bits)
LiveStackerType.PIXINSIGHT -> {
val runner = PixInsightScriptRunner(executablePath!!)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import nebulosa.astap.platesolver.AstapPlateSolver
import nebulosa.astrometrynet.nova.NovaAstrometryNetService
import nebulosa.astrometrynet.platesolver.LocalAstrometryNetPlateSolver
import nebulosa.astrometrynet.platesolver.NovaAstrometryNetPlateSolver
import nebulosa.siril.platesolver.SirilPlateSolver
import okhttp3.OkHttpClient
import org.hibernate.validator.constraints.time.DurationMax
import org.hibernate.validator.constraints.time.DurationMin
Expand All @@ -16,6 +17,8 @@ data class PlateSolverRequest(
@JvmField val type: PlateSolverType = PlateSolverType.ASTROMETRY_NET_ONLINE,
@JvmField val executablePath: Path? = null,
@JvmField val downsampleFactor: Int = 0,
@JvmField val focalLength: Double = 0.0,
@JvmField val pixelSize: Double = 0.0,
@JvmField val apiUrl: String = "",
@JvmField val apiKey: String = "",
@field:DurationMin(seconds = 0) @field:DurationMax(minutes = 5) @field:DurationUnit(ChronoUnit.SECONDS)
Expand All @@ -31,6 +34,7 @@ data class PlateSolverRequest(
val service = NOVA_ASTROMETRY_NET_CACHE.getOrPut(key) { NovaAstrometryNetService(apiUrl, httpClient) }
NovaAstrometryNetPlateSolver(service, apiKey)
}
PlateSolverType.SIRIL -> SirilPlateSolver(executablePath!!, focalLength, pixelSize)
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,5 @@ class PlateSolverService(
fun solve(
options: PlateSolverRequest, path: Path,
centerRA: Angle = 0.0, centerDEC: Angle = 0.0, radius: Angle = 0.0,
) = options.get(httpClient)
.solve(path, null, centerRA, centerDEC, radius, 1, options.timeout.takeIf { it.toSeconds() > 0 })
) = options.get(httpClient).solve(path, null, centerRA, centerDEC, radius, options.downsampleFactor, options.timeout)
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,5 @@ enum class PlateSolverType {
ASTAP,
ASTROMETRY_NET,
ASTROMETRY_NET_ONLINE,
SIRIL,
}
28 changes: 0 additions & 28 deletions api/src/test/kotlin/SirilLiveStackerTest.kt

This file was deleted.

35 changes: 27 additions & 8 deletions desktop/src/app/image/image.component.html
Original file line number Diff line number Diff line change
Expand Up @@ -165,34 +165,52 @@
<div class="col-12 gap-2 align-items-center">
<span class="p-float-label">
<p-dropdown [options]="'PLATE_SOLVER' | dropdownOptions" [(ngModel)]="solver.type" styleClass="p-inputtext-sm border-0"
[autoDisplayFirst]="false" />
[autoDisplayFirst]="false" appendTo="body" />
<label>Type</label>
</span>
<p-checkbox [binary]="true" label="Blind" [(ngModel)]="solver.blind" />
<p-checkbox [binary]="true" [disabled]="solver.type === 'SIRIL'" label="Blind" [(ngModel)]="solver.blind" />
</div>
<div class="col-8">
<div class="grid">
<div class="col-6">
<span class="p-float-label">
<input pInputText [disabled]="solver.blind" class="p-inputtext-sm border-0 w-full" [(ngModel)]="solver.centerRA" />
<input pInputText [disabled]="solver.blind && solver.type !== 'SIRIL'" class="p-inputtext-sm border-0 w-full"
[(ngModel)]="solver.centerRA" />
<label>Center RA (h)</label>
</span>
</div>
<div class="col-6">
<span class="p-float-label">
<input pInputText [disabled]="solver.blind" class="p-inputtext-sm border-0 w-full" [(ngModel)]="solver.centerDEC" />
<input pInputText [disabled]="solver.blind && solver.type !== 'SIRIL'" class="p-inputtext-sm border-0 w-full"
[(ngModel)]="solver.centerDEC" />
<label>Center DEC (°)</label>
</span>
</div>
</div>
</div>
<div class="col-4 flex flex-row align-items-center gap-2">
<div class="col-4 flex flex-row align-items-center">
<span class="p-float-label">
<p-inputNumber [disabled]="solver.blind" [min]="1" [max]="180" styleClass="p-inputtext-sm border-0 w-full" [showButtons]="true"
[(ngModel)]="solver.radius" scrollableNumber />
<p-inputNumber [disabled]="solver.blind && solver.type !== 'SIRIL'" [min]="1" [max]="180" styleClass="p-inputtext-sm border-0 w-full"
[showButtons]="true" [(ngModel)]="solver.radius" scrollableNumber />
<label>Radius (°)</label>
</span>
</div>
@if (solver.type === 'SIRIL') {
<div class="col-6 flex flex-row align-items-center">
<span class="p-float-label">
<p-inputNumber [min]="0" [max]="10000" styleClass="p-inputtext-sm border-0 w-full" [showButtons]="true"
[(ngModel)]="solver.focalLength" [allowEmpty]="false" locale="en" scrollableNumber />
<label>Focal length (mm)</label>
</span>
</div>
<div class="col-6 flex flex-row align-items-center">
<span class="p-float-label">
<p-inputNumber [min]="0" [max]="100" [step]="0.01" styleClass="p-inputtext-sm border-0 w-full" [showButtons]="true"
[(ngModel)]="solver.pixelSize" [allowEmpty]="false" locale="en" scrollableNumber />
<label>Pixel size (µm)</label>
</span>
</div>
}
</div>
<ng-template pTemplate="footer">
<div class="grid pt-2">
Expand Down Expand Up @@ -247,7 +265,8 @@
</div>
</div>

<p-button [disabled]="solver.running" icon="mdi mdi-sigma" label="Solve" (onClick)="solveImage()" [text]="true" size="small" />
<p-button [disabled]="solver.running || !canPlateSolve" icon="mdi mdi-sigma" label="Solve" (onClick)="solveImage()" [text]="true"
size="small" />
</ng-template>
</p-dialog>

Expand Down
29 changes: 27 additions & 2 deletions desktop/src/app/image/image.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ import { PreferenceService } from '../../shared/services/preference.service'
import { PrimeService } from '../../shared/services/prime.service'
import { Angle, EquatorialCoordinateJ2000 } from '../../shared/types/atlas.types'
import { Camera } from '../../shared/types/camera.types'
import { AnnotationInfoDialog, DEFAULT_FOV, DetectedStar, EMPTY_IMAGE_SOLVED, FOV, IMAGE_STATISTICS_BIT_OPTIONS, ImageAnnotation, ImageAnnotationDialog, ImageChannel, ImageData, ImageFITSHeadersDialog, ImageFOVDialog, ImageInfo, ImageROI, ImageSCNRDialog, ImageSaveDialog, ImageSolved, ImageSolverDialog, ImageStatisticsBitOption, ImageStretchDialog, ImageTransformation, StarDetectionDialog } from '../../shared/types/image.types'
import { AnnotationInfoDialog, DEFAULT_FOV, DetectedStar, EMPTY_IMAGE_SOLVED, FITSHeaderItem, FOV, IMAGE_STATISTICS_BIT_OPTIONS, ImageAnnotation, ImageAnnotationDialog, ImageChannel, ImageData, ImageFITSHeadersDialog, ImageFOVDialog, ImageInfo, ImageROI, ImageSCNRDialog, ImageSaveDialog, ImageSolved, ImageSolverDialog, ImageStatisticsBitOption, ImageStretchDialog, ImageTransformation, StarDetectionDialog } from '../../shared/types/image.types'
import { Mount } from '../../shared/types/mount.types'
import { CoordinateInterpolator, InterpolatedCoordinate } from '../../shared/utils/coordinate-interpolation'
import { AppComponent } from '../app.component'
Expand Down Expand Up @@ -130,12 +130,14 @@ export class ImageComponent implements AfterViewInit, OnDestroy {
readonly solver: ImageSolverDialog = {
showDialog: false,
running: false,
type: 'ASTAP',
blind: true,
centerRA: '',
centerDEC: '',
radius: 4,
focalLength: 0,
pixelSize: 0,
solved: structuredClone(EMPTY_IMAGE_SOLVED),
type: 'ASTAP'
}

crossHair = false
Expand Down Expand Up @@ -459,6 +461,10 @@ export class ImageComponent implements AfterViewInit, OnDestroy {
return (this.showLiveStackedImage && this.imageData.liveStackedPath) || this.imageData.path
}

get canPlateSolve() {
return this.solver.type !== 'SIRIL' || (this.solver.focalLength > 0 && this.solver.pixelSize > 0)
}

constructor(
private app: AppComponent,
private route: ActivatedRoute,
Expand Down Expand Up @@ -879,6 +885,8 @@ export class ImageComponent implements AfterViewInit, OnDestroy {

this.fitsHeaders.headers = info.headers

this.retrieveInfoFromImageHeaders(info.headers)

if (this.imageURL) window.URL.revokeObjectURL(this.imageURL)
this.imageURL = window.URL.createObjectURL(blob)
image.src = this.imageURL
Expand All @@ -891,6 +899,21 @@ export class ImageComponent implements AfterViewInit, OnDestroy {
this.retrieveCoordinateInterpolation()
}

private retrieveInfoFromImageHeaders(headers: FITSHeaderItem[]) {
const imagePreference = this.preference.imagePreference.get()

for (const item of headers) {
if (item.name === 'FOCALLEN') {
this.solver.focalLength = parseFloat(item.value)
} else if (item.name === 'XPIXSZ') {
this.solver.pixelSize = parseFloat(item.value)
}
}

this.solver.focalLength ||= imagePreference.solverFocalLength || 0
this.solver.pixelSize ||= imagePreference.solverPixelSize || 0
}

imageClicked(event: MouseEvent, contextMenu: boolean) {
this.imageMouseX = event.offsetX
this.imageMouseY = event.offsetY
Expand Down Expand Up @@ -1254,6 +1277,8 @@ export class ImageComponent implements AfterViewInit, OnDestroy {
const preference = this.preference.imagePreference.get()
preference.solverRadius = this.solver.radius
preference.solverType = this.solver.type
preference.solverPixelSize = this.solver.pixelSize
preference.solverFocalLength = this.solver.focalLength
preference.starDetectionType = this.starDetection.type
preference.starDetectionMinSNR = this.starDetection.minSNR
this.preference.imagePreference.set(preference)
Expand Down
2 changes: 1 addition & 1 deletion desktop/src/app/settings/settings.component.html
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@
<label>Timeout (s)</label>
</span>
</div>
<div class="col-12" *ngIf="plateSolverType === 'ASTAP'">
<div class="col-12" *ngIf="plateSolverType !== 'ASTROMETRY_NET_ONLINE'">
<neb-path-chooser key="PLATE_SOLVER_EXECUTABLE_PATH" [directory]="false" label="Executable path"
[path]="plateSolvers.get(plateSolverType)!.executablePath" class="w-full"
(pathChange)="plateSolvers.get(plateSolverType)!.executablePath = $event; save()" />
Expand Down
2 changes: 2 additions & 0 deletions desktop/src/app/settings/settings.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ export class SettingsComponent implements AfterViewInit, OnDestroy {

this.plateSolvers.set('ASTAP', preference.plateSolverRequest('ASTAP').get())
this.plateSolvers.set('ASTROMETRY_NET_ONLINE', preference.plateSolverRequest('ASTROMETRY_NET_ONLINE').get())
this.plateSolvers.set('SIRIL', preference.plateSolverRequest('SIRIL').get())

this.starDetectors.set('ASTAP', preference.starDetectionRequest('ASTAP').get())
this.starDetectors.set('PIXINSIGHT', preference.starDetectionRequest('PIXINSIGHT').get())
Expand Down Expand Up @@ -130,6 +131,7 @@ export class SettingsComponent implements AfterViewInit, OnDestroy {
save() {
this.preference.plateSolverRequest('ASTAP').set(this.plateSolvers.get('ASTAP'))
this.preference.plateSolverRequest('ASTROMETRY_NET_ONLINE').set(this.plateSolvers.get('ASTROMETRY_NET_ONLINE'))
this.preference.plateSolverRequest('SIRIL').set(this.plateSolvers.get('SIRIL'))

this.preference.starDetectionRequest('ASTAP').set(this.starDetectors.get('ASTAP'))
this.preference.starDetectionRequest('PIXINSIGHT').set(this.starDetectors.get('PIXINSIGHT'))
Expand Down
2 changes: 1 addition & 1 deletion desktop/src/shared/pipes/dropdown-options.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ export class DropdownOptionsPipe implements PipeTransform {
transform(type: DropdownOptionType): DropdownOptionReturnType | undefined {
switch (type) {
case 'STAR_DETECTOR': return ['ASTAP', 'PIXINSIGHT']
case 'PLATE_SOLVER': return ['ASTAP', 'ASTROMETRY_NET_ONLINE']
case 'PLATE_SOLVER': return ['ASTAP', 'ASTROMETRY_NET_ONLINE', 'SIRIL']
case 'AUTO_FOCUS_FITTING_MODE': return ['TRENDLINES', 'PARABOLIC', 'TREND_PARABOLIC', 'HYPERBOLIC', 'TREND_HYPERBOLIC']
case 'AUTO_FOCUS_BACKLASH_COMPENSATION_MODE': return ['NONE', 'ABSOLUTE', 'OVERSHOOT']
case 'LIVE_STACKER': return ['SIRIL', 'PIXINSIGHT']
Expand Down
4 changes: 4 additions & 0 deletions desktop/src/shared/types/image.types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,8 @@ export interface ImageStatistics {
export interface ImagePreference {
solverRadius?: number
solverType?: PlateSolverType
solverFocalLength?: number
solverPixelSize?: number
savePath?: string
starDetectionType?: StarDetectorType
starDetectionMinSNR?: number
Expand Down Expand Up @@ -215,6 +217,8 @@ export interface ImageSolverDialog {
centerRA: Angle
centerDEC: Angle
radius: number
focalLength: number
pixelSize: number
readonly solved: ImageSolved
type: PlateSolverType
}
Expand Down
6 changes: 3 additions & 3 deletions desktop/src/shared/types/settings.types.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
export type PlateSolverType = 'ASTROMETRY_NET' | 'ASTROMETRY_NET_ONLINE' | 'ASTAP'
export type PlateSolverType = 'ASTROMETRY_NET' | 'ASTROMETRY_NET_ONLINE' | 'ASTAP' | 'SIRIL'

export interface PlateSolverOptions {
type: PlateSolverType
Expand All @@ -15,7 +15,7 @@ export const EMPTY_PLATE_SOLVER_OPTIONS: PlateSolverOptions = {
downsampleFactor: 0,
apiUrl: 'https://nova.astrometry.net/',
apiKey: '',
timeout: 600,
timeout: 300,
}

export type StarDetectorType = 'ASTAP' | 'PIXINSIGHT'
Expand All @@ -31,7 +31,7 @@ export interface StarDetectionOptions {
export const EMPTY_STAR_DETECTION_OPTIONS: StarDetectionOptions = {
type: 'ASTAP',
executablePath: '',
timeout: 600,
timeout: 300,
minSNR: 0,
slot: 1,
}
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ data class AstapPlateSolver(private val executablePath: Path) : PlateSolver {
override fun solve(
path: Path?, image: Image?,
centerRA: Angle, centerDEC: Angle, radius: Angle,
downsampleFactor: Int, timeout: Duration?,
downsampleFactor: Int, timeout: Duration,
cancellationToken: CancellationToken,
): PlateSolution {
requireNotNull(path) { "path is required" }
Expand Down Expand Up @@ -61,7 +61,7 @@ data class AstapPlateSolver(private val executablePath: Path) : PlateSolver {
LOG.info("ASTAP solving. command={}", cmd.command)

try {
val timeoutOrDefault = timeout?.takeIf { it.toSeconds() > 0 } ?: Duration.ofMinutes(5)
val timeoutOrDefault = timeout.takeIf { it.toSeconds() > 0 } ?: Duration.ofMinutes(5)
cancellationToken.listen(cmd)
cmd.start(timeoutOrDefault)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ data class LibAstrometryNetPlateSolver(private val solver: LibAstrometryNet) : P
override fun solve(
path: Path?, image: Image?,
centerRA: Angle, centerDEC: Angle, radius: Angle,
downsampleFactor: Int, timeout: Duration?,
downsampleFactor: Int, timeout: Duration,
cancellationToken: CancellationToken,
): PlateSolution {
return PlateSolution.NO_SOLUTION
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
package nebulosa.astrometrynet.platesolver

import nebulosa.common.concurrency.cancel.CancellationToken
import nebulosa.common.exec.LineReadListener
import nebulosa.common.exec.CommandLineListener
import nebulosa.common.exec.commandLine
import nebulosa.image.Image
import nebulosa.log.loggerFor
Expand All @@ -23,7 +23,7 @@ data class LocalAstrometryNetPlateSolver(private val executablePath: Path) : Pla
override fun solve(
path: Path?, image: Image?,
centerRA: Angle, centerDEC: Angle, radius: Angle,
downsampleFactor: Int, timeout: Duration?,
downsampleFactor: Int, timeout: Duration,
cancellationToken: CancellationToken,
): PlateSolution {
requireNotNull(path) { "path is required" }
Expand All @@ -39,7 +39,7 @@ data class LocalAstrometryNetPlateSolver(private val executablePath: Path) : Pla

putArg("--dir", outFolder)

putArg("--cpulimit", timeout?.takeIf { it.toSeconds() > 0 }?.toSeconds() ?: 300)
putArg("--cpulimit", timeout.takeIf { it.toSeconds() > 0 }?.toSeconds() ?: 300)
putArg("--scale-units", "degwidth")
putArg("--guess-scale")
putArg("--crpix-center")
Expand All @@ -61,7 +61,7 @@ data class LocalAstrometryNetPlateSolver(private val executablePath: Path) : Pla

try {
cancellationToken.listen(cmd)
cmd.registerLineReadListener(solution)
cmd.registerCommandLineListener(solution)
cmd.start()
LOG.info("astrometry.net exited. code={}", cmd.get())
return solution.get()
Expand All @@ -74,14 +74,14 @@ data class LocalAstrometryNetPlateSolver(private val executablePath: Path) : Pla
}
}

private class PlateSolutionLineReader : LineReadListener.OnInput, Supplier<PlateSolution> {
private class PlateSolutionLineReader : CommandLineListener.OnLineRead, Supplier<PlateSolution> {

@Volatile private var fieldCenter: DoubleArray? = null
@Volatile private var fieldRotation: Angle = 0.0
@Volatile private var pixelScale: Angle = 0.0
@Volatile private var fieldSize: DoubleArray? = null

override fun onInputRead(line: String) {
override fun onLineRead(line: String) {
fieldCenter(line)?.also { fieldCenter = it }
?: fieldRotation(line)?.also { fieldRotation = it }
?: pixelScale(line)?.also { pixelScale = it }
Expand Down
Loading