Skip to content

Commit

Permalink
[api][desktop]: Fix TPPA
Browse files Browse the repository at this point in the history
  • Loading branch information
tiagohm committed May 11, 2024
1 parent c68e0f7 commit 4946a1d
Show file tree
Hide file tree
Showing 14 changed files with 88 additions and 79 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package nebulosa.api.alignment.polar.tppa

import com.fasterxml.jackson.databind.annotation.JsonSerialize
import nebulosa.api.beans.converters.angle.DeclinationSerializer
import nebulosa.api.beans.converters.angle.RightAscensionSerializer
import nebulosa.api.cameras.CameraCaptureEvent
import nebulosa.api.messages.MessageEvent
import nebulosa.indi.device.camera.Camera
Expand All @@ -10,6 +11,8 @@ import nebulosa.math.Angle
data class TPPAEvent(
@JvmField val camera: Camera,
@JvmField val state: TPPAState = TPPAState.IDLE,
@JvmField @field:JsonSerialize(using = RightAscensionSerializer::class) val rightAscension: Angle = 0.0,
@JvmField @field:JsonSerialize(using = DeclinationSerializer::class) val declination: Angle = 0.0,
@JvmField @field:JsonSerialize(using = DeclinationSerializer::class) val azimuthError: Angle = 0.0,
@JvmField @field:JsonSerialize(using = DeclinationSerializer::class) val altitudeError: Angle = 0.0,
@JvmField @JsonSerialize(using = DeclinationSerializer::class) val totalError: Angle = 0.0,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,4 +19,5 @@ data class TPPAStartRequest(
@JvmField val stopTrackingWhenDone: Boolean = true,
@field:DurationMin(seconds = 1L) @JvmField val stepDirection: GuideDirection = GuideDirection.EAST,
@field:DurationUnit(ChronoUnit.SECONDS) @field:DurationMin(seconds = 1L) @JvmField val stepDuration: Duration = Duration.ZERO,
@JvmField val stepSpeed: String? = null,
)
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package nebulosa.api.alignment.polar.tppa
enum class TPPAState {
IDLE,
SLEWING,
SLEWED,
SOLVING,
SOLVED,
COMPUTED,
Expand Down
35 changes: 28 additions & 7 deletions api/src/main/kotlin/nebulosa/api/alignment/polar/tppa/TPPATask.kt
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,8 @@ import nebulosa.api.cameras.AutoSubFolderMode
import nebulosa.api.cameras.CameraCaptureEvent
import nebulosa.api.cameras.CameraCaptureState
import nebulosa.api.cameras.CameraCaptureTask
import nebulosa.api.guiding.GuidePulseRequest
import nebulosa.api.messages.MessageEvent
import nebulosa.api.mounts.MountMoveRequest
import nebulosa.api.mounts.MountMoveTask
import nebulosa.api.tasks.Task
import nebulosa.common.concurrency.cancel.CancellationToken
Expand All @@ -25,6 +25,8 @@ import nebulosa.plate.solving.PlateSolver
import java.nio.file.Files
import java.nio.file.Path
import java.time.Duration
import java.util.concurrent.atomic.AtomicBoolean
import kotlin.math.hypot

data class TPPATask(
@JvmField val camera: Camera,
Expand All @@ -35,7 +37,7 @@ data class TPPATask(
@JvmField val latitude: Angle = mount!!.latitude,
) : Task<MessageEvent>(), Consumer<CameraCaptureEvent> {

@JvmField val guideRequest = GuidePulseRequest(request.stepDirection, request.stepDuration)
@JvmField val mountMoveRequest = MountMoveRequest(request.stepDirection, request.stepDuration, request.stepSpeed)
@JvmField val cameraRequest = request.capture.copy(
savePath = Files.createTempDirectory("tppa"),
exposureAmount = 0, exposureDelay = Duration.ZERO,
Expand All @@ -47,7 +49,10 @@ data class TPPATask(
private val cameraCaptureTask = CameraCaptureTask(camera, cameraRequest, exposureMaxRepeat = 1)
private val mountMoveState = BooleanArray(3)
private val elapsedTime = Stopwatch()
private val finished = AtomicBoolean()

@Volatile private var rightAscension: Angle = 0.0
@Volatile private var declination: Angle = 0.0
@Volatile private var azimuthError: Angle = 0.0
@Volatile private var altitudeError: Angle = 0.0
@Volatile private var totalError: Angle = 0.0
Expand All @@ -71,7 +76,9 @@ data class TPPATask(
savedImage = event.savePath!!
}

sendEvent(TPPAState.SOLVING, event)
if (!finished.get()) {
sendEvent(TPPAState.SOLVING, event)
}
}

override fun execute(cancellationToken: CancellationToken) {
Expand All @@ -81,8 +88,12 @@ data class TPPATask(
camera, mount, request
)

finished.set(false)
elapsedTime.start()

rightAscension = mount?.rightAscension ?: 0.0
declination = mount?.declination ?: 0.0

while (!cancellationToken.isDone) {
cancellationToken.waitForPause()

Expand All @@ -91,12 +102,16 @@ data class TPPATask(
// SLEWING.
if (mount != null) {
if (alignment.state in 1..2 && !mountMoveState[alignment.state]) {
MountMoveTask(mount, guideRequest).use {
MountMoveTask(mount, mountMoveRequest).use {
sendEvent(TPPAState.SLEWING)
it.execute(cancellationToken)
mountMoveState[alignment.state] = true
}

rightAscension = mount.rightAscension
declination = mount.declination
sendEvent(TPPAState.SLEWED)

LOG.info("TPPA slewed. rightAscension={}, declination={}", mount.rightAscension.formatHMS(), mount.declination.formatSignedDMS())
}
}
Expand Down Expand Up @@ -135,6 +150,8 @@ data class TPPATask(
when (result) {
is ThreePointPolarAlignmentResult.NeedMoreMeasurement -> {
noSolutionAttempts = 0
rightAscension = result.rightAscension
declination = result.declination
sendEvent(TPPAState.SOLVED)
continue
}
Expand All @@ -153,8 +170,11 @@ data class TPPATask(
is ThreePointPolarAlignmentResult.Measured -> {
noSolutionAttempts = 0

rightAscension = result.rightAscension
declination = result.declination
azimuthError = result.azimuth
altitudeError = result.altitude
totalError = hypot(azimuthError, altitudeError)

azimuthErrorDirection = when {
azimuthError > 0 -> if (latitude > 0) "🠔 Move LEFT/WEST" else "🠔 Move LEFT/EAST"
Expand All @@ -181,14 +201,15 @@ data class TPPATask(
}
}

finished.set(true)
elapsedTime.stop()

if (request.stopTrackingWhenDone) {
mount?.tracking(false)
}

sendEvent(TPPAState.FINISHED)

elapsedTime.stop()

LOG.info("TPPA finished. camera={}, mount={}, request={}", camera, mount, request)
}

Expand All @@ -199,7 +220,7 @@ data class TPPATask(

private fun sendEvent(state: TPPAState, capture: CameraCaptureEvent? = null) {
val event = TPPAEvent(
camera, state,
camera, state, rightAscension, declination,
azimuthError, altitudeError, totalError,
azimuthErrorDirection, altitudeErrorDirection,
processCameraCaptureEvent(capture),
Expand Down
10 changes: 10 additions & 0 deletions api/src/main/kotlin/nebulosa/api/mounts/MountMoveRequest.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package nebulosa.api.mounts

import nebulosa.guiding.GuideDirection
import java.time.Duration

data class MountMoveRequest(
@JvmField val direction: GuideDirection,
@JvmField val duration: Duration,
@JvmField val speed: String? = null,
)
11 changes: 7 additions & 4 deletions api/src/main/kotlin/nebulosa/api/mounts/MountMoveTask.kt
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
package nebulosa.api.mounts

import io.reactivex.rxjava3.functions.Consumer
import nebulosa.api.guiding.GuidePulseRequest
import nebulosa.api.tasks.Task
import nebulosa.api.tasks.delay.DelayEvent
import nebulosa.api.tasks.delay.DelayTask
Expand All @@ -14,7 +13,7 @@ import nebulosa.log.loggerFor

data class MountMoveTask(
@JvmField val mount: Mount,
@JvmField val request: GuidePulseRequest,
@JvmField val request: MountMoveRequest,
) : Task<MountMoveEvent>(), CancellationListener, Consumer<DelayEvent> {

private val delayTask = DelayTask(request.duration)
Expand All @@ -25,9 +24,13 @@ data class MountMoveTask(

override fun execute(cancellationToken: CancellationToken) {
if (!cancellationToken.isDone && request.duration.toMillis() > 0) {
mount.slewRates.takeIf { !request.speed.isNullOrBlank() }
?.find { it.name == request.speed || it.label == request.speed }
?.also { mount.slewRate(it) }

mount.move(request.direction, true)

LOG.info("Mount Move started. mount={}, duration={}, direction={}", mount, request.duration.toMillis(), request.direction)
LOG.info("Mount Move started. mount={}, request={}", mount, request)

try {
cancellationToken.listen(this)
Expand All @@ -37,7 +40,7 @@ data class MountMoveTask(
cancellationToken.unlisten(this)
}

LOG.info("Mount Move finished. mount={}, duration={}, direction={}", mount, request.duration.toMillis(), request.direction)
LOG.info("Mount Move finished. mount={}, request={}", mount, request)
}
}

Expand Down
Binary file modified desktop/alignment.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
7 changes: 6 additions & 1 deletion desktop/src/app/alignment/alignment.component.html
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,13 @@
icon="mdi mdi-connection" size="small" severity="info" pTooltip="Connect" tooltipPosition="bottom" [text]="true" />
}
</div>
<div class="col-12 pt-0 text-sm text-gray-400 flex align-items-center mt-1 gap-1 text-sm" style="min-height: 25px;">
<div class="col-12 relative pt-0 text-sm text-gray-400 flex align-items-center mt-1 gap-1 text-sm" style="min-height: 25px;">
<neb-camera-exposure #cameraExposure [info]="status" />

<div *ngIf="alignmentMethod === 'TPPA'" class="absolute flex flex-row align-items-center gap-1" style="right: 8px; top: -2px">
<p-tag value="RA: {{ tppaRightAscension }}" [severity]="tppaFailed ? 'danger' : 'info'" />
<p-tag value="DEC: {{ tppaDeclination }}" [severity]="tppaFailed ? 'danger' : 'info'" />
</div>
</div>
</div>
<div class="grid">
Expand Down
12 changes: 12 additions & 0 deletions desktop/src/app/alignment/alignment.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,9 @@ export class AlignmentComponent implements AfterViewInit, OnDestroy {
}

readonly plateSolverTypes = Array.from(DEFAULT_SOLVER_TYPES)
tppaFailed = false
tppaRightAscension: Angle = `00h00m00s`
tppaDeclination: Angle = `00°00'00"`
tppaAzimuthError: Angle = `00°00'00"`
tppaAzimuthErrorDirection = ''
tppaAltitudeError: Angle = `00°00'00"`
Expand Down Expand Up @@ -177,6 +180,9 @@ export class AlignmentComponent implements AfterViewInit, OnDestroy {
this.running = event.state !== 'FINISHED'

if (event.state === 'COMPUTED') {
this.tppaFailed = false
this.tppaRightAscension = event.rightAscension
this.tppaDeclination = event.declination
this.tppaAzimuthError = event.azimuthError
this.tppaAltitudeError = event.altitudeError
this.tppaAzimuthErrorDirection = event.azimuthErrorDirection
Expand All @@ -189,6 +195,12 @@ export class AlignmentComponent implements AfterViewInit, OnDestroy {
electron.autoResizeWindow()
} else if (event.state === 'SOLVING' && event.capture && event.capture.state !== 'CAPTURE_FINISHED') {
this.cameraExposure.handleCameraCaptureEvent(event.capture, true)
} else if (event.state === 'SOLVED' || event.state === 'SLEWED') {
this.tppaFailed = false
this.tppaRightAscension = event.rightAscension
this.tppaDeclination = event.declination
} else if (event.state === 'FAILED') {
this.tppaFailed = true
}
})
}
Expand Down
4 changes: 3 additions & 1 deletion desktop/src/shared/types/alignment.types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ export type Hemisphere = 'NORTHERN' | 'SOUTHERN'

export type DARVState = 'IDLE' | 'INITIAL_PAUSE' | 'FORWARD' | 'BACKWARD'

export type TPPAState = 'IDLE' | 'SLEWING' | 'SOLVING' | 'SOLVED' | 'COMPUTED' | 'FINISHED' | 'FAILED'
export type TPPAState = 'IDLE' | 'SLEWING' | 'SLEWED' | 'SOLVING' | 'SOLVED' | 'COMPUTED' | 'FINISHED' | 'FAILED'

export type AlignmentMethod = 'DARV' | 'TPPA'

Expand Down Expand Up @@ -63,6 +63,8 @@ export interface TPPAStart {
export interface TPPAEvent extends MessageEvent {
camera: Camera
state: TPPAState
rightAscension: Angle
declination: Angle
azimuthError: Angle
altitudeError: Angle
totalError: Angle
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ internal data class PolarErrorDetermination(
if (isNorthern && x < 0 || !isNorthern && x > 0) -normalized else normalized
}

@JvmField val errorPosition = Position(plane, longitude, latitude)
@JvmField val errorPosition = Position(plane)

fun compute(): DoubleArray {
val altitudeError: Double
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ import nebulosa.erfa.eraAtco13
import nebulosa.math.Angle
import nebulosa.math.ONE_ATM
import nebulosa.math.Vector3D
import nebulosa.time.CurrentTime
import nebulosa.time.IERS
import nebulosa.time.InstantOfTime
import kotlin.math.cos
Expand All @@ -21,7 +20,7 @@ internal data class Position(
operator fun invoke(
rightAscension: Angle, declination: Angle,
longitude: Angle, latitude: Angle,
time: InstantOfTime = CurrentTime,
time: InstantOfTime,
compensateRefraction: Boolean = false,
): Position {
// SOFA.CelestialToTopocentric.
Expand All @@ -31,25 +30,20 @@ internal data class Position(
// @formatter:off
val (b) = eraAtco13(rightAscension, declination, 0.0, 0.0, 0.0, 0.0, time.utc.whole, time.utc.fraction, dut1, longitude, latitude, 0.0, xp, yp, pressure, 15.0, 0.5, 0.55)
// @formatter:on
val topocentric = Topocentric(b[0], PIOVERTWO - b[1], longitude, latitude)
val topocentric = Topocentric(b[0], PIOVERTWO - b[1])
// val vector = CartesianCoordinate.of(-b[0], b[1], 1.0)
val theta = -b[0]
val phi = b[1]
val x = cos(theta) * sin(phi)
val y = sin(theta) * sin(phi)
val sp = sin(phi)
val x = cos(theta) * sp
val y = sin(theta) * sp
val z = cos(phi)
return Position(topocentric, Vector3D(x, y, z))
}

operator fun invoke(
vector: Vector3D, longitude: Angle, latitude: Angle,
): Position {
val topocentric = if (vector.x == 0.0 && vector.y == 0.0) {
Topocentric(0.0, PIOVERTWO, longitude, latitude)
} else {
Topocentric(-vector.longitude, PIOVERTWO - vector.latitude, longitude, latitude)
}

operator fun invoke(vector: Vector3D): Position {
val topocentric = if (vector.x == 0.0 && vector.y == 0.0) Topocentric.ZERO
else Topocentric(-vector.longitude, PIOVERTWO - vector.latitude)
return Position(topocentric, vector)
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,12 @@
package nebulosa.alignment.polar.point.three

import nebulosa.constants.PIOVERTWO
import nebulosa.math.Angle

data class Topocentric(
@JvmField val azimuth: Angle, @JvmField val altitude: Angle,
@JvmField val longitude: Angle, @JvmField val latitude: Angle,
)
data class Topocentric(@JvmField val azimuth: Angle, @JvmField val altitude: Angle) {

companion object {

@JvmStatic val ZERO = Topocentric(0.0, PIOVERTWO)
}
}
Loading

0 comments on commit 4946a1d

Please sign in to comment.