Files
qt5-legatus-legacy/Lts/Services/AreaEffectSimulation/EffectorFiringStrategy/dm72_firing_strategy.cpp
T
torstenandCopilot 3028302f7b Qt6 Migration: Merge latest Qt5 devel content and apply migration fixes
- Merged 292 changed files from dlbo snapshot
- Fixed IDL enum collisions (PortType, PdmEventType)
- Fixed Grantlee template for C++11 scoped enums
- Fixed CMake VERSION_GREATER syntax
- Added missing directories (THP, LtsRestClient, etc.)

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-09-15 18:20:51 +00:00

215 lines
8.0 KiB
C++
Executable File

/**
* Effector Firing Strategy for DM72
* $Id$
* $HeadURL$
* $Author$
* Created by Hannes Fritsche, ESZS, 2026-05-21
*/
#include "dm72_firing_strategy.h"
#include "main_handler.h"
#include "object_effector.h"
#include <LtsCoordTrans/lts_coord_trans.h>
#include <TrainingAreaBase/Plugins/AdlerJson/telegrams.h>
#include <Trajectory/trajectory_transform.h>
#include <QJsonArray>
#include <QJsonDocument>
#include <QJsonObject>
#include <QJsonValue>
#include <QTransform>
namespace AreaEffectSimulation
{
Dm72FiringStrategy::Dm72FiringStrategy(ObjectEffector *effector)
: AbstractFiringStrategy(effector)
{
init();
DEBUG_LOG(m_logger) << "using Dm72FiringStrategy";
}
bool Dm72FiringStrategy::doFiring()
{
m_transformedTrajectory = TrajectoryTransform::transformToNewTarget(m_effector->intendendTrajectory(), m_impactPoints.at(m_impactPointIndex++));
if (m_transformedTrajectory.size() < 2)
{
ERROR_LOG(m_logger) << LOGFUNCTION << " transformedTrajectory is not sufficiant"
<< ", m_transformedTrajectory.size()" << m_transformedTrajectory.size();
return false;
}
if (m_impactPointIndex >= m_impactPoints.size())
m_impactPointIndex = 0;
const qint64 trajectoryStartTime = getStartTime();
const auto geometry = buildTrajectoryGeometry(m_transformedTrajectory);
const auto trajTimeAware = buildTrajectoryTimeAware(geometry, trajectoryStartTime);
QJsonObject props{{QStringLiteral("effectDurationMsec"), m_effectDurationMsec}};
const int chargeEffectId = sendChargeEffect(trajTimeAware, props);
if (chargeEffectId < 0)
{
ERROR_LOG(m_logger) << LOGFUNCTION << " sendChargeEffect failed";
return false;
}
emit signalFired(m_effector->participantId(), m_effector->aesItemId(), {chargeEffectId});
return true;
}
void Dm72FiringStrategy::init()
{
DEBUG_LOG(m_logger) << LOGFUNCTION << " START"
<< ", m_effector->participantId=" << m_effector->participantId()
<< ", m_effector->aesItemId=" << m_effector->aesItemId();
using namespace AdlerJson;
using namespace LtsCoordTrans;
m_impactPointIndex = 0;
m_impactPoints.clear();
bool bOk = false;
WirkmittelFeuerkommandoRakete wirkmittel;
const std::string propertiesStdStr = m_effector->effectorData().properties();
DEBUG_LOG(m_logger) << LOGFUNCTION << " START" << ", propertiesStdStr= '" << propertiesStdStr << "'";
const QJsonObject properties = QJsonDocument::fromJson(QByteArray::fromStdString(propertiesStdStr)).object();
const Core::GeoPosition geoStartPos = participantPosition(m_effector->participantId());
const UtmCoordinate utmStartPos = UtmCoordinate::fromGeographic(geoStartPos.lon(), geoStartPos.lat());
wirkmittelFeuerkommandoRaketeFromJson(properties, wirkmittel, bOk);
using UtmWithHeight = std::pair<UtmCoordinate, int>;
m_effectDurationMsec = getFeuerkommandoRaketeEffectDurationMsec(properties);
QVector<UtmWithHeight> tempUtmTreffpunkte;
tempUtmTreffpunkte.reserve(wirkmittel.geschoss.anzahl);
for (const Treffpunkt &treffpunkt : qAsConst(wirkmittel.treffpunkte))
{
const UtmCoordinate utmTreffpunkt = UtmCoordinate::fromGeographic(treffpunkt.koordinate.koordinate.longitude * deg2rad(),
treffpunkt.koordinate.koordinate.latitude * deg2rad(),
utmStartPos.zone);
for (int anzahlCounter = 1; anzahlCounter <= treffpunkt.anzahl; ++anzahlCounter)
tempUtmTreffpunkte.append({utmTreffpunkt, treffpunkt.koordinate.hoehe});
}
const QVector<QPointF> mineLayingCentroidDeviations = mineLayingCentroids();
int deviationIndex = 0;
for (const UtmWithHeight &tempUtmTreffpunkt : tempUtmTreffpunkte)
{
const QPointF &deviation = mineLayingCentroidDeviations.at(deviationIndex++);
const LtsUtils::QPoint3D impactPoint(tempUtmTreffpunkt.first.x - utmStartPos.x + deviation.x(),
tempUtmTreffpunkt.first.y - utmStartPos.y + deviation.y(),
tempUtmTreffpunkt.second);
m_impactPoints.append(impactPoint);
if (deviationIndex >= mineLayingCentroidDeviations.size())
deviationIndex = 0;
}
DEBUG_LOG(m_logger) << "Got " << m_impactPoints.size() << " impact points";
TRACE_LOG(m_logger) << LOGFUNCTION << " END";
}
QVector<QPointF> Dm72FiringStrategy::mineLayingCentroids() const
{
using namespace LtsCoordTrans;
const int chargeTypeId = m_effector->effectorData().chargeStruct().chargeTypeId();
const ProjectDataManagement::ChargeType &chargeType = m_effector->mainHandler()->dds()->pdmDtc()->chargeType(chargeTypeId);
const QJsonObject chargeTypeProperties = QJsonDocument::fromJson(QByteArray::fromStdString(chargeType.properties())).object();
const QJsonObject mineLaying = chargeTypeProperties.value(QStringLiteral("mineLaying")).toObject();
const QJsonArray mineLayingPattern = mineLaying.value(QStringLiteral("pattern")).toArray();
/* QMap is used here because it sort its entries by key.
* Since each pattern has an id and we want the pattern sorted by id, adding them to a QMap
* automatically sorts the pattern.
*/
QMap<int, QPointF> centroidsMap;
const UtmCoordinate mhpUtm = UtmCoordinate::fromGeographic(m_effector->effectorData().mhpPosition().lon(),
m_effector->effectorData().mhpPosition().lat());
const double rotationAngle = angleToMhpUtm(mhpUtm.toPoint());
QTransform rotation;
rotation.rotate(rotationAngle);
for (const QJsonValue &patternValue: mineLayingPattern)
{
const QJsonObject patternObject = patternValue.toObject();
const int patternId = patternObject.value(QStringLiteral("id")).toInt();
const QJsonArray patternPositionArray = patternObject.value(QStringLiteral("positions")).toArray();
int sumNorthing = 0;
int sumEasting = 0;
for (const QJsonValue &positionValue : patternPositionArray)
{
const QJsonObject positionObject = positionValue.toObject();
const int northing = positionObject.value(QStringLiteral("northing")).toInt();
const int easting = positionObject.value(QStringLiteral("easting")).toInt();
sumNorthing += northing;
sumEasting += easting;
}
QPointF centroid(static_cast<double>(sumEasting) / patternPositionArray.size(), static_cast<double>(sumNorthing) / patternPositionArray.size());
centroid = rotation.map(centroid);
centroidsMap.insert(patternId, centroid);
}
if (centroidsMap.isEmpty())
{
WARNING_LOG(m_logger) << "Could not read mine laying patterns from DM72 ammunition properties for creation of DM72 trajectories.";
return { QPointF(0.0, 0.0) }; // We have to return at least one QPointF(0.0, 0.0) to ensure later DM72 functionality
}
int vectorIndex = 0;
QVector<QPointF> mineLayingCentroids;
mineLayingCentroids.reserve(centroidsMap.size());
for (auto it = centroidsMap.constBegin(); it != centroidsMap.constEnd(); ++it)
mineLayingCentroids.insert(vectorIndex++, it.value()); // insert with index ensures sorting
return mineLayingCentroids;
}
double Dm72FiringStrategy::angleToMhpUtm(const QPointF &mhpUtm) const
{
const ParticipantManagement::ParticipantPosition &ownPosition = m_effector->mainHandler()->dds()->ppmDtc()->participantPosition(m_effector->participantId());
const LtsCoordTrans::UtmCoordinate ownPositionUtm = LtsCoordTrans::UtmCoordinate::fromGeographic(ownPosition.pos().lon(), ownPosition.pos().lat());
const double angle = QLineF(mhpUtm, ownPositionUtm.toPoint()).angle();
double northAngle = 90 - angle;
northAngle = std::fmod(northAngle, 360.0);
if (northAngle < 0.0)
northAngle += 360.0;
DEBUG_LOG(m_logger) << "angleToMhpUtm:"
<< "mhpUtm=" << mhpUtm
<< ", ownPositionUtm=" << ownPositionUtm.toPoint()
<< ", angle=" << angle
<< ", northAngle=" << northAngle;
return northAngle;
}
} // namespace AreaEffectSimulation