ПУУД с постоянным азимутом
This commit is contained in:
@@ -15,6 +15,8 @@
|
|||||||
- `ballistics.utils.astro.AstronomerJ2000`;
|
- `ballistics.utils.astro.AstronomerJ2000`;
|
||||||
- `ballistics.utils.math.Vector3D`, `Matrix3D`, `Quaternion3D`.
|
- `ballistics.utils.math.Vector3D`, `Matrix3D`, `Quaternion3D`.
|
||||||
|
|
||||||
|
В текущей модели спутника используется одна центральная линия визирования. Номер линии визирования и отклонения боковых линий из расчетного API удалены.
|
||||||
|
|
||||||
Основной API:
|
Основной API:
|
||||||
|
|
||||||
```kotlin
|
```kotlin
|
||||||
@@ -25,7 +27,6 @@ val calculator = AngularMotionCalculatorFactory.create(
|
|||||||
|
|
||||||
val result = calculator.calculate(
|
val result = calculator.calculate(
|
||||||
SurveyId(
|
SurveyId(
|
||||||
nlv = 4,
|
|
||||||
t = startTimeSeconds,
|
t = startTimeSeconds,
|
||||||
b = latitudeRad,
|
b = latitudeRad,
|
||||||
l = longitudeRad,
|
l = longitudeRad,
|
||||||
|
|||||||
+42
-57
@@ -25,7 +25,6 @@ abstract class AbstractPuudCalculator(
|
|||||||
protected val astro = AstronomerJ2000(earthType)
|
protected val astro = AstronomerJ2000(earthType)
|
||||||
|
|
||||||
protected fun validate(id: SurveyId) {
|
protected fun validate(id: SurveyId) {
|
||||||
require(id.nlv in 1..7) { "Номер линии визирования должен быть в диапазоне 1..7" }
|
|
||||||
require(id.duration >= 0.0) { "Длительность режима не может быть отрицательной" }
|
require(id.duration >= 0.0) { "Длительность режима не может быть отрицательной" }
|
||||||
require(config.focus > 0.0) { "Фокусное расстояние должно быть положительным" }
|
require(config.focus > 0.0) { "Фокусное расстояние должно быть положительным" }
|
||||||
}
|
}
|
||||||
@@ -36,27 +35,6 @@ abstract class AbstractPuudCalculator(
|
|||||||
protected fun pointAt(t: Double): OrbitalPoint =
|
protected fun pointAt(t: Double): OrbitalPoint =
|
||||||
stepper.calculate(t) ?: throw AngularMotionCalculationException("Ошибка выхода на заданное время: $t")
|
stepper.calculate(t) ?: throw AngularMotionCalculationException("Ошибка выхода на заданное время: $t")
|
||||||
|
|
||||||
protected fun lineAngle(nlv: Int): Double =
|
|
||||||
when (nlv) {
|
|
||||||
1 -> config.psk1Angle + config.lv1Angle
|
|
||||||
2 -> config.psk1Angle + config.lv2Angle
|
|
||||||
3 -> config.psk1Angle + config.lv3Angle
|
|
||||||
4 -> config.psk3Angle + config.lv4Angle
|
|
||||||
5 -> config.psk2Angle + config.lv5Angle
|
|
||||||
6 -> config.psk2Angle + config.lv6Angle
|
|
||||||
7 -> config.psk2Angle + config.lv7Angle
|
|
||||||
else -> 0.0
|
|
||||||
}
|
|
||||||
|
|
||||||
protected fun oepNlv(oep: Int): Int =
|
|
||||||
when (oep) {
|
|
||||||
1 -> 3
|
|
||||||
2 -> 1
|
|
||||||
3 -> 7
|
|
||||||
4 -> 5
|
|
||||||
else -> 4
|
|
||||||
}
|
|
||||||
|
|
||||||
protected fun sdiAt(sdi: List<Double>, elapsed: Double): Double =
|
protected fun sdiAt(sdi: List<Double>, elapsed: Double): Double =
|
||||||
if (sdi.isEmpty()) {
|
if (sdi.isEmpty()) {
|
||||||
-1.0
|
-1.0
|
||||||
@@ -129,10 +107,9 @@ abstract class AbstractPuudCalculator(
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun equationValue(id: SurveyId, t: Double): Double =
|
private fun equationValue(id: SurveyId, t: Double): Double =
|
||||||
orientOnPoint(id.nlv, t, id.b, id.l, id.h).tang - id.uprAngle
|
orientOnPoint(t, id.b, id.l, id.h).tang - id.uprAngle
|
||||||
|
|
||||||
protected fun orientOnPoint(
|
protected fun orientOnPoint(
|
||||||
nlv: Int,
|
|
||||||
t: Double,
|
t: Double,
|
||||||
b: Double,
|
b: Double,
|
||||||
l: Double,
|
l: Double,
|
||||||
@@ -141,12 +118,11 @@ abstract class AbstractPuudCalculator(
|
|||||||
): Orientation {
|
): Orientation {
|
||||||
val ka = pointAt(t)
|
val ka = pointAt(t)
|
||||||
val targetGsk = astro.earth.blh2xyz(b, l, h)
|
val targetGsk = astro.earth.blh2xyz(b, l, h)
|
||||||
val w = pointInOrbitalFrame(ka, targetGsk)
|
val targetInOrbit = pointInOrbitalFrame(ka, targetGsk)
|
||||||
val compensated = rotationZ(-lineAngle(nlv)) * w
|
val orient = orientationFromOrbitalVector(targetInOrbit)
|
||||||
val orient = orientationFromOrbitalVector(compensated)
|
|
||||||
|
|
||||||
if (routeDirectionGsk != null && routeDirectionGsk.module() > 1.0e-8) {
|
if (routeDirectionGsk != null && routeDirectionGsk.module() > 1.0e-8) {
|
||||||
orient.risk = riskFromGroundDirection(ka, nlv, orient, routeDirectionGsk)
|
orient.risk = riskFromGroundDirection(ka, orient, routeDirectionGsk)
|
||||||
}
|
}
|
||||||
return orient
|
return orient
|
||||||
}
|
}
|
||||||
@@ -161,7 +137,6 @@ abstract class AbstractPuudCalculator(
|
|||||||
*/
|
*/
|
||||||
private fun riskFromGroundDirection(
|
private fun riskFromGroundDirection(
|
||||||
kaGsk: OrbitalPoint,
|
kaGsk: OrbitalPoint,
|
||||||
nlv: Int,
|
|
||||||
orientWithoutRisk: Orientation,
|
orientWithoutRisk: Orientation,
|
||||||
routeDirectionGsk: Vector3D,
|
routeDirectionGsk: Vector3D,
|
||||||
): Double {
|
): Double {
|
||||||
@@ -169,9 +144,8 @@ abstract class AbstractPuudCalculator(
|
|||||||
val routeAbs = astro.grinvToASK(routeDirectionGsk, kaGsk.t)
|
val routeAbs = astro.grinvToASK(routeDirectionGsk, kaGsk.t)
|
||||||
val routeOrbit = orbBookToOrbMatrix() * absToOrbBookMatrix(kaAbs.r, kaAbs.v) * routeAbs
|
val routeOrbit = orbBookToOrbMatrix() * absToOrbBookMatrix(kaAbs.r, kaAbs.v) * routeAbs
|
||||||
val routeConnected = orientationMatrix(orientWithoutRisk).transpose() * routeOrbit
|
val routeConnected = orientationMatrix(orientWithoutRisk).transpose() * routeOrbit
|
||||||
val routeClv = rotationZ(-lineAngle(nlv)) * routeConnected
|
val wx = routeConnected.x
|
||||||
val wx = routeClv.x
|
val wz = routeConnected.z
|
||||||
val wz = routeClv.z
|
|
||||||
return if (sqrt(wx * wx + wz * wz) < 1.0e-10) {
|
return if (sqrt(wx * wx + wz * wz) < 1.0e-10) {
|
||||||
0.0
|
0.0
|
||||||
} else {
|
} else {
|
||||||
@@ -192,14 +166,13 @@ abstract class AbstractPuudCalculator(
|
|||||||
return Orientation(tang, kren, 0.0)
|
return Orientation(tang, kren, 0.0)
|
||||||
}
|
}
|
||||||
|
|
||||||
protected fun pointOnEarth(point: OrbitalPoint, nlv: Int, orientation: Orientation): BLHPoint? {
|
protected fun pointOnEarth(point: OrbitalPoint, orientation: Orientation): BLHPoint? {
|
||||||
val ask = astro.grinvToASK(point)
|
val ask = astro.grinvToASK(point)
|
||||||
val orbitToAbs = absToOrbBookMatrix(ask.r, ask.v).transpose() * orbBookToOrbMatrix().transpose()
|
val orbitToAbs = absToOrbBookMatrix(ask.r, ask.v).transpose() * orbBookToOrbMatrix().transpose()
|
||||||
val g = Matrix3D().also { it.makeOzMatrix(astro.si2000(point.t)) }.transpose()
|
val g = Matrix3D().also { it.makeOzMatrix(astro.si2000(point.t)) }.transpose()
|
||||||
val orbitToGsk = g * orbitToAbs
|
val orbitToGsk = g * orbitToAbs
|
||||||
val connectedToOrbit = orientationMatrix(orientation)
|
val connectedToOrbit = orientationMatrix(orientation)
|
||||||
val dConnected = lineOfSightVectorInConnected(lineAngle(nlv))
|
val direction = (orbitToGsk * connectedToOrbit * lineOfSightVectorInConnected()).normSafe()
|
||||||
val direction = (orbitToGsk * connectedToOrbit * dConnected).normSafe()
|
|
||||||
return earthIntersection(point.r, direction)
|
return earthIntersection(point.r, direction)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -225,23 +198,20 @@ abstract class AbstractPuudCalculator(
|
|||||||
return quaternionFromMatrix(absToConnected.transpose()).normalized()
|
return quaternionFromMatrix(absToConnected.transpose()).normalized()
|
||||||
}
|
}
|
||||||
|
|
||||||
protected fun wdForLines(point: OrbitalPoint, orientation: Orientation, omega: Vector3D): List<Vector3D> =
|
protected fun sdiForWd(wd: Vector3D, sickle: Boolean = false): Double =
|
||||||
(1..7).map { nlv -> wd(point, nlv, orientation, omega) }
|
(if (sickle) wd.z else wd.x) * config.focus
|
||||||
|
|
||||||
protected fun sdiForWd(wd: List<Vector3D>, sickle: Boolean = false): List<Double> =
|
|
||||||
wd.map { (if (sickle) it.z else it.x) * config.focus }
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Аналог AbstractAISTPUUD::wd. Возвращает W/D в связанной СК для заданной линии визирования.
|
* Аналог AbstractAISTPUUD::wd. Возвращает W/D в связанной СК для единственной центральной линии визирования.
|
||||||
*
|
*
|
||||||
* W — относительная скорость движения по земной поверхности точки пересечения ЛВ с Землей.
|
* W — относительная скорость движения по земной поверхности точки пересечения ЦЛВ с Землей.
|
||||||
* Это не скорость КА: из скорости спутника вычитается переносное вращение Земли, а затем
|
* Это не скорость КА: из скорости спутника вычитается переносное вращение Земли, а затем
|
||||||
* добавляется радиальная составляющая изменения наклонной дальности, чтобы точка оставалась
|
* добавляется радиальная составляющая изменения наклонной дальности, чтобы точка оставалась
|
||||||
* на поверхности эллипсоида. Такая схема соответствует формулам из OrbitalMotion::AbstractAISTPUUD::wd.
|
* на поверхности эллипсоида. Такая схема соответствует формулам из OrbitalMotion::AbstractAISTPUUD::wd.
|
||||||
*/
|
*/
|
||||||
protected fun wd(point: OrbitalPoint, nlv: Int, orientation: Orientation, omegaConnected: Vector3D): Vector3D {
|
protected fun wd(point: OrbitalPoint, orientation: Orientation, omegaConnected: Vector3D): Vector3D {
|
||||||
val ask = astro.grinvToASK(point)
|
val ask = astro.grinvToASK(point)
|
||||||
val lineConnected = lineOfSightVectorInConnected(lineAngle(nlv))
|
val lineConnected = lineOfSightVectorInConnected()
|
||||||
val connectedToOrbit = orientationMatrix(orientation)
|
val connectedToOrbit = orientationMatrix(orientation)
|
||||||
val absToOrbBook = absToOrbBookMatrix(ask.r, ask.v)
|
val absToOrbBook = absToOrbBookMatrix(ask.r, ask.v)
|
||||||
val orbitBookToAbs = absToOrbBook.transpose()
|
val orbitBookToAbs = absToOrbBook.transpose()
|
||||||
@@ -283,25 +253,41 @@ abstract class AbstractPuudCalculator(
|
|||||||
wGround = absToOrbBook * wGround
|
wGround = absToOrbBook * wGround
|
||||||
wGround = orbBookToOrbMatrix() * wGround
|
wGround = orbBookToOrbMatrix() * wGround
|
||||||
wGround = connectedToOrbit.transpose() * wGround
|
wGround = connectedToOrbit.transpose() * wGround
|
||||||
wGround = rotationZ(-lineAngle(nlv)) * wGround
|
|
||||||
|
|
||||||
return wGround / range
|
return wGround / range
|
||||||
}
|
}
|
||||||
|
|
||||||
protected fun routeNormalInGreenwich(b: Double, l: Double, h: Double, azimuth: Double): Vector3D {
|
protected fun routeNormalInGreenwich(b: Double, l: Double, h: Double, azimuth: Double): Vector3D {
|
||||||
val first = astro.earth.blh2xyz(b, l, h)
|
// Центральная линия маршрута по ТЗ задается сечением ОЗЭ плоскостью,
|
||||||
val secondBlh = endPointByAzimuth(b, l, azimuth, 0.1 * PI / 180.0)
|
// построенной в начальной точке по нормали к ОЗЭ и касательной к ОЗЭ
|
||||||
val second = astro.earth.blh2xyz(secondBlh.lat, secondBlh.long, h)
|
// с заданным азимутом. Нормаль такой плоскости — вектор normal x tangent.
|
||||||
return first.rem(second).normSafe()
|
val normal = ellipsoidNormalInGreenwich(b, l)
|
||||||
|
val tangent = routeDirectionInGreenwich(b, l, azimuth)
|
||||||
|
return normal.rem(tangent).normSafe()
|
||||||
|
}
|
||||||
|
|
||||||
|
protected fun routeDirectionInGreenwich(b: Double, l: Double, azimuth: Double): Vector3D {
|
||||||
|
val north = northDirectionInGreenwich(b, l)
|
||||||
|
val east = eastDirectionInGreenwich(l)
|
||||||
|
return (north * cos(azimuth) + east * sin(azimuth)).normSafe()
|
||||||
}
|
}
|
||||||
|
|
||||||
protected fun routeDirectionInOrbitalFrame(id: SurveyId, t: Double): Vector3D {
|
protected fun routeDirectionInOrbitalFrame(id: SurveyId, t: Double): Vector3D {
|
||||||
val p1 = astro.earth.blh2xyz(id.b, id.l, id.h)
|
|
||||||
val p2 = astro.earth.blh2xyz(endPointByAzimuth(id.b, id.l, id.azimuth, 0.1 * PI / 180.0))
|
|
||||||
val ka = pointAt(t)
|
val ka = pointAt(t)
|
||||||
return pointInOrbitalFrame(ka, p2) - pointInOrbitalFrame(ka, p1)
|
val kaAbs = astro.grinvToASK(ka)
|
||||||
|
val routeAbs = astro.grinvToASK(routeDirectionInGreenwich(id.b, id.l, id.azimuth), t)
|
||||||
|
return orbBookToOrbMatrix() * absToOrbBookMatrix(kaAbs.r, kaAbs.v) * routeAbs
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun ellipsoidNormalInGreenwich(b: Double, l: Double): Vector3D =
|
||||||
|
Vector3D(cos(b) * cos(l), cos(b) * sin(l), sin(b)).normSafe()
|
||||||
|
|
||||||
|
private fun eastDirectionInGreenwich(l: Double): Vector3D =
|
||||||
|
Vector3D(-sin(l), cos(l), 0.0).normSafe()
|
||||||
|
|
||||||
|
private fun northDirectionInGreenwich(b: Double, l: Double): Vector3D =
|
||||||
|
Vector3D(-sin(b) * cos(l), -sin(b) * sin(l), cos(b)).normSafe()
|
||||||
|
|
||||||
protected fun endPointByAzimuth(b: Double, l: Double, azimuth: Double, centralAngle: Double): BLHPoint {
|
protected fun endPointByAzimuth(b: Double, l: Double, azimuth: Double, centralAngle: Double): BLHPoint {
|
||||||
val sinB = sin(b)
|
val sinB = sin(b)
|
||||||
val cosB = cos(b)
|
val cosB = cos(b)
|
||||||
@@ -314,7 +300,6 @@ abstract class AbstractPuudCalculator(
|
|||||||
|
|
||||||
protected fun buildPoint(
|
protected fun buildPoint(
|
||||||
t: Double,
|
t: Double,
|
||||||
nlv: Int,
|
|
||||||
orientation: Orientation,
|
orientation: Orientation,
|
||||||
previous: AngularMotionPoint? = null,
|
previous: AngularMotionPoint? = null,
|
||||||
sickle: Boolean = false,
|
sickle: Boolean = false,
|
||||||
@@ -323,8 +308,8 @@ abstract class AbstractPuudCalculator(
|
|||||||
val q = quaternionFor(orbital, orientation)
|
val q = quaternionFor(orbital, orientation)
|
||||||
val omega = previous?.let { omegaFromTwoQuat(it.quaternion, q, t - it.t) } ?: Vector3D()
|
val omega = previous?.let { omegaFromTwoQuat(it.quaternion, q, t - it.t) } ?: Vector3D()
|
||||||
val eps = previous?.let { if (abs(t - it.t) > EPS) (omega - it.omega) / (t - it.t) else Vector3D() } ?: Vector3D()
|
val eps = previous?.let { if (abs(t - it.t) > EPS) (omega - it.omega) / (t - it.t) else Vector3D() } ?: Vector3D()
|
||||||
val ground = pointOnEarth(orbital, nlv, orientation)
|
val ground = pointOnEarth(orbital, orientation)
|
||||||
val wd = wdForLines(orbital, orientation, omega)
|
val wd = wd(orbital, orientation, omega)
|
||||||
return AngularMotionPoint(
|
return AngularMotionPoint(
|
||||||
t = t,
|
t = t,
|
||||||
orbitalPoint = orbital,
|
orbitalPoint = orbital,
|
||||||
@@ -338,7 +323,7 @@ abstract class AbstractPuudCalculator(
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
protected fun calculateTauPoints(points: List<AngularMotionPoint>, nlv: Int): List<AngularMotionPoint> {
|
protected fun calculateTauPoints(points: List<AngularMotionPoint>): List<AngularMotionPoint> {
|
||||||
if (points.size < 3 || config.tau <= 0.0) return emptyList()
|
if (points.size < 3 || config.tau <= 0.0) return emptyList()
|
||||||
val step = calculationStep()
|
val step = calculationStep()
|
||||||
val baseTime = points.first().t
|
val baseTime = points.first().t
|
||||||
@@ -358,7 +343,7 @@ abstract class AbstractPuudCalculator(
|
|||||||
lagrange(x, xs, kren),
|
lagrange(x, xs, kren),
|
||||||
lagrange(x, xs, risk),
|
lagrange(x, xs, risk),
|
||||||
)
|
)
|
||||||
val point = buildPoint(t, nlv, orientation, previous)
|
val point = buildPoint(t, orientation, previous)
|
||||||
result += point
|
result += point
|
||||||
previous = point
|
previous = point
|
||||||
d -= step
|
d -= step
|
||||||
|
|||||||
+2
-17
@@ -5,7 +5,6 @@ import ballistics.types.OrbitalPoint
|
|||||||
import ballistics.types.Orientation
|
import ballistics.types.Orientation
|
||||||
import ballistics.utils.math.Quaternion3D
|
import ballistics.utils.math.Quaternion3D
|
||||||
import ballistics.utils.math.Vector3D
|
import ballistics.utils.math.Vector3D
|
||||||
import kotlin.math.PI
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Параметры блока состояния КА, перенесенные из sBLS_KA проекта OrbitalMotion.
|
* Параметры блока состояния КА, перенесенные из sBLS_KA проекта OrbitalMotion.
|
||||||
@@ -22,18 +21,6 @@ data class AngularMotionConfig(
|
|||||||
val discreteTimes: Double = 0.125,
|
val discreteTimes: Double = 0.125,
|
||||||
/** Длительность интервала успокоения, с. */
|
/** Длительность интервала успокоения, с. */
|
||||||
val tau: Double = 10.0,
|
val tau: Double = 10.0,
|
||||||
/** Углы отклонения программных СК. */
|
|
||||||
val psk1Angle: Double = 19.5 * PI / 180.0,
|
|
||||||
val psk3Angle: Double = 0.0,
|
|
||||||
val psk2Angle: Double = -19.5 * PI / 180.0,
|
|
||||||
/** Углы отклонения линий визирования. */
|
|
||||||
val lv1Angle: Double = 0.01875,
|
|
||||||
val lv2Angle: Double = 0.0,
|
|
||||||
val lv3Angle: Double = -0.01875,
|
|
||||||
val lv4Angle: Double = 0.0,
|
|
||||||
val lv5Angle: Double = 0.01875,
|
|
||||||
val lv6Angle: Double = 0.0,
|
|
||||||
val lv7Angle: Double = -0.01875,
|
|
||||||
/** Расстояние от центра ОЭП до центра фокальной плоскости телескопа, мм. */
|
/** Расстояние от центра ОЭП до центра фокальной плоскости телескопа, мм. */
|
||||||
val dxOep: Double = 37.5,
|
val dxOep: Double = 37.5,
|
||||||
/** Длина линейки ОЭП, мм. */
|
/** Длина линейки ОЭП, мм. */
|
||||||
@@ -53,8 +40,6 @@ enum class AngularMotionMode {
|
|||||||
data class SurveyId(
|
data class SurveyId(
|
||||||
/** Признаки включения ОЭП: 1..4. */
|
/** Признаки включения ОЭП: 1..4. */
|
||||||
val oep: List<Boolean> = listOf(false, false, false, false),
|
val oep: List<Boolean> = listOf(false, false, false, false),
|
||||||
/** Номер линии визирования: 1..7. */
|
|
||||||
val nlv: Int = 4,
|
|
||||||
/** Примерное интегральное время начала наблюдения, с. */
|
/** Примерное интегральное время начала наблюдения, с. */
|
||||||
val t: Double = 0.0,
|
val t: Double = 0.0,
|
||||||
/** Координаты точки прицеливания: широта/долгота в радианах, высота в метрах. */
|
/** Координаты точки прицеливания: широта/долгота в радианах, высота в метрах. */
|
||||||
@@ -89,8 +74,8 @@ data class AngularMotionPoint(
|
|||||||
val omega: Vector3D = Vector3D(),
|
val omega: Vector3D = Vector3D(),
|
||||||
val eps: Vector3D = Vector3D(),
|
val eps: Vector3D = Vector3D(),
|
||||||
val quaternion: Quaternion3D = Quaternion3D(1.0, 0.0, 0.0, 0.0),
|
val quaternion: Quaternion3D = Quaternion3D(1.0, 0.0, 0.0, 0.0),
|
||||||
val wd: List<Vector3D> = List(7) { Vector3D() },
|
val wd: Vector3D = Vector3D(),
|
||||||
val sdi: List<Double> = List(7) { 0.0 },
|
val sdi: Double = 0.0,
|
||||||
)
|
)
|
||||||
|
|
||||||
/** Полный результат расчета режима углового движения. */
|
/** Полный результат расчета режима углового движения. */
|
||||||
|
|||||||
+48
-60
@@ -1,7 +1,6 @@
|
|||||||
package space.nstart.pcp.angularmotion
|
package space.nstart.pcp.angularmotion
|
||||||
|
|
||||||
import ballistics.orbitalPoints.timeStepper.AbstractStepper
|
import ballistics.orbitalPoints.timeStepper.AbstractStepper
|
||||||
import ballistics.types.BLHPoint
|
|
||||||
import ballistics.types.EarthType
|
import ballistics.types.EarthType
|
||||||
import ballistics.types.Orientation
|
import ballistics.types.Orientation
|
||||||
import ballistics.utils.math.Matrix3D
|
import ballistics.utils.math.Matrix3D
|
||||||
@@ -32,41 +31,35 @@ open class AzimuthPUUD(
|
|||||||
throw AngularMotionCalculationException("Некорректное значение СДИ")
|
throw AngularMotionCalculationException("Некорректное значение СДИ")
|
||||||
}
|
}
|
||||||
|
|
||||||
var tn = calcTn(id)
|
val tn = calcTn(id)
|
||||||
var workId = id
|
val points = calculateAzimuth(id, tn, id.duration)
|
||||||
var duration = id.duration
|
|
||||||
var oeps = buildOeps(id, tn)
|
|
||||||
|
|
||||||
// Логика специальных ЛВ 2/6 из AzimuthAISTPUUD: расширяем длительность на
|
|
||||||
// время прохода между линейками ОЭП и сдвигаем t_on/t_off парных ОЭП.
|
|
||||||
if (id.nlv == 2 || id.nlv == 6) {
|
|
||||||
val rvo = roundDownByDay(config.dxOep / id.sdi.first(), config.discreteTimes).coerceAtLeast(0.0)
|
|
||||||
duration += rvo * 2.0
|
|
||||||
tn -= rvo
|
|
||||||
oeps = shiftedPairOeps(id, tn, rvo)
|
|
||||||
workId = id.copy(t = tn)
|
|
||||||
}
|
|
||||||
|
|
||||||
val points = calculateAzimuth(workId, tn, duration)
|
|
||||||
return AngularMotionResult(
|
return AngularMotionResult(
|
||||||
mode = AngularMotionMode.AZIMUTH,
|
mode = AngularMotionMode.AZIMUTH,
|
||||||
startTime = tn,
|
startTime = tn,
|
||||||
points = points,
|
points = points,
|
||||||
tauPoints = calculateTauPoints(points, id.nlv),
|
tauPoints = calculateTauPoints(points),
|
||||||
oeps = oeps,
|
oeps = buildOeps(id, tn),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
protected open fun calculateAzimuth(id: SurveyId, tn: Double, duration: Double): List<AngularMotionPoint> {
|
protected open fun calculateAzimuth(id: SurveyId, tn: Double, duration: Double): List<AngularMotionPoint> {
|
||||||
val step = calculationStep()
|
val step = calculationStep()
|
||||||
val routeNormalGsk = routeNormalInGreenwich(id.b, id.l, id.h, id.azimuth)
|
val routeNormalGsk = routeNormalInGreenwich(id.b, id.l, id.h, id.azimuth)
|
||||||
|
val routeDirectionGsk = routeDirectionInGreenwich(id.b, id.l, id.azimuth)
|
||||||
val firstTargetGsk = astro.earth.blh2xyz(id.b, id.l, id.h)
|
val firstTargetGsk = astro.earth.blh2xyz(id.b, id.l, id.h)
|
||||||
var currentSdi = sdiAt(id.sdi, 0.0)
|
val initialSdi = sdiAt(id.sdi, 0.0)
|
||||||
|
|
||||||
val firstPoint = pointAt(tn)
|
val firstPoint = pointAt(tn)
|
||||||
val firstAbs = astro.grinvToASK(firstPoint)
|
val firstAbs = astro.grinvToASK(firstPoint)
|
||||||
val targetAbs = astro.grinvToASK(firstTargetGsk, tn)
|
val targetAbs = astro.grinvToASK(firstTargetGsk, tn)
|
||||||
val initialLiv = initialVisirQuaternion(firstAbs.r, targetAbs, routeNormalGsk, tn, currentSdi < 0.0)
|
val initialLiv = initialVisirQuaternion(
|
||||||
|
rAbs = firstAbs.r,
|
||||||
|
targetAbs = targetAbs,
|
||||||
|
routeNormalGsk = routeNormalGsk,
|
||||||
|
routeDirectionGsk = routeDirectionGsk,
|
||||||
|
time = tn,
|
||||||
|
reverse = initialSdi < 0.0,
|
||||||
|
)
|
||||||
var liv = initialLiv
|
var liv = initialLiv
|
||||||
|
|
||||||
val result = mutableListOf<AngularMotionPoint>()
|
val result = mutableListOf<AngularMotionPoint>()
|
||||||
@@ -75,10 +68,9 @@ open class AzimuthPUUD(
|
|||||||
var previous: AngularMotionPoint? = null
|
var previous: AngularMotionPoint? = null
|
||||||
|
|
||||||
while (elapsed <= duration + EPS) {
|
while (elapsed <= duration + EPS) {
|
||||||
currentSdi = sdiAt(id.sdi, elapsed)
|
|
||||||
val orbital = pointAt(t)
|
val orbital = pointAt(t)
|
||||||
val orientation = orientationFromVisirQuaternion(orbital, id.nlv, liv)
|
val orientation = orientationFromVisirQuaternion(orbital, liv)
|
||||||
val point = buildIntegratedPoint(t, id.nlv, orientation, liv, previous, sickle)
|
val point = buildIntegratedPoint(t, orientation, liv, previous, sickle)
|
||||||
result += point
|
result += point
|
||||||
previous = point
|
previous = point
|
||||||
|
|
||||||
@@ -87,7 +79,8 @@ open class AzimuthPUUD(
|
|||||||
val p = pointAt(time)
|
val p = pointAt(time)
|
||||||
val ask = astro.grinvToASK(p)
|
val ask = astro.grinvToASK(p)
|
||||||
val di = slantRangeFromQuaternion(q, ask.r)
|
val di = slantRangeFromQuaternion(q, ask.r)
|
||||||
ownCornerSpeed(time, q, di, ask.r, ask.v, currentSdi)
|
val sdiForTime = sdiAt(id.sdi, time - tn)
|
||||||
|
ownCornerSpeed(time, q, di, ask.r, ask.v, sdiForTime)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
elapsed += step
|
elapsed += step
|
||||||
@@ -120,7 +113,6 @@ open class AzimuthPUUD(
|
|||||||
|
|
||||||
protected fun buildIntegratedPoint(
|
protected fun buildIntegratedPoint(
|
||||||
t: Double,
|
t: Double,
|
||||||
nlv: Int,
|
|
||||||
orientation: Orientation,
|
orientation: Orientation,
|
||||||
liv: Quaternion3D,
|
liv: Quaternion3D,
|
||||||
previous: AngularMotionPoint?,
|
previous: AngularMotionPoint?,
|
||||||
@@ -129,8 +121,8 @@ open class AzimuthPUUD(
|
|||||||
val orbital = pointAt(t)
|
val orbital = pointAt(t)
|
||||||
val omega = previous?.let { omegaFromTwoQuat(it.quaternion, liv, t - it.t) } ?: Vector3D()
|
val omega = previous?.let { omegaFromTwoQuat(it.quaternion, liv, t - it.t) } ?: Vector3D()
|
||||||
val eps = previous?.let { if (abs(t - it.t) > EPS) (omega - it.omega) / (t - it.t) else Vector3D() } ?: Vector3D()
|
val eps = previous?.let { if (abs(t - it.t) > EPS) (omega - it.omega) / (t - it.t) else Vector3D() } ?: Vector3D()
|
||||||
val ground = pointOnEarth(orbital, nlv, orientation)
|
val ground = pointOnEarth(orbital, orientation)
|
||||||
val wd = wdForLines(orbital, orientation, omega)
|
val wd = wd(orbital, orientation, omega)
|
||||||
return AngularMotionPoint(
|
return AngularMotionPoint(
|
||||||
t = t,
|
t = t,
|
||||||
orbitalPoint = orbital,
|
orbitalPoint = orbital,
|
||||||
@@ -148,25 +140,36 @@ open class AzimuthPUUD(
|
|||||||
rAbs: Vector3D,
|
rAbs: Vector3D,
|
||||||
targetAbs: Vector3D,
|
targetAbs: Vector3D,
|
||||||
routeNormalGsk: Vector3D,
|
routeNormalGsk: Vector3D,
|
||||||
|
routeDirectionGsk: Vector3D,
|
||||||
time: Double,
|
time: Double,
|
||||||
reverse: Boolean,
|
reverse: Boolean,
|
||||||
): Quaternion3D {
|
): Quaternion3D {
|
||||||
var normalAbs = astro.grinvToASK(routeNormalGsk, time).normSafe()
|
val line = (targetAbs - rAbs).normSafe()
|
||||||
if (reverse) normalAbs = normalAbs * -1.0
|
val routeNormalAbs = astro.grinvToASK(routeNormalGsk, time).normSafe()
|
||||||
val di = targetAbs - rAbs
|
val routeDirectionAbs = astro.grinvToASK(routeDirectionGsk, time).normSafe()
|
||||||
val eY = normalAbs.rem(targetAbs.normSafe()).normSafe()
|
|
||||||
val eZ = eY.rem(di.normSafe()).normSafe()
|
// Ось движения изображения в визирной СК должна быть связана с касательной
|
||||||
var m = Matrix3D(
|
// центральной линии маршрута, а не с хордой двух земных радиус-векторов.
|
||||||
di.normSafe(),
|
// Проекция азимутальной касательной в фокальную плоскость соответствует
|
||||||
eZ.rem(di.normSafe()).normSafe(),
|
// критериям Wz/D = 0 и Wx/D = const: поперечная составляющая обнуляется,
|
||||||
eZ,
|
// а продольная ось направлена по маршруту.
|
||||||
)
|
var routeAxis = (routeDirectionAbs - line * (routeDirectionAbs * line)).normSafe()
|
||||||
if (sickle) {
|
if (routeAxis.module() < EPS) {
|
||||||
m = rotationX(PI) * Matrix3D(
|
routeAxis = routeNormalAbs.rem(line).normSafe()
|
||||||
di.normSafe(),
|
}
|
||||||
eZ,
|
if (reverse) {
|
||||||
di.normSafe().rem(eZ).normSafe(),
|
routeAxis = routeAxis * -1.0
|
||||||
|
}
|
||||||
|
|
||||||
|
val crossAxis = line.rem(routeAxis).normSafe()
|
||||||
|
val m = if (sickle) {
|
||||||
|
rotationX(PI) * Matrix3D(
|
||||||
|
line,
|
||||||
|
crossAxis,
|
||||||
|
line.rem(crossAxis).normSafe(),
|
||||||
)
|
)
|
||||||
|
} else {
|
||||||
|
Matrix3D(line, routeAxis, crossAxis)
|
||||||
}
|
}
|
||||||
return quaternionFromMatrix(m).inverse().normalized()
|
return quaternionFromMatrix(m).inverse().normalized()
|
||||||
}
|
}
|
||||||
@@ -182,29 +185,14 @@ open class AzimuthPUUD(
|
|||||||
return lineAbs * root
|
return lineAbs * root
|
||||||
}
|
}
|
||||||
|
|
||||||
protected fun orientationFromVisirQuaternion(orbital: ballistics.types.OrbitalPoint, nlv: Int, liv: Quaternion3D): Orientation {
|
protected fun orientationFromVisirQuaternion(orbital: ballistics.types.OrbitalPoint, liv: Quaternion3D): Orientation {
|
||||||
val ask = astro.grinvToASK(orbital)
|
val ask = astro.grinvToASK(orbital)
|
||||||
val ob = orbBookToOrbMatrix()
|
val ob = orbBookToOrbMatrix()
|
||||||
val absOb = absToOrbBookMatrix(ask.r, ask.v)
|
val absOb = absToOrbBookMatrix(ask.r, ask.v)
|
||||||
val absToOrbit = ob * absOb
|
val absToOrbit = ob * absOb
|
||||||
val oepToConnected = rotationZ(-lineAngle(nlv)).transpose()
|
|
||||||
val lConToVisir = Quaternion3D(0.0, sqrt(2.0) / 2.0, -sqrt(2.0) / 2.0, 0.0)
|
val lConToVisir = Quaternion3D(0.0, sqrt(2.0) / 2.0, -sqrt(2.0) / 2.0, 0.0)
|
||||||
val connectedToAbs = (liv * lConToVisir.inverse() * quaternionFromMatrix(oepToConnected).inverse()).matrix().transpose()
|
val connectedToAbs = (liv * lConToVisir.inverse()).matrix().transpose()
|
||||||
val orbitToConnected = connectedToAbs * absToOrbit.transpose()
|
val orbitToConnected = connectedToAbs * absToOrbit.transpose()
|
||||||
return anglesFromOrbToCon(orbitToConnected)
|
return anglesFromOrbToCon(orbitToConnected)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun shiftedPairOeps(id: SurveyId, tn: Double, rvo: Double): List<OepResult> {
|
|
||||||
val base = MutableList(4) { index ->
|
|
||||||
if (id.oep.getOrNull(index) == true) OepResult(true, tn, tn + id.duration) else OepResult(false)
|
|
||||||
}
|
|
||||||
if (id.nlv == 2) {
|
|
||||||
base[0] = OepResult(id.oep.getOrNull(0) == true, tn + rvo * 2.0, tn + rvo * 2.0 + id.duration)
|
|
||||||
base[1] = OepResult(id.oep.getOrNull(1) == true, tn, tn + id.duration)
|
|
||||||
} else if (id.nlv == 6) {
|
|
||||||
base[2] = OepResult(id.oep.getOrNull(2) == true, tn + rvo * 2.0, tn + rvo * 2.0 + id.duration)
|
|
||||||
base[3] = OepResult(id.oep.getOrNull(3) == true, tn, tn + id.duration)
|
|
||||||
}
|
|
||||||
return base
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
+10
-12
@@ -11,7 +11,7 @@ import ballistics.utils.math.Vector3D
|
|||||||
*
|
*
|
||||||
* Базовая логика повторяет исходный класс: уточняется время начала по упреждающему
|
* Базовая логика повторяет исходный класс: уточняется время начала по упреждающему
|
||||||
* углу, затем с дискретом stepPuud рассчитываются углы ориентации, точка пересечения
|
* углу, затем с дискретом stepPuud рассчитываются углы ориентации, точка пересечения
|
||||||
* ЛВ с Землей, кватернион, угловая скорость, W/D и СДИ по всем семи ЛВ.
|
* единственной ЦЛВ с Землей, кватернион, угловая скорость, W/D и СДИ.
|
||||||
*/
|
*/
|
||||||
class ConstOrientPUUD(
|
class ConstOrientPUUD(
|
||||||
stepper: AbstractStepper,
|
stepper: AbstractStepper,
|
||||||
@@ -27,7 +27,7 @@ class ConstOrientPUUD(
|
|||||||
mode = AngularMotionMode.CONST_ORIENT,
|
mode = AngularMotionMode.CONST_ORIENT,
|
||||||
startTime = tn,
|
startTime = tn,
|
||||||
points = points,
|
points = points,
|
||||||
tauPoints = calculateTauPoints(points, id.nlv),
|
tauPoints = calculateTauPoints(points),
|
||||||
oeps = buildOeps(id, tn),
|
oeps = buildOeps(id, tn),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@@ -44,21 +44,20 @@ class ConstOrientPUUD(
|
|||||||
// пересекаем эту фиксированную ЦЛВ с Землей. Важно, чтобы первая точка маршрута
|
// пересекаем эту фиксированную ЦЛВ с Землей. Важно, чтобы первая точка маршрута
|
||||||
// тоже была получена через такое пересечение: иначе первый шаг сшивает заданную
|
// тоже была получена через такое пересечение: иначе первый шаг сшивает заданную
|
||||||
// исходную ЦЛМ с расчетной трассой постоянной ЦЛВ и дает искусственный скачок рысканья.
|
// исходную ЦЛМ с расчетной трассой постоянной ЦЛВ и дает искусственный скачок рысканья.
|
||||||
val fixedClvOrientation = orientOnPoint(id.nlv, tn, id.b, id.l, id.h)
|
val fixedClvOrientation = orientOnPoint(tn, id.b, id.l, id.h)
|
||||||
|
|
||||||
while (elapsed <= duration + 2.0 * step + EPS) {
|
while (elapsed <= duration + 2.0 * step + EPS) {
|
||||||
val currentGround = pointOnFixedClv(t, id.nlv, fixedClvOrientation)
|
val currentGround = pointOnFixedClv(t, fixedClvOrientation)
|
||||||
?: BLHPoint(id.b, id.l, id.h)
|
?: BLHPoint(id.b, id.l, id.h)
|
||||||
val routeDirectionGsk = fixedClvGroundDirection(t, step, id.nlv, fixedClvOrientation, currentGround)
|
val routeDirectionGsk = fixedClvGroundDirection(t, step, fixedClvOrientation, currentGround)
|
||||||
val orientation = orientOnPoint(
|
val orientation = orientOnPoint(
|
||||||
id.nlv,
|
|
||||||
t,
|
t,
|
||||||
currentGround.lat,
|
currentGround.lat,
|
||||||
currentGround.long,
|
currentGround.long,
|
||||||
currentGround.h,
|
currentGround.h,
|
||||||
routeDirectionGsk,
|
routeDirectionGsk,
|
||||||
)
|
)
|
||||||
val point = buildPoint(t, id.nlv, orientation, previous)
|
val point = buildPoint(t, orientation, previous)
|
||||||
result += point
|
result += point
|
||||||
previous = point
|
previous = point
|
||||||
|
|
||||||
@@ -69,18 +68,17 @@ class ConstOrientPUUD(
|
|||||||
return if (result.size > 2) result.dropLast(2) else result
|
return if (result.size > 2) result.dropLast(2) else result
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun pointOnFixedClv(t: Double, nlv: Int, fixedClvOrientation: Orientation): BLHPoint? =
|
private fun pointOnFixedClv(t: Double, fixedClvOrientation: Orientation): BLHPoint? =
|
||||||
pointOnEarth(pointAt(t), nlv, fixedClvOrientation)
|
pointOnEarth(pointAt(t), fixedClvOrientation)
|
||||||
|
|
||||||
private fun fixedClvGroundDirection(
|
private fun fixedClvGroundDirection(
|
||||||
t: Double,
|
t: Double,
|
||||||
step: Double,
|
step: Double,
|
||||||
nlv: Int,
|
|
||||||
fixedClvOrientation: Orientation,
|
fixedClvOrientation: Orientation,
|
||||||
currentGround: BLHPoint,
|
currentGround: BLHPoint,
|
||||||
): Vector3D {
|
): Vector3D {
|
||||||
val previousGround = pointOnFixedClv(t - step, nlv, fixedClvOrientation)
|
val previousGround = pointOnFixedClv(t - step, fixedClvOrientation)
|
||||||
val nextGround = pointOnFixedClv(t + step, nlv, fixedClvOrientation)
|
val nextGround = pointOnFixedClv(t + step, fixedClvOrientation)
|
||||||
|
|
||||||
return when {
|
return when {
|
||||||
previousGround != null && nextGround != null -> groundDirection(previousGround, nextGround)
|
previousGround != null && nextGround != null -> groundDirection(previousGround, nextGround)
|
||||||
|
|||||||
+4
-5
@@ -143,12 +143,11 @@ internal fun conToOpticMatrix(): Matrix3D = Matrix3D(
|
|||||||
Vector3D(0.0, 0.0, -1.0),
|
Vector3D(0.0, 0.0, -1.0),
|
||||||
)
|
)
|
||||||
|
|
||||||
internal fun lineOfSightVectorInConnected(lineAngle: Double): Vector3D {
|
internal fun lineOfSightVectorInConnected(): Vector3D {
|
||||||
// Центральная линия визирования направлена из центра КА к Земле.
|
// У спутника используется единственная центральная линия визирования.
|
||||||
// В программной/связанной СК при нулевых углах +OY направлена от Земли,
|
// В программной/связанной СК при нулевых углах +OY направлена от Земли,
|
||||||
// поэтому ЦЛВ соответствует вектору (0, -1, 0). Отклонения боковых ЛВ
|
// поэтому ЦЛВ, направленная из центра КА к Земле, задается вектором (0, -1, 0).
|
||||||
// задаются поворотом вокруг OZ связанной СК, как в OrbitalMotion::conToCLV.
|
return Vector3D(0.0, -1.0, 0.0)
|
||||||
return Vector3D(sin(lineAngle), -cos(lineAngle), 0.0).normSafe()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
internal fun quaternionFromMatrix(m: Matrix3D): Quaternion3D = Quaternion3D().also { it.fromMatrixStanley(m) }.normalized()
|
internal fun quaternionFromMatrix(m: Matrix3D): Quaternion3D = Quaternion3D().also { it.fromMatrixStanley(m) }.normalized()
|
||||||
|
|||||||
+1
-1
@@ -29,7 +29,7 @@ class SmoothSDIPUUD(
|
|||||||
mode = AngularMotionMode.SMOOTH_SDI,
|
mode = AngularMotionMode.SMOOTH_SDI,
|
||||||
startTime = tn,
|
startTime = tn,
|
||||||
points = points,
|
points = points,
|
||||||
tauPoints = calculateTauPoints(points, id.nlv),
|
tauPoints = calculateTauPoints(points),
|
||||||
oeps = buildOeps(id, tn),
|
oeps = buildOeps(id, tn),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
+75
-4
@@ -10,6 +10,8 @@ import ballistics.utils.fromDateTime
|
|||||||
import ballistics.utils.math.Vector3D
|
import ballistics.utils.math.Vector3D
|
||||||
import ballistics.utils.toDateTime
|
import ballistics.utils.toDateTime
|
||||||
import java.time.LocalDateTime
|
import java.time.LocalDateTime
|
||||||
|
import java.time.format.DateTimeFormatter
|
||||||
|
import java.util.Locale
|
||||||
import kotlin.math.PI
|
import kotlin.math.PI
|
||||||
import kotlin.math.cos
|
import kotlin.math.cos
|
||||||
import kotlin.math.sin
|
import kotlin.math.sin
|
||||||
@@ -18,11 +20,12 @@ import kotlin.test.assertEquals
|
|||||||
import kotlin.test.assertTrue
|
import kotlin.test.assertTrue
|
||||||
|
|
||||||
class AngularMotionCalculatorSmokeTest {
|
class AngularMotionCalculatorSmokeTest {
|
||||||
|
private val outputTimeFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss.SSS")
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `const orientation returns points`() {
|
fun `const orientation returns points`() {
|
||||||
val result = ConstOrientPUUD(CircularStepper()).calculate(
|
val result = ConstOrientPUUD(CircularStepper()).calculate(
|
||||||
SurveyId(
|
SurveyId(
|
||||||
nlv = 4,
|
|
||||||
t = 1000.0,
|
t = 1000.0,
|
||||||
b = 0.1,
|
b = 0.1,
|
||||||
l = 0.2,
|
l = 0.2,
|
||||||
@@ -44,7 +47,6 @@ class AngularMotionCalculatorSmokeTest {
|
|||||||
|
|
||||||
val result = calculator.calculate(
|
val result = calculator.calculate(
|
||||||
SurveyId(
|
SurveyId(
|
||||||
nlv = 4,
|
|
||||||
t = 1000.0,
|
t = 1000.0,
|
||||||
b = 0.1,
|
b = 0.1,
|
||||||
l = 0.2,
|
l = 0.2,
|
||||||
@@ -83,7 +85,7 @@ class AngularMotionCalculatorSmokeTest {
|
|||||||
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun chek(){
|
fun chekConstOrient(){
|
||||||
val ballistics = Ballistics()
|
val ballistics = Ballistics()
|
||||||
val t = LocalDateTime.of(2023, 4, 12, 17, 41, 18, 0)
|
val t = LocalDateTime.of(2023, 4, 12, 17, 41, 18, 0)
|
||||||
val ic = InitialConditions(
|
val ic = InitialConditions(
|
||||||
@@ -119,7 +121,6 @@ class AngularMotionCalculatorSmokeTest {
|
|||||||
val calculator = ConstOrientPUUD(stepper, EarthType.PZ90d02)
|
val calculator = ConstOrientPUUD(stepper, EarthType.PZ90d02)
|
||||||
val id = SurveyId(
|
val id = SurveyId(
|
||||||
oep = listOf(false, false, false, true),
|
oep = listOf(false, false, false, true),
|
||||||
nlv = 5,
|
|
||||||
t = fromDateTime(LocalDateTime.of(2023, 4, 16, 3, 48, 22, 0)),
|
t = fromDateTime(LocalDateTime.of(2023, 4, 16, 3, 48, 22, 0)),
|
||||||
b = 49.25824 * PI / 180,
|
b = 49.25824 * PI / 180,
|
||||||
l = 153.65914 * PI / 180,
|
l = 153.65914 * PI / 180,
|
||||||
@@ -140,4 +141,74 @@ class AngularMotionCalculatorSmokeTest {
|
|||||||
println("${toDateTime( v.t)} ${v.orientation.tang * 180 / PI} ${v.orientation.kren * 180 / PI } ${v.orientation.risk * 180 / PI}")
|
println("${toDateTime( v.t)} ${v.orientation.tang * 180 / PI} ${v.orientation.kren * 180 / PI } ${v.orientation.risk * 180 / PI}")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun chekAzimuth(){
|
||||||
|
val ballistics = Ballistics()
|
||||||
|
val t = LocalDateTime.of(2023, 4, 12, 17, 41, 18, 0)
|
||||||
|
val ic = InitialConditions(
|
||||||
|
OrbitalPoint(
|
||||||
|
fromDateTime(t),
|
||||||
|
1,
|
||||||
|
Vector3D(
|
||||||
|
-3101926.630,
|
||||||
|
6018678.8,
|
||||||
|
0.0,
|
||||||
|
),
|
||||||
|
Vector3D(
|
||||||
|
1287.651,
|
||||||
|
663.634,
|
||||||
|
7612.951,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
0.005,
|
||||||
|
160.0
|
||||||
|
)
|
||||||
|
val result = ballistics.calculateOrbPoints(
|
||||||
|
ic,
|
||||||
|
ic.point.t,
|
||||||
|
ic.point.t + 86400 * 10
|
||||||
|
)
|
||||||
|
assertEquals(result, BallisticsError.OK)
|
||||||
|
if (result != BallisticsError.OK)
|
||||||
|
return
|
||||||
|
val stepper = ballistics.getStepper()
|
||||||
|
assert(stepper != null)
|
||||||
|
if (stepper == null)
|
||||||
|
return
|
||||||
|
val calculator = AzimuthPUUD(stepper, EarthType.PZ90d02)
|
||||||
|
val id = SurveyId(
|
||||||
|
oep = listOf(false, false, false, true),
|
||||||
|
t = fromDateTime(LocalDateTime.of(2023, 4, 16, 3, 48, 22, 0)),
|
||||||
|
b = 49.25824 * PI / 180,
|
||||||
|
l = 153.65914 * PI / 180,
|
||||||
|
h = 0.0,
|
||||||
|
duration = 69.0,
|
||||||
|
sdi = listOf(30.0),
|
||||||
|
azimuth = 25.567 * PI / 180,
|
||||||
|
uprAngle = 6.0 * PI / 180,
|
||||||
|
pointInCenter = false
|
||||||
|
)
|
||||||
|
val rc = calculator.calculate(id)
|
||||||
|
|
||||||
|
println(rc.mode)
|
||||||
|
println(rc.points.size)
|
||||||
|
println(rc.startTime)
|
||||||
|
|
||||||
|
for (v in rc.points) {
|
||||||
|
println(
|
||||||
|
String.format(
|
||||||
|
Locale.US,
|
||||||
|
"%s %.3f %.3f %.3f , %s %s",
|
||||||
|
toDateTime(v.t).format(outputTimeFormatter),
|
||||||
|
v.orientation.tang * 180 / PI,
|
||||||
|
v.orientation.kren * 180 / PI,
|
||||||
|
v.orientation.risk * 180 / PI,
|
||||||
|
v.groundPoint?.let { String.format(Locale.US, "%.3f", it.lat * 180 / PI) },
|
||||||
|
v.groundPoint?.let { String.format(Locale.US, "%.3f", it.long * 180 / PI) },
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user