Skip to content

Commit

Permalink
Merge branch 'dev'
Browse files Browse the repository at this point in the history
  • Loading branch information
tiagohm committed Oct 17, 2024
2 parents fd15b3f + 1e77f69 commit 97b7006
Show file tree
Hide file tree
Showing 32 changed files with 691 additions and 557 deletions.
29 changes: 17 additions & 12 deletions api/src/main/kotlin/nebulosa/api/autofocus/AutoFocusJob.kt
Original file line number Diff line number Diff line change
@@ -1,9 +1,7 @@
package nebulosa.api.autofocus

import nebulosa.api.cameras.*
import nebulosa.api.focusers.BacklashCompensationFocuserMoveTask
import nebulosa.api.focusers.BacklashCompensationMode
import nebulosa.api.focusers.FocuserEventAware
import nebulosa.api.focusers.*
import nebulosa.api.message.MessageEvent
import nebulosa.autofocus.AutoFocus
import nebulosa.autofocus.AutoFocusListener
Expand Down Expand Up @@ -46,7 +44,7 @@ data class AutoFocusJob(
)

private val cameraExposureTask = CameraExposureTask(this, camera, cameraRequest)
private val backlashCompensationFocuserMoveTask = BacklashCompensationFocuserMoveTask(this, focuser, 0, request.backlashCompensation)
private val backlashCompensator = BacklashCompensator(request.backlashCompensation, focuser.maxPosition)
private val reverse = request.backlashCompensation.mode == BacklashCompensationMode.OVERSHOOT && request.backlashCompensation.backlashIn > 0
private val finished = AtomicBoolean()

Expand All @@ -57,6 +55,7 @@ data class AutoFocusJob(
)

@Volatile private var initialFocusPosition = 0
@Volatile private var focuserTask: FocuserTask? = null

@JvmField val status = AutoFocusEvent(camera)

Expand All @@ -74,8 +73,8 @@ data class AutoFocusJob(
add(cameraExposureTask)
}
is AutoFocusResult.MoveFocuser -> {
backlashCompensationFocuserMoveTask.position = if (relative) focuser.position + position else position
add(backlashCompensationFocuserMoveTask)
val position = if (relative) focuser.position + position else position
add(BacklashCompensationFocuserMoveTask(this@AutoFocusJob, focuser, position, backlashCompensator))
}
is AutoFocusResult.Completed -> {
status.determinedFocusPoint = determinedFocusPoint
Expand All @@ -102,7 +101,7 @@ data class AutoFocusJob(
}

override fun handleFocuserEvent(event: FocuserEvent) {
backlashCompensationFocuserMoveTask.handleFocuserEvent(event)
focuserTask?.handleFocuserEvent(event)
}

override fun beforeStart() {
Expand All @@ -115,10 +114,12 @@ data class AutoFocusJob(
}

override fun beforeTask(task: Task) {
if (task === backlashCompensationFocuserMoveTask) {
if (task is FocuserTask) {
focuserTask = task
status.state = AutoFocusState.MOVING
status.send()
} else if (task === cameraExposureTask) {
focuserTask = null
status.state = AutoFocusState.EXPOSURING
status.send()
}
Expand Down Expand Up @@ -201,15 +202,19 @@ data class AutoFocusJob(

private fun restoringFocuserToInitialPosition() {
finished.set(true)
backlashCompensationFocuserMoveTask.position = initialFocusPosition
backlashCompensationFocuserMoveTask.run()

focuserTask = BacklashCompensationFocuserMoveTask(this, focuser, initialFocusPosition, backlashCompensator)
focuserTask!!.run()

cameraExposureTask.run()
}

private fun movingFocuserToDeterminedFocusPosition(position: CurvePoint) {
finished.set(true)
backlashCompensationFocuserMoveTask.position = position.x.roundToInt()
backlashCompensationFocuserMoveTask.run()

focuserTask = BacklashCompensationFocuserMoveTask(this, focuser, position.x.roundToInt(), backlashCompensator)
focuserTask!!.run()

cameraExposureTask.run()
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,8 @@ data class CameraStartCaptureRequest(
@JvmField val shutterPosition: Int = 0,
// Focuser.
@JvmField val focusOffset: Int = 0,
// Rotator.
@JvmField val angle: Double = -1.0, // deg
// Others.
@JvmField val namingFormat: CameraCaptureNamingFormat = CameraCaptureNamingFormat.DEFAULT,
) : Validatable {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package nebulosa.api.focusers
import nebulosa.indi.device.focuser.FocuserEvent
import nebulosa.indi.device.focuser.FocuserMoveFailed
import nebulosa.indi.device.focuser.FocuserMovingChanged
import nebulosa.indi.device.focuser.FocuserPositionChanged
import nebulosa.job.manager.Job
import nebulosa.log.d
import nebulosa.log.loggerFor
Expand All @@ -22,7 +23,7 @@ sealed class AbstractFocuserMoveTask : FocuserTask, CancellationListener {
if (event.device === focuser) {
when (event) {
is FocuserMovingChanged -> if (event.device.moving) moving = true else if (moving) latch.reset()
// is FocuserPositionChanged -> if (moving && !event.device.moving) latch.reset()
is FocuserPositionChanged -> if (moving && !event.device.moving) latch.reset()
is FocuserMoveFailed -> latch.reset()
}
}
Expand All @@ -36,6 +37,7 @@ sealed class AbstractFocuserMoveTask : FocuserTask, CancellationListener {
if (!job.isCancelled && focuser.connected && !focuser.moving && canMove()) {
LOG.d("Focuser move started. focuser={}", focuser)
latch.countUp()
moving = true
move()
latch.await()
moving = false
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,139 +3,54 @@ package nebulosa.api.focusers
import nebulosa.indi.device.focuser.Focuser
import nebulosa.indi.device.focuser.FocuserEvent
import nebulosa.job.manager.Job
import nebulosa.log.d
import nebulosa.log.loggerFor
import nebulosa.log.w
import nebulosa.util.concurrency.cancellation.CancellationSource

/**
* This task will wrap an absolute backlash [compensation] model around the [focuser].
* This task will wrap an absolute backlash [compensator] model around the [focuser].
* On each move an absolute backlash compensation value will be applied, if the focuser changes its moving direction
* The returned position will then accommodate for this backlash and simulating the position without backlash.
*/
data class BacklashCompensationFocuserMoveTask(
@JvmField val job: Job,
override val focuser: Focuser,
@JvmField var position: Int,
@JvmField val compensation: BacklashCompensation,
@JvmField val position: Int,
@JvmField val compensator: BacklashCompensator,
) : FocuserTask {

enum class OvershootDirection {
NONE,
IN,
OUT,
}

@Volatile private var offset = 0
@Volatile private var lastDirection = OvershootDirection.NONE

private val task = FocuserMoveAbsoluteTask(job, focuser, 0)
@Volatile private var task: FocuserTask? = null

/**
* Returns the adjusted position based on the amount of backlash compensation.
*/
val adjustedPosition
get() = focuser.position - offset
get() = compensator.adjustedPosition(focuser.position)

override fun handleFocuserEvent(event: FocuserEvent) {
task.handleFocuserEvent(event)
task?.handleFocuserEvent(event)
}

override fun onCancel(source: CancellationSource) {
task.onCancel(source)
task?.onCancel(source)
}

override fun onPause(paused: Boolean) {
task.onPause(paused)
task?.onPause(paused)
}

override fun run() {
if (!job.isCancelled && focuser.connected && !focuser.moving) {
val startPosition = focuser.position

val newPosition = when (compensation.mode) {
BacklashCompensationMode.ABSOLUTE -> {
val adjustedTargetPosition = position + offset

if (adjustedTargetPosition < 0) {
offset = 0
0
} else if (adjustedTargetPosition > focuser.maxPosition) {
offset = 0
focuser.maxPosition
} else {
val backlashCompensation = calculateAbsoluteBacklashCompensation(startPosition, adjustedTargetPosition)
offset += backlashCompensation
adjustedTargetPosition + backlashCompensation
}
}
BacklashCompensationMode.OVERSHOOT -> {
val backlashCompensation = calculateOvershootBacklashCompensation(startPosition, position)

if (backlashCompensation != 0) {
val overshoot = position + backlashCompensation
val targetPositions = compensator.compute(position, focuser.position)

if (overshoot < 0) {
LOG.w("overshooting position is below minimum 0, skipping overshoot")
} else if (overshoot > focuser.maxPosition) {
LOG.w("overshooting position is above maximum {}, skipping overshoot", focuser.maxPosition)
} else {
LOG.d("overshooting from {} to overshoot position {} using a compensation of {}. Moving back to position {}", startPosition, overshoot, backlashCompensation, position)
moveFocuser(overshoot)
}
}

position
}
else -> {
position
}
for (position in targetPositions) {
moveFocuser(position)
}

LOG.d("moving to position {} using {} backlash compensation", newPosition, compensation.mode)

moveFocuser(newPosition)
}
}

private fun moveFocuser(position: Int) {
if (position > 0 && position <= focuser.maxPosition) {
lastDirection = determineMovingDirection(focuser.position, position)
task.position = position
task.run()
if (!job.isCancelled && position in 0..focuser.maxPosition) {
task = FocuserMoveAbsoluteTask(job, focuser, position)
task!!.run()
}
}

private fun determineMovingDirection(prevPosition: Int, newPosition: Int): OvershootDirection {
return if (newPosition > prevPosition) OvershootDirection.OUT
else if (newPosition < prevPosition) OvershootDirection.IN
else lastDirection
}

private fun calculateAbsoluteBacklashCompensation(lastPosition: Int, newPosition: Int): Int {
val direction = determineMovingDirection(lastPosition, newPosition)

return if (direction == OvershootDirection.IN && lastDirection == OvershootDirection.OUT) {
LOG.d("Focuser is reversing direction from outwards to inwards")
-compensation.backlashIn
} else if (direction == OvershootDirection.OUT && lastDirection === OvershootDirection.IN) {
LOG.d("Focuser is reversing direction from inwards to outwards")
compensation.backlashOut
} else {
0
}
}

private fun calculateOvershootBacklashCompensation(lastPosition: Int, newPosition: Int): Int {
val direction = determineMovingDirection(lastPosition, newPosition)

return if (direction == OvershootDirection.IN && compensation.backlashIn != 0) -compensation.backlashIn
else if (direction == OvershootDirection.OUT && compensation.backlashOut != 0) compensation.backlashOut
else 0
}

companion object {

@JvmStatic private val LOG = loggerFor<BacklashCompensationFocuserMoveTask>()
}
}
118 changes: 118 additions & 0 deletions api/src/main/kotlin/nebulosa/api/focusers/BacklashCompensator.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
package nebulosa.api.focusers

import nebulosa.log.d
import nebulosa.log.loggerFor
import nebulosa.log.w
import kotlin.math.max
import kotlin.math.min

// https://bitbucket.org/Isbeorn/nina

data class BacklashCompensator(
@JvmField val compensation: BacklashCompensation,
@JvmField val maxPosition: Int,
) {

enum class OvershootDirection {
NONE,
IN,
OUT,
}

@Volatile var lastDirection = OvershootDirection.NONE
private set

@Volatile var offset = 0
private set

fun compute(targetPosition: Int, currentPosition: Int): IntArray {
var newPosition = targetPosition

when (compensation.mode) {
BacklashCompensationMode.ABSOLUTE -> {
val adjustedTargetPosition = targetPosition + offset

if (adjustedTargetPosition < 0) {
offset = 0
newPosition = 0
} else if (adjustedTargetPosition > maxPosition) {
offset = 0
newPosition = maxPosition
} else {
val backlashCompensation = calculateAbsoluteBacklashCompensation(currentPosition, adjustedTargetPosition)
offset += backlashCompensation
newPosition = max(0, min(adjustedTargetPosition + backlashCompensation, maxPosition))
}
}
BacklashCompensationMode.OVERSHOOT -> {
val backlashCompensation = calculateOvershootBacklashCompensation(currentPosition, targetPosition)

if (backlashCompensation != 0) {
val overshoot = targetPosition + backlashCompensation

if (overshoot < 0) {
LOG.w("overshooting position is below minimum 0, skipping overshoot")
} else if (overshoot > maxPosition) {
LOG.w("overshooting position is above maximum {}, skipping overshoot", maxPosition)
} else {
LOG.d("overshooting from {} to overshoot position {} using a compensation of {}. Moving back to position {}", currentPosition, overshoot, backlashCompensation, newPosition)

move(currentPosition, overshoot)
move(overshoot, newPosition)

return intArrayOf(overshoot, newPosition)
}
}
}
BacklashCompensationMode.NONE -> Unit
}

move(currentPosition, newPosition)

return intArrayOf(newPosition)
}

fun adjustedPosition(currentPosition: Int): Int {
return currentPosition - offset
}


@Suppress("NOTHING_TO_INLINE")
private inline fun move(currentPosition: Int, newPosition: Int) {
lastDirection = determineMovingDirection(currentPosition, newPosition)
LOG.d("moving to position {} using {} backlash compensation", newPosition, compensation.mode)
}

private fun determineMovingDirection(prevPosition: Int, newPosition: Int): OvershootDirection {
return if (newPosition > prevPosition) OvershootDirection.OUT
else if (newPosition < prevPosition) OvershootDirection.IN
else lastDirection
}

private fun calculateAbsoluteBacklashCompensation(lastPosition: Int, newPosition: Int): Int {
val direction = determineMovingDirection(lastPosition, newPosition)

return if (direction == OvershootDirection.IN && lastDirection == OvershootDirection.OUT) {
LOG.d("focuser is reversing direction from outwards to inwards")
-compensation.backlashIn
} else if (direction == OvershootDirection.OUT && lastDirection === OvershootDirection.IN) {
LOG.d("focuser is reversing direction from inwards to outwards")
compensation.backlashOut
} else {
0
}
}

private fun calculateOvershootBacklashCompensation(lastPosition: Int, newPosition: Int): Int {
val direction = determineMovingDirection(lastPosition, newPosition)

return if (direction == OvershootDirection.IN && compensation.backlashIn != 0) -compensation.backlashIn
else if (direction == OvershootDirection.OUT && compensation.backlashOut != 0) compensation.backlashOut
else 0
}

companion object {

@JvmStatic private val LOG = loggerFor<BacklashCompensator>()
}
}
Loading

0 comments on commit 97b7006

Please sign in to comment.