- 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>
215 lines
8.0 KiB
C++
Executable File
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
|