/** * 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 #include #include #include #include #include #include #include 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; m_effectDurationMsec = getFeuerkommandoRaketeEffectDurationMsec(properties); QVector 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 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 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 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(sumEasting) / patternPositionArray.size(), static_cast(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 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