сравнение TLE
This commit is contained in:
+4
-3
@@ -1,15 +1,13 @@
|
||||
package space.nstart.pcp.pcp_request_service.controller
|
||||
|
||||
import ballistics.types.OrbitalPoint
|
||||
import org.springframework.beans.factory.annotation.Autowired
|
||||
import org.springframework.web.bind.annotation.GetMapping
|
||||
import org.springframework.web.bind.annotation.PostMapping
|
||||
import org.springframework.web.bind.annotation.RequestBody
|
||||
import org.springframework.web.bind.annotation.RequestMapping
|
||||
import org.springframework.web.bind.annotation.RestController
|
||||
import space.nstart.pcp.pcp_request_service.service.TLEService
|
||||
import space.nstart.pcp.pcp_types_lib.dto.ballistics.OrbPointDTO
|
||||
import space.nstart.pcp.pcp_types_lib.dto.ballistics.TLEDTO
|
||||
import space.nstart.pcp.pcp_types_lib.dto.ballistics.TlePointRequestDTO
|
||||
import space.nstart.pcp.pcp_types_lib.dto.ballistics.TleRvaRequestDTO
|
||||
|
||||
@RestController
|
||||
@@ -23,6 +21,9 @@ class TLEController {
|
||||
@PostMapping("/parse")
|
||||
fun orbit(@RequestBody tle : TLEDTO) = tleService.parseTLE(tle)
|
||||
|
||||
@PostMapping("/point")
|
||||
fun point(@RequestBody req: TlePointRequestDTO) = tleService.point(req)
|
||||
|
||||
|
||||
@PostMapping("/rva")
|
||||
fun rva(@RequestBody req : TleRvaRequestDTO) = tleService.rva(req)
|
||||
|
||||
+34
@@ -1,7 +1,9 @@
|
||||
package space.nstart.pcp.pcp_request_service.service
|
||||
|
||||
import ballistics.Ballistics
|
||||
import ballistics.orbitalPoints.timeStepper.TLEStepper
|
||||
import ballistics.types.BallisticsError
|
||||
import ballistics.types.EarthType
|
||||
import ballistics.types.PPI
|
||||
import ballistics.types.TLE
|
||||
import ballistics.types.TLEParams
|
||||
@@ -15,6 +17,7 @@ import space.nstart.pcp.pcp_request_service.configuration.CustomValidationExcept
|
||||
import space.nstart.pcp.pcp_types_lib.dto.ballistics.OrbPointDTO
|
||||
import space.nstart.pcp.pcp_types_lib.dto.ballistics.RadioVisibilityAreaDTO
|
||||
import space.nstart.pcp.pcp_types_lib.dto.ballistics.TLEDTO
|
||||
import space.nstart.pcp.pcp_types_lib.dto.ballistics.TlePointRequestDTO
|
||||
import space.nstart.pcp.pcp_types_lib.dto.ballistics.TargetPositionDTO
|
||||
import space.nstart.pcp.pcp_types_lib.dto.ballistics.TleRvaRequestDTO
|
||||
import kotlin.math.PI
|
||||
@@ -47,6 +50,37 @@ class TLEService() {
|
||||
|
||||
|
||||
|
||||
|
||||
fun point(req: TlePointRequestDTO): OrbPointDTO {
|
||||
val bal = Ballistics()
|
||||
val tleParams = try {
|
||||
bal.getTLEParams(TLE(req.tle.first, req.tle.second), req.tle.header ?: "empty")
|
||||
} catch (ex: Exception) {
|
||||
throw CustomValidationException("Ошибка формата TLE : ${ex.message}")
|
||||
}
|
||||
if (req.time < tleParams.time) {
|
||||
throw CustomValidationException("Время расчета должно быть не раньше эпохи TLE ${tleParams.time}")
|
||||
}
|
||||
|
||||
val point = try {
|
||||
val stepper = TLEStepper(req.tle.first, req.tle.second, EarthType.PZ90d02)
|
||||
stepper.calculate(fromDateTime(req.time))
|
||||
} catch (ex: Exception) {
|
||||
throw CustomErrorException("Ошибка расчета положения по TLE : ${ex.message}")
|
||||
}
|
||||
|
||||
return OrbPointDTO(
|
||||
toDateTime(point.t),
|
||||
point.vit.toLong(),
|
||||
point.v.x,
|
||||
point.v.y,
|
||||
point.v.z,
|
||||
point.r.x,
|
||||
point.r.y,
|
||||
point.r.z
|
||||
)
|
||||
}
|
||||
|
||||
fun rva(req : TleRvaRequestDTO) : Iterable<RadioVisibilityAreaDTO> {
|
||||
|
||||
val bal = Ballistics()
|
||||
|
||||
+73
-25
@@ -4,18 +4,23 @@ import org.springframework.web.bind.annotation.PostMapping
|
||||
import org.springframework.web.bind.annotation.RequestBody
|
||||
import org.springframework.web.bind.annotation.RequestMapping
|
||||
import org.springframework.web.bind.annotation.RestController
|
||||
import space.nstart.pcp.pcp_types_lib.dto.ballistics.OrbPointDTO
|
||||
import space.nstart.pcp.pcp_types_lib.dto.ballistics.TLEDTO
|
||||
import space.nstart.pcp.slots_service.configuration.CustomValidationException
|
||||
import space.nstart.pcp.slots_service.dto.tlecomparison.TleCompareRequestDTO
|
||||
import space.nstart.pcp.slots_service.dto.tlecomparison.TleCompareResponseDTO
|
||||
import space.nstart.pcp.slots_service.dto.tlecomparison.TleCompareRowDTO
|
||||
import space.nstart.pcp.slots_service.dto.tlecomparison.TleParsedDTO
|
||||
import space.nstart.pcp.slots_service.dto.tlecomparison.TlePositionDTO
|
||||
import space.nstart.pcp.slots_service.dto.tlecomparison.TlePositionDeltaDTO
|
||||
import space.nstart.pcp.slots_service.dto.tlecomparison.TleTextRequestDTO
|
||||
import space.nstart.pcp.slots_service.service.BallisticsService
|
||||
import java.time.Duration
|
||||
import java.time.LocalDateTime
|
||||
import java.time.format.DateTimeFormatter
|
||||
import java.util.Locale
|
||||
import kotlin.math.abs
|
||||
import kotlin.math.sqrt
|
||||
|
||||
@RestController
|
||||
@RequestMapping("/api/tle-comparison")
|
||||
@@ -29,12 +34,38 @@ class TleComparisonController(
|
||||
|
||||
@PostMapping("/compare")
|
||||
fun compare(@RequestBody request: TleCompareRequestDTO): TleCompareResponseDTO {
|
||||
val first = ballisticsService.parseTle(parseTleText(request.first))
|
||||
val second = ballisticsService.parseTle(parseTleText(request.second))
|
||||
val firstTle = parseTleText(request.first)
|
||||
val secondTle = parseTleText(request.second)
|
||||
val firstParsed = ballisticsService.parseTle(firstTle)
|
||||
val secondParsed = ballisticsService.parseTle(secondTle)
|
||||
val calculationTime = request.time
|
||||
?: throw CustomValidationException("Задайте время сравнения TLE")
|
||||
val firstEpoch = firstParsed.time
|
||||
?: throw CustomValidationException("Не удалось определить эпоху первого TLE")
|
||||
val secondEpoch = secondParsed.time
|
||||
?: throw CustomValidationException("Не удалось определить эпоху второго TLE")
|
||||
val maxEpoch = maxOf(firstEpoch, secondEpoch)
|
||||
if (calculationTime < maxEpoch) {
|
||||
throw CustomValidationException("Время сравнения должно быть не раньше максимальной эпохи TLE: $maxEpoch")
|
||||
}
|
||||
|
||||
val firstPoint = ballisticsService.tlePoint(firstTle, calculationTime).toPosition()
|
||||
val secondPoint = ballisticsService.tlePoint(secondTle, calculationTime).toPosition()
|
||||
val delta = TlePositionDeltaDTO(
|
||||
dx = secondPoint.x - firstPoint.x,
|
||||
dy = secondPoint.y - firstPoint.y,
|
||||
dz = secondPoint.z - firstPoint.z,
|
||||
norm = distance(secondPoint.x - firstPoint.x, secondPoint.y - firstPoint.y, secondPoint.z - firstPoint.z)
|
||||
)
|
||||
|
||||
return TleCompareResponseDTO(
|
||||
first = first,
|
||||
second = second,
|
||||
rows = buildRows(first, second)
|
||||
time = calculationTime,
|
||||
firstEpoch = firstEpoch,
|
||||
secondEpoch = secondEpoch,
|
||||
first = firstPoint,
|
||||
second = secondPoint,
|
||||
radiusVectorDelta = delta,
|
||||
rows = buildRows(firstPoint, secondPoint, delta)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -69,23 +100,38 @@ class TleComparisonController(
|
||||
)
|
||||
}
|
||||
|
||||
private fun buildRows(first: TleParsedDTO, second: TleParsedDTO): List<TleCompareRowDTO> = listOf(
|
||||
textRow("satName", "Название", first.satName, second.satName),
|
||||
textRow("noradId", "NORAD ID", first.noradId?.toString(), second.noradId?.toString()),
|
||||
textRow("revolution", "Номер витка", first.revolution?.toString(), second.revolution?.toString()),
|
||||
timeRow("time", "Эпоха", first.time, second.time),
|
||||
numberRow("inclination", "Наклонение", first.inclination, second.inclination),
|
||||
numberRow("ascendingNode", "ВУЗ", first.ascendingNode, second.ascendingNode),
|
||||
numberRow("argPerigee", "Аргумент перигея", first.argPerigee, second.argPerigee),
|
||||
numberRow("meanAnomaly", "Средняя аномалия", first.meanAnomaly, second.meanAnomaly),
|
||||
numberRow("eccentricity", "Эксцентриситет", first.eccentricity, second.eccentricity),
|
||||
numberRow("perigee", "Перигей", first.perigee, second.perigee),
|
||||
numberRow("apogee", "Апогей", first.apogee, second.apogee),
|
||||
numberRow("period", "Период", first.period, second.period),
|
||||
numberRow("semiMajor", "Большая полуось", first.semiMajor, second.semiMajor),
|
||||
numberRow("semiMinor", "Малая полуось", first.semiMinor, second.semiMinor),
|
||||
numberRow("meanMotion", "Среднее движение", first.meanMotion, second.meanMotion),
|
||||
numberRow("meanMotionTLE", "Среднее движение TLE", first.meanMotionTLE, second.meanMotionTLE)
|
||||
private fun buildRows(
|
||||
first: TlePositionDTO,
|
||||
second: TlePositionDTO,
|
||||
delta: TlePositionDeltaDTO
|
||||
): List<TleCompareRowDTO> = listOf(
|
||||
timeRow("time", "Время расчета", first.time, second.time),
|
||||
textRow("revolution", "Виток", first.revolution.toString(), second.revolution.toString()),
|
||||
numberRow("x", "X радиус-вектора, м", first.x, second.x),
|
||||
numberRow("y", "Y радиус-вектора, м", first.y, second.y),
|
||||
numberRow("z", "Z радиус-вектора, м", first.z, second.z),
|
||||
numberRow("radiusNorm", "Модуль радиус-вектора, м", first.radiusNorm, second.radiusNorm),
|
||||
numberRow("vx", "Vx, м/с", first.vx, second.vx),
|
||||
numberRow("vy", "Vy, м/с", first.vy, second.vy),
|
||||
numberRow("vz", "Vz, м/с", first.vz, second.vz),
|
||||
numberRow("velocityNorm", "Модуль скорости, м/с", first.velocityNorm, second.velocityNorm),
|
||||
TleCompareRowDTO("dx", "ΔX = X2 - X1, м", first = null, second = null, delta = delta.dx.formatSignedNumber()),
|
||||
TleCompareRowDTO("dy", "ΔY = Y2 - Y1, м", first = null, second = null, delta = delta.dy.formatSignedNumber()),
|
||||
TleCompareRowDTO("dz", "ΔZ = Z2 - Z1, м", first = null, second = null, delta = delta.dz.formatSignedNumber()),
|
||||
TleCompareRowDTO("radiusDeltaNorm", "|Δr|, м", first = null, second = null, delta = delta.norm.formatNumber())
|
||||
)
|
||||
|
||||
private fun OrbPointDTO.toPosition(): TlePositionDTO = TlePositionDTO(
|
||||
time = time,
|
||||
revolution = revolution,
|
||||
x = x,
|
||||
y = y,
|
||||
z = z,
|
||||
vx = vx,
|
||||
vy = vy,
|
||||
vz = vz,
|
||||
radiusNorm = distance(x, y, z),
|
||||
velocityNorm = distance(vx, vy, vz)
|
||||
)
|
||||
|
||||
private fun textRow(code: String, name: String, first: String?, second: String?): TleCompareRowDTO =
|
||||
@@ -100,7 +146,7 @@ class TleComparisonController(
|
||||
private fun timeRow(code: String, name: String, first: LocalDateTime?, second: LocalDateTime?): TleCompareRowDTO {
|
||||
val formatter = DateTimeFormatter.ISO_LOCAL_DATE_TIME
|
||||
val delta = if (first != null && second != null) {
|
||||
val seconds = java.time.Duration.between(first, second).seconds
|
||||
val seconds = Duration.between(first, second).seconds
|
||||
formatSecondsDelta(seconds)
|
||||
} else {
|
||||
null
|
||||
@@ -117,11 +163,13 @@ class TleComparisonController(
|
||||
delta = if (first != null && second != null) (second - first).formatSignedNumber() else null
|
||||
)
|
||||
|
||||
private fun Double.formatNumber(): String = String.format(Locale.US, "%.8f", this)
|
||||
private fun distance(x: Double, y: Double, z: Double): Double = sqrt(x * x + y * y + z * z)
|
||||
|
||||
private fun Double.formatNumber(): String = String.format(Locale.US, "%.6f", this)
|
||||
|
||||
private fun Double.formatSignedNumber(): String {
|
||||
val sign = if (this > 0) "+" else ""
|
||||
return sign + String.format(Locale.US, "%.8f", this)
|
||||
return sign + String.format(Locale.US, "%.6f", this)
|
||||
}
|
||||
|
||||
private fun formatSecondsDelta(seconds: Long): String {
|
||||
|
||||
+28
-3
@@ -8,7 +8,8 @@ class TleTextRequestDTO(
|
||||
|
||||
class TleCompareRequestDTO(
|
||||
var first: String = "",
|
||||
var second: String = ""
|
||||
var second: String = "",
|
||||
var time: LocalDateTime? = null
|
||||
)
|
||||
|
||||
data class TleParsedDTO(
|
||||
@@ -32,6 +33,19 @@ data class TleParsedDTO(
|
||||
val meanMotionTLE: Double? = null
|
||||
)
|
||||
|
||||
data class TlePositionDTO(
|
||||
val time: LocalDateTime,
|
||||
val revolution: Long,
|
||||
val x: Double,
|
||||
val y: Double,
|
||||
val z: Double,
|
||||
val vx: Double,
|
||||
val vy: Double,
|
||||
val vz: Double,
|
||||
val radiusNorm: Double,
|
||||
val velocityNorm: Double
|
||||
)
|
||||
|
||||
data class TleCompareRowDTO(
|
||||
val code: String,
|
||||
val name: String,
|
||||
@@ -41,7 +55,18 @@ data class TleCompareRowDTO(
|
||||
)
|
||||
|
||||
data class TleCompareResponseDTO(
|
||||
val first: TleParsedDTO,
|
||||
val second: TleParsedDTO,
|
||||
val time: LocalDateTime,
|
||||
val firstEpoch: LocalDateTime,
|
||||
val secondEpoch: LocalDateTime,
|
||||
val first: TlePositionDTO,
|
||||
val second: TlePositionDTO,
|
||||
val radiusVectorDelta: TlePositionDeltaDTO,
|
||||
val rows: List<TleCompareRowDTO>
|
||||
)
|
||||
|
||||
data class TlePositionDeltaDTO(
|
||||
val dx: Double,
|
||||
val dy: Double,
|
||||
val dz: Double,
|
||||
val norm: Double
|
||||
)
|
||||
|
||||
+11
@@ -11,6 +11,7 @@ import space.nstart.pcp.pcp_types_lib.dto.ballistics.FlightLineDTO
|
||||
import space.nstart.pcp.pcp_types_lib.dto.ballistics.OrbPointDTO
|
||||
import space.nstart.pcp.pcp_types_lib.dto.ballistics.SatelliteOrbitAvailabilityDTO
|
||||
import space.nstart.pcp.pcp_types_lib.dto.ballistics.TLEDTO
|
||||
import space.nstart.pcp.pcp_types_lib.dto.ballistics.TlePointRequestDTO
|
||||
import space.nstart.pcp.slots_service.dto.tlecomparison.TleParsedDTO
|
||||
import space.nstart.pcp.slots_service.configuration.CustomErrorException
|
||||
import java.net.URI
|
||||
@@ -80,6 +81,16 @@ class BallisticsService (webClientBuilderProvider: ObjectProvider<WebClient.Buil
|
||||
.block()
|
||||
?: throw CustomErrorException("Не удалось разобрать TLE")
|
||||
|
||||
fun tlePoint(tle: TLEDTO, time: LocalDateTime): OrbPointDTO =
|
||||
webClientBuilder.build()
|
||||
.post()
|
||||
.uri("$url/api/tle/point")
|
||||
.bodyValue(TlePointRequestDTO(tle = tle, time = time))
|
||||
.retrieve()
|
||||
.bodyToMono(OrbPointDTO::class.java)
|
||||
.block()
|
||||
?: throw CustomErrorException("Не удалось рассчитать положение по TLE на время $time")
|
||||
|
||||
private fun withTimeInterval(
|
||||
path: String,
|
||||
timeStart: LocalDateTime?,
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
bindEvents();
|
||||
updateReference('first', null);
|
||||
updateReference('second', null);
|
||||
updateTimeLimits();
|
||||
});
|
||||
|
||||
function bindEvents() {
|
||||
@@ -16,6 +17,7 @@
|
||||
const second = el('tle-second-text');
|
||||
first?.addEventListener('input', () => scheduleParse('first'));
|
||||
second?.addEventListener('input', () => scheduleParse('second'));
|
||||
el('tle-comparison-time')?.addEventListener('change', validateCalculationTime);
|
||||
el('tle-compare-submit')?.addEventListener('click', compareTle);
|
||||
el('tle-compare-clear')?.addEventListener('click', clearPage);
|
||||
}
|
||||
@@ -26,6 +28,7 @@
|
||||
if (!text.trim()) {
|
||||
state[side] = null;
|
||||
updateReference(side, null);
|
||||
updateTimeLimits();
|
||||
return;
|
||||
}
|
||||
setStatus(side, 'Разбор...', 'text-muted');
|
||||
@@ -37,10 +40,12 @@
|
||||
const parsed = await postJson('/api/tle-comparison/parse', { tle: getTleText(side) });
|
||||
state[side] = parsed;
|
||||
updateReference(side, parsed);
|
||||
updateTimeLimits();
|
||||
setStatus(side, 'Разобрано', 'tle-parse-ok');
|
||||
} catch (error) {
|
||||
state[side] = null;
|
||||
updateReference(side, null);
|
||||
updateTimeLimits();
|
||||
setStatus(side, error.message || 'Ошибка разбора', 'tle-parse-error');
|
||||
}
|
||||
}
|
||||
@@ -49,21 +54,25 @@
|
||||
showAlert('');
|
||||
const first = getTleText('first');
|
||||
const second = getTleText('second');
|
||||
const time = getCalculationTime();
|
||||
if (!first.trim() || !second.trim()) {
|
||||
showAlert('Введите оба TLE для сравнения.', 'warning');
|
||||
return;
|
||||
}
|
||||
if (!time) {
|
||||
showAlert('Задайте время расчета.', 'warning');
|
||||
return;
|
||||
}
|
||||
const minTime = maxEpoch();
|
||||
if (minTime && time < minTime) {
|
||||
showAlert(`Время расчета должно быть не раньше максимальной эпохи TLE: ${formatDateTime(minTime)}.`, 'warning');
|
||||
return;
|
||||
}
|
||||
|
||||
setCompareLoading(true);
|
||||
try {
|
||||
const response = await postJson('/api/tle-comparison/compare', { first, second });
|
||||
state.first = response.first;
|
||||
state.second = response.second;
|
||||
updateReference('first', response.first);
|
||||
updateReference('second', response.second);
|
||||
setStatus('first', 'Разобрано', 'tle-parse-ok');
|
||||
setStatus('second', 'Разобрано', 'tle-parse-ok');
|
||||
renderComparison(response.rows || []);
|
||||
const response = await postJson('/api/tle-comparison/compare', { first, second, time });
|
||||
renderComparison(response);
|
||||
} catch (error) {
|
||||
showAlert(error.message || 'Не удалось выполнить сравнение TLE.', 'danger');
|
||||
} finally {
|
||||
@@ -71,7 +80,8 @@
|
||||
}
|
||||
}
|
||||
|
||||
function renderComparison(rows) {
|
||||
function renderComparison(response) {
|
||||
const rows = response?.rows || [];
|
||||
const body = el('tle-comparison-result-body');
|
||||
const empty = el('tle-comparison-empty');
|
||||
const wrap = el('tle-comparison-table-wrap');
|
||||
@@ -98,9 +108,9 @@
|
||||
empty.classList.add('d-none');
|
||||
wrap.classList.remove('d-none');
|
||||
if (meta) {
|
||||
const firstEpoch = formatDateTime(state.first?.time);
|
||||
const secondEpoch = formatDateTime(state.second?.time);
|
||||
meta.textContent = `Эпохи: TLE 1 — ${firstEpoch || '—'}, TLE 2 — ${secondEpoch || '—'}`;
|
||||
const deltaNorm = response.radiusVectorDelta?.norm;
|
||||
const deltaText = Number.isFinite(deltaNorm) ? `${formatNumber(deltaNorm)} м` : '—';
|
||||
meta.textContent = `Время расчета: ${formatDateTime(response.time)}. |Δr| = ${deltaText}. Эпохи: TLE 1 — ${formatDateTime(response.firstEpoch)}, TLE 2 — ${formatDateTime(response.secondEpoch)}.`;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -114,16 +124,46 @@
|
||||
}
|
||||
}
|
||||
|
||||
function updateTimeLimits() {
|
||||
const input = el('tle-comparison-time');
|
||||
const helper = el('tle-comparison-time-helper');
|
||||
const minTime = maxEpoch();
|
||||
if (!input) return;
|
||||
if (!minTime) {
|
||||
input.removeAttribute('min');
|
||||
if (helper) helper.textContent = 'Минимальное допустимое время появится после разбора двух TLE.';
|
||||
return;
|
||||
}
|
||||
input.min = toDateTimeLocalValue(minTime);
|
||||
if (!input.value || input.value < input.min) {
|
||||
input.value = input.min;
|
||||
}
|
||||
if (helper) helper.textContent = `Минимальное допустимое время: ${formatDateTime(minTime)}.`;
|
||||
}
|
||||
|
||||
function validateCalculationTime() {
|
||||
const input = el('tle-comparison-time');
|
||||
const minTime = maxEpoch();
|
||||
if (!input || !minTime || !input.value) return;
|
||||
const time = getCalculationTime();
|
||||
if (time && time < minTime) {
|
||||
showAlert(`Время расчета должно быть не раньше максимальной эпохи TLE: ${formatDateTime(minTime)}.`, 'warning');
|
||||
}
|
||||
}
|
||||
|
||||
function clearPage() {
|
||||
el('tle-first-text').value = '';
|
||||
el('tle-second-text').value = '';
|
||||
const time = el('tle-comparison-time');
|
||||
if (time) time.value = '';
|
||||
state.first = null;
|
||||
state.second = null;
|
||||
updateReference('first', null);
|
||||
updateReference('second', null);
|
||||
updateTimeLimits();
|
||||
showAlert('');
|
||||
renderComparison([]);
|
||||
setText('tle-comparison-result-meta', 'Нажмите “Сравнить” после ввода двух TLE.');
|
||||
renderComparison({ rows: [] });
|
||||
setText('tle-comparison-result-meta', 'Задайте время не раньше максимальной эпохи TLE и нажмите “Сравнить”.');
|
||||
}
|
||||
|
||||
async function postJson(url, payload) {
|
||||
@@ -145,6 +185,16 @@
|
||||
return response.json();
|
||||
}
|
||||
|
||||
function maxEpoch() {
|
||||
if (!state.first?.time || !state.second?.time) return null;
|
||||
return state.first.time >= state.second.time ? state.first.time : state.second.time;
|
||||
}
|
||||
|
||||
function getCalculationTime() {
|
||||
const value = el('tle-comparison-time')?.value;
|
||||
return value ? value : null;
|
||||
}
|
||||
|
||||
function getTleText(side) {
|
||||
return el(`tle-${side}-text`)?.value || '';
|
||||
}
|
||||
@@ -160,7 +210,7 @@
|
||||
const button = el('tle-compare-submit');
|
||||
if (!button) return;
|
||||
button.disabled = loading;
|
||||
button.textContent = loading ? 'Сравнение...' : 'Сравнить';
|
||||
button.textContent = loading ? 'Расчет...' : 'Рассчитать';
|
||||
}
|
||||
|
||||
function showAlert(message, type = 'info') {
|
||||
@@ -174,11 +224,20 @@
|
||||
: '';
|
||||
}
|
||||
|
||||
function toDateTimeLocalValue(value) {
|
||||
if (!value) return '';
|
||||
return String(value).slice(0, 19);
|
||||
}
|
||||
|
||||
function formatDateTime(value) {
|
||||
if (!value) return '';
|
||||
return String(value).replace('T', ' ');
|
||||
}
|
||||
|
||||
function formatNumber(value) {
|
||||
return Number(value).toLocaleString('ru-RU', { maximumFractionDigits: 3 });
|
||||
}
|
||||
|
||||
function emptyToDash(value) {
|
||||
return value === null || value === undefined || value === '' ? '—' : value;
|
||||
}
|
||||
|
||||
@@ -13,10 +13,10 @@
|
||||
<div class="d-flex justify-content-between align-items-start flex-wrap gap-3 mb-4">
|
||||
<div>
|
||||
<h2 class="mb-1">Сравнение TLE</h2>
|
||||
<div class="catalog-helper">Вставьте два набора TLE, проверьте эпохи и сравните параметры, полученные через ballistics-service.</div>
|
||||
<div class="catalog-helper">Вставьте два набора TLE, проверьте эпохи и рассчитайте положение спутника на заданный момент времени.</div>
|
||||
</div>
|
||||
<div class="d-flex gap-2 flex-wrap">
|
||||
<button id="tle-compare-submit" type="button" class="btn btn-primary">Сравнить</button>
|
||||
<button id="tle-compare-submit" type="button" class="btn btn-primary">Рассчитать</button>
|
||||
<button id="tle-compare-clear" type="button" class="btn btn-outline-secondary">Очистить</button>
|
||||
</div>
|
||||
</div>
|
||||
@@ -26,9 +26,18 @@
|
||||
<div class="card catalog-card tle-input-card mb-4">
|
||||
<div class="card-header">
|
||||
<div class="catalog-section-title">Ввод данных</div>
|
||||
<div class="catalog-helper mt-1">Можно вставить две или три строки TLE. Заголовок определяется автоматически, строки 1 и 2 обязательны.</div>
|
||||
<div class="catalog-helper mt-1">Можно вставить две или три строки TLE. Заголовок определяется автоматически, строки 1 и 2 обязательны. Время расчета должно быть не раньше максимальной эпохи двух TLE.</div>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div class="row g-3 mb-4">
|
||||
<div class="col-12 col-md-5 col-xl-4">
|
||||
<label for="tle-comparison-time" class="form-label">Время расчета</label>
|
||||
<input id="tle-comparison-time" type="datetime-local" class="form-control" step="1"/>
|
||||
</div>
|
||||
<div class="col-12 col-md-7 col-xl-8 d-flex align-items-end">
|
||||
<div id="tle-comparison-time-helper" class="catalog-helper">Минимальное допустимое время появится после разбора двух TLE.</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row g-4">
|
||||
<div class="col-12 col-xl-6">
|
||||
<div class="tle-input-widget h-100">
|
||||
@@ -90,11 +99,11 @@
|
||||
<div class="card-header d-flex justify-content-between align-items-start gap-3 flex-wrap">
|
||||
<div>
|
||||
<div class="catalog-section-title">Результаты</div>
|
||||
<div id="tle-comparison-result-meta" class="catalog-helper mt-1">Нажмите “Сравнить” после ввода двух TLE.</div>
|
||||
<div id="tle-comparison-result-meta" class="catalog-helper mt-1">Задайте время не раньше максимальной эпохи TLE и нажмите “Сравнить”.</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card-body p-0">
|
||||
<div id="tle-comparison-empty" class="catalog-empty">Результаты сравнения появятся здесь.</div>
|
||||
<div id="tle-comparison-empty" class="catalog-empty">Параметры положения и разность радиус-векторов появятся здесь.</div>
|
||||
<div id="tle-comparison-table-wrap" class="table-responsive tle-result-table-wrap d-none">
|
||||
<table class="table table-hover mb-0 align-middle">
|
||||
<thead class="table-light">
|
||||
@@ -102,7 +111,7 @@
|
||||
<th>Параметр</th>
|
||||
<th>TLE 1</th>
|
||||
<th>TLE 2</th>
|
||||
<th>Разница</th>
|
||||
<th>Разница / значение</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody id="tle-comparison-result-body"></tbody>
|
||||
|
||||
Reference in New Issue
Block a user