Finish plotter demo
This commit is contained in:
parent
a2b7d1ecb0
commit
4a5f5fab8c
@ -9,6 +9,7 @@ import space.kscience.controls.constructor.units.numerical
|
||||
import space.kscience.dataforge.context.Context
|
||||
import space.kscience.dataforge.meta.MetaConverter
|
||||
|
||||
//TODO use current as input
|
||||
|
||||
public class Drive(
|
||||
context: Context,
|
||||
|
@ -9,7 +9,7 @@ import space.kscience.dataforge.context.Context
|
||||
import space.kscience.dataforge.meta.MetaConverter
|
||||
import kotlin.math.max
|
||||
import kotlin.math.min
|
||||
import kotlin.math.roundToInt
|
||||
import kotlin.math.roundToLong
|
||||
import kotlin.time.DurationUnit
|
||||
|
||||
/**
|
||||
@ -22,25 +22,27 @@ import kotlin.time.DurationUnit
|
||||
public class StepDrive(
|
||||
context: Context,
|
||||
ticksPerSecond: MutableDeviceState<Double>,
|
||||
target: MutableDeviceState<Int> = MutableDeviceState(0),
|
||||
private val writeTicks: suspend (ticks: Int, speed: Double) -> Unit = { _, _ -> },
|
||||
target: MutableDeviceState<Long> = MutableDeviceState(0),
|
||||
private val writeTicks: suspend (ticks: Long, speed: Double) -> Unit = { _, _ -> },
|
||||
) : DeviceConstructor(context) {
|
||||
|
||||
public val target: MutableDeviceState<Int> by property(MetaConverter.int, target)
|
||||
public val target: MutableDeviceState<Long> by property(MetaConverter.long, target)
|
||||
|
||||
public val speed: MutableDeviceState<Double> by property(MetaConverter.double, ticksPerSecond)
|
||||
|
||||
private val positionState = stateOf(target.value)
|
||||
|
||||
public val position: DeviceState<Int> by property(MetaConverter.int, positionState)
|
||||
public val position: DeviceState<Long> by property(MetaConverter.long, positionState)
|
||||
|
||||
|
||||
//FIXME round to zero problem
|
||||
private val ticker = onTimer(reads = setOf(target, position), writes = setOf(position)) { prev, next ->
|
||||
val tickSpeed = ticksPerSecond.value
|
||||
val timeDelta = (next - prev).toDouble(DurationUnit.SECONDS)
|
||||
val ticksDelta: Int = target.value - position.value
|
||||
val steps: Int = when {
|
||||
ticksDelta > 0 -> min(ticksDelta, (timeDelta * tickSpeed).roundToInt())
|
||||
ticksDelta < 0 -> max(ticksDelta, -(timeDelta * tickSpeed).roundToInt())
|
||||
val ticksDelta: Long = target.value - position.value
|
||||
val steps: Long = when {
|
||||
ticksDelta > 0 -> min(ticksDelta, (timeDelta * tickSpeed).roundToLong())
|
||||
ticksDelta < 0 -> max(ticksDelta, -(timeDelta * tickSpeed).roundToLong())
|
||||
else -> return@onTimer
|
||||
}
|
||||
writeTicks(steps, tickSpeed)
|
||||
@ -55,6 +57,6 @@ public fun StepDrive.angle(
|
||||
step: NumericalValue<Degrees>,
|
||||
zero: NumericalValue<Degrees> = NumericalValue(0),
|
||||
): DeviceState<NumericalValue<Degrees>> = position.map {
|
||||
zero + it.toDouble() * step
|
||||
zero + it * step
|
||||
}
|
||||
|
||||
|
@ -25,11 +25,9 @@ public class Inertia<U : UnitsOfMeasurement, V : UnitsOfMeasurement>(
|
||||
registerState(velocity)
|
||||
}
|
||||
|
||||
private val movementTimer = timer(timerPrecision)
|
||||
|
||||
private var currentForce = force.value
|
||||
|
||||
private val movement = movementTimer.onChange { prev, next ->
|
||||
private val movement = onTimer (timerPrecision) { prev, next ->
|
||||
val dtSeconds = (next - prev).toDouble(DurationUnit.SECONDS)
|
||||
|
||||
// compute new value based on velocity and acceleration from the previous step
|
||||
@ -57,21 +55,6 @@ public class Inertia<U : UnitsOfMeasurement, V : UnitsOfMeasurement>(
|
||||
position = position,
|
||||
velocity = velocity
|
||||
)
|
||||
//
|
||||
//
|
||||
// public fun linear(
|
||||
// context: Context,
|
||||
// force: DeviceState<NumericalValue<Newtons>>,
|
||||
// mass: NumericalValue<Kilograms>,
|
||||
// initialPosition: NumericalValue<Meters>,
|
||||
// initialVelocity: NumericalValue<MetersPerSecond> = NumericalValue(0),
|
||||
// ): Inertia<Meters, MetersPerSecond> = Inertia(
|
||||
// context = context,
|
||||
// force = force.values(),
|
||||
// inertia = mass.value,
|
||||
// position = MutableDeviceState(initialPosition),
|
||||
// velocity = MutableDeviceState(initialVelocity)
|
||||
// )
|
||||
|
||||
public fun circular(
|
||||
context: Context,
|
||||
@ -86,19 +69,5 @@ public class Inertia<U : UnitsOfMeasurement, V : UnitsOfMeasurement>(
|
||||
position = position,
|
||||
velocity = velocity
|
||||
)
|
||||
//
|
||||
// public fun circular(
|
||||
// context: Context,
|
||||
// force: DeviceState<NumericalValue<NewtonsMeters>>,
|
||||
// momentOfInertia: NumericalValue<KgM2>,
|
||||
// initialPosition: NumericalValue<Degrees>,
|
||||
// initialVelocity: NumericalValue<DegreesPerSecond> = NumericalValue(0),
|
||||
// ): Inertia<Degrees, DegreesPerSecond> = Inertia(
|
||||
// context = context,
|
||||
// force = force.values(),
|
||||
// inertia = momentOfInertia.value,
|
||||
// position = MutableDeviceState(initialPosition),
|
||||
// velocity = MutableDeviceState(initialVelocity)
|
||||
// )
|
||||
}
|
||||
}
|
@ -12,17 +12,17 @@ public class ScrewDrive(
|
||||
public val leverage: NumericalValue<Meters>,
|
||||
) : ModelConstructor(context) {
|
||||
|
||||
public fun transformForce(
|
||||
stateOfForce: DeviceState<NumericalValue<NewtonsMeters>>,
|
||||
): DeviceState<NumericalValue<Newtons>> = DeviceState.map(stateOfForce) {
|
||||
NumericalValue(it.value * leverage.value/2/ PI)
|
||||
public fun torqueToForce(
|
||||
stateOfMomentum: DeviceState<NumericalValue<NewtonsMeters>>,
|
||||
): DeviceState<NumericalValue<Newtons>> = DeviceState.map(stateOfMomentum) { momentum ->
|
||||
NumericalValue(momentum.value / leverage.value )
|
||||
}
|
||||
|
||||
public fun transformOffset(
|
||||
public fun degreesToMeters(
|
||||
stateOfAngle: DeviceState<NumericalValue<Degrees>>,
|
||||
offset: NumericalValue<Meters> = NumericalValue(0),
|
||||
): DeviceState<NumericalValue<Meters>> = DeviceState.map(stateOfAngle) {
|
||||
offset + NumericalValue(it.value * leverage.value/2/ PI)
|
||||
): DeviceState<NumericalValue<Meters>> = DeviceState.map(stateOfAngle) { degrees ->
|
||||
offset + NumericalValue(degrees.value * 2 * PI / 360 *leverage.value )
|
||||
}
|
||||
|
||||
}
|
@ -1,17 +0,0 @@
|
||||
package space.kscience.controls.constructor.models
|
||||
|
||||
import space.kscience.controls.constructor.ModelConstructor
|
||||
import space.kscience.controls.constructor.MutableDeviceState
|
||||
import space.kscience.controls.constructor.stateOf
|
||||
import space.kscience.controls.constructor.units.Meters
|
||||
import space.kscience.controls.constructor.units.NumericalValue
|
||||
import space.kscience.dataforge.context.Context
|
||||
|
||||
public class XYPosition(
|
||||
context: Context,
|
||||
initialX: NumericalValue<Meters> = NumericalValue(0.0),
|
||||
initialY: NumericalValue<Meters> = NumericalValue(0.0),
|
||||
) : ModelConstructor(context) {
|
||||
public val x: MutableDeviceState<NumericalValue<Meters>> = stateOf(initialX)
|
||||
public val y: MutableDeviceState<NumericalValue<Meters>> = stateOf(initialY)
|
||||
}
|
@ -0,0 +1,8 @@
|
||||
package space.kscience.controls.constructor.models
|
||||
|
||||
import space.kscience.controls.constructor.units.NumericalValue
|
||||
import space.kscience.controls.constructor.units.UnitsOfMeasurement
|
||||
|
||||
public data class XY<U: UnitsOfMeasurement>(val x: NumericalValue<U>, val y: NumericalValue<U>)
|
||||
|
||||
public data class XYZ<U: UnitsOfMeasurement>(val x: NumericalValue<U>, val y: NumericalValue<U>, val z: NumericalValue<U>)
|
@ -94,7 +94,7 @@ internal fun createLinearDriveModel(
|
||||
//create a drive model with zero starting force
|
||||
val drive = Drive(context)
|
||||
|
||||
//a screw drive to converse a rotational moment into a linear one
|
||||
//a screw drive to convert a rotational moment into a force
|
||||
val screwDrive = ScrewDrive(context, leverage)
|
||||
|
||||
|
||||
@ -107,7 +107,7 @@ internal fun createLinearDriveModel(
|
||||
*/
|
||||
val inertiaModel = Inertia.linear(
|
||||
context = context,
|
||||
force = screwDrive.transformForce(drive.force),
|
||||
force = screwDrive.torqueToForce(drive.force),
|
||||
mass = mass,
|
||||
position = position
|
||||
)
|
||||
@ -266,7 +266,7 @@ fun main() = application {
|
||||
yAxisModel = rememberDoubleLinearAxisModel((range.start - 1.0)..(range.endInclusive + 1.0)),
|
||||
xAxisTitle = { Text("Time in seconds relative to current") },
|
||||
xAxisLabels = { it: Instant ->
|
||||
androidx.compose.material3.Text(
|
||||
Text(
|
||||
(clock.now() - it).toDouble(
|
||||
DurationUnit.SECONDS
|
||||
).toString(2)
|
||||
|
@ -18,7 +18,6 @@ import space.kscience.controls.constructor.MutableDeviceState
|
||||
import space.kscience.controls.constructor.device
|
||||
import space.kscience.controls.constructor.devices.StepDrive
|
||||
import space.kscience.controls.constructor.devices.angle
|
||||
import space.kscience.controls.constructor.models.RangeState
|
||||
import space.kscience.controls.constructor.models.ScrewDrive
|
||||
import space.kscience.controls.constructor.models.coerceIn
|
||||
import space.kscience.controls.constructor.units.*
|
||||
@ -38,21 +37,26 @@ class Plotter(
|
||||
val xDrive by device(xDrive)
|
||||
val yDrive by device(yDrive)
|
||||
|
||||
public fun moveToXY(x: Int, y: Int) {
|
||||
xDrive.target.value = x
|
||||
yDrive.target.value = y
|
||||
public fun moveToXY(x: Number, y: Number) {
|
||||
xDrive.target.value = x.toLong()
|
||||
yDrive.target.value = y.toLong()
|
||||
}
|
||||
|
||||
// val position = combineState(xDrive.position, yDrive.position) { x, y ->
|
||||
// space.kscience.controls.constructor.models.XY(x, y)
|
||||
// }
|
||||
|
||||
//TODO add calibration
|
||||
|
||||
// TODO add draw as action
|
||||
}
|
||||
|
||||
suspend fun Plotter.modernArt(xRange: IntRange, yRange: IntRange) {
|
||||
while (isActive){
|
||||
while (isActive) {
|
||||
val randomX = Random.nextInt(xRange.first, xRange.last)
|
||||
val randomY = Random.nextInt(xRange.first, xRange.last)
|
||||
val randomY = Random.nextInt(yRange.first, yRange.last)
|
||||
moveToXY(randomX, randomY)
|
||||
//TODO wait for position instead of custom delay
|
||||
delay(500)
|
||||
paint(Color(Random.nextInt()))
|
||||
}
|
||||
@ -80,8 +84,8 @@ suspend fun Plotter.square(xRange: IntRange, yRange: IntRange) {
|
||||
|
||||
private val xRange = NumericalValue<Meters>(-0.5)..NumericalValue<Meters>(0.5)
|
||||
private val yRange = NumericalValue<Meters>(-0.5)..NumericalValue<Meters>(0.5)
|
||||
private val ticksPerSecond = MutableDeviceState(250.0)
|
||||
private val step = NumericalValue<Degrees>(1.2)
|
||||
private val ticksPerSecond = MutableDeviceState(3000.0)
|
||||
private val step = NumericalValue<Degrees>(1.8)
|
||||
|
||||
|
||||
private data class PlotterPoint(
|
||||
@ -106,13 +110,13 @@ suspend fun main() = application {
|
||||
/* Here goes the device definition block */
|
||||
|
||||
|
||||
val xScrewDrive = ScrewDrive(context, NumericalValue(0.01))
|
||||
val xDrive = StepDrive(context, ticksPerSecond)
|
||||
val x: RangeState<NumericalValue<Meters>> = xScrewDrive.transformOffset(xDrive.angle(step)).coerceIn(xRange)
|
||||
val xTransmission = ScrewDrive(context, NumericalValue(0.01))
|
||||
val x = xTransmission.degreesToMeters(xDrive.angle(step)).coerceIn(xRange)
|
||||
|
||||
val yScrewDrive = ScrewDrive(context, NumericalValue(0.01))
|
||||
val yDrive = StepDrive(context, ticksPerSecond)
|
||||
val y: RangeState<NumericalValue<Meters>> = yScrewDrive.transformOffset(yDrive.angle(step)).coerceIn(yRange)
|
||||
val yTransmission = ScrewDrive(context, NumericalValue(0.01))
|
||||
val y = yTransmission.degreesToMeters(yDrive.angle(step)).coerceIn(yRange)
|
||||
|
||||
val plotter = Plotter(context, xDrive, yDrive) { color ->
|
||||
println("Point X: ${x.value.value}, Y: ${y.value.value}, color: $color")
|
||||
@ -134,10 +138,12 @@ suspend fun main() = application {
|
||||
}
|
||||
}
|
||||
|
||||
/* run program */
|
||||
|
||||
launch {
|
||||
val range = -100..100
|
||||
plotter.modernArt(range, range)
|
||||
//plotter.square(range, range)
|
||||
val range = -1000..1000
|
||||
// plotter.modernArt(range, range)
|
||||
plotter.square(range, range)
|
||||
}
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user