From ccce642664714deb1addf8161d92dbd98967b4e9 Mon Sep 17 00:00:00 2001 From: mandelbroo Date: Tue, 12 May 2026 19:05:17 +0000 Subject: [PATCH 01/15] cmake enable tests; vscode task for tests --- .vscode/tasks.json | 5 +++++ CMakeLists.txt | 2 ++ 2 files changed, 7 insertions(+) diff --git a/.vscode/tasks.json b/.vscode/tasks.json index a29b2ed..20bfa8f 100644 --- a/.vscode/tasks.json +++ b/.vscode/tasks.json @@ -32,6 +32,11 @@ "panel": "dedicated", "clear": true } + }, + { + "label": "CTest: run all test", + "type": "shell", + "command": "ctest --test-dir build/debug --output-on-failure" } ] } diff --git a/CMakeLists.txt b/CMakeLists.txt index 79f396b..19abe51 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -19,3 +19,5 @@ add_subdirectory(homework_04) # Demo-код для заняття 2.4: локальне і віддалене відлагодження через GDB. add_subdirectory(demos/lesson_2_4/debug_probe) + +enable_testing() From f50028f3ad906844f237d51b38fd83bafc9bca9d Mon Sep 17 00:00:00 2001 From: mandelbroo Date: Tue, 12 May 2026 19:05:30 +0000 Subject: [PATCH 02/15] globla cmake include hw6 --- CMakeLists.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/CMakeLists.txt b/CMakeLists.txt index 19abe51..277f717 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -16,6 +16,7 @@ set(CMAKE_EXPORT_COMPILE_COMMANDS ON) # Домашні роботи. По мірі появи додаємо кожну окремим add_subdirectory. add_subdirectory(homework_04) +add_subdirectory(homework_06) # Demo-код для заняття 2.4: локальне і віддалене відлагодження через GDB. add_subdirectory(demos/lesson_2_4/debug_probe) From c8a48b020e68b9c6ef415328fedead5e4f2760f0 Mon Sep 17 00:00:00 2001 From: mandelbroo Date: Sun, 17 May 2026 11:00:02 +0000 Subject: [PATCH 03/15] ballistics lib --- homework_06/include/ballistics.hpp | 33 +++++++++++ homework_06/src/ballistics.cpp | 94 ++++++++++++++++++++++++++++++ 2 files changed, 127 insertions(+) create mode 100644 homework_06/include/ballistics.hpp create mode 100644 homework_06/src/ballistics.cpp diff --git a/homework_06/include/ballistics.hpp b/homework_06/include/ballistics.hpp new file mode 100644 index 0000000..e5a7075 --- /dev/null +++ b/homework_06/include/ballistics.hpp @@ -0,0 +1,33 @@ +#include + +struct Coord { + double x; + double y; + double z = 0.0f; +}; + +struct AmmoParams { + std::string name = "Unknown"; + float mass; // маса (кг) + float drag; // коефіцієнт опору + float lift; // коефіцієнт підйому +}; + +struct DroneConfig { + Coord startPos; // початкова позиція (x, y) + AmmoParams ammo; + float altitude; // висота + float initialDir; // початковий напрямок (рад) + float attackSpeed; // швидкість атаки (м/с) + float accelPath; // шлях розгону (м) + // char ammoName[32]; // обрані боєприпаси + float arrayTimeStep; // крок часу масиву цілей + float simTimeStep; // крок симуляції + float hitRadius; // радіус влучення + float angularSpeed; // кутова швидкість (рад/с) + float turnThreshold; // поріг повороту (рад) +}; + +float calcAmmoFallTime(const AmmoParams& ammo, const float& attackSpeed, const float& droneHeight); +float calcHorizontalDistance(const float& fallTime, DroneConfig& drone, const Coord& targetPosition); +float calcDistance(const Coord& a, const Coord& b); diff --git a/homework_06/src/ballistics.cpp b/homework_06/src/ballistics.cpp new file mode 100644 index 0000000..55b25d5 --- /dev/null +++ b/homework_06/src/ballistics.cpp @@ -0,0 +1,94 @@ +#define _USE_MATH_DEFINES +#include "ballistics.hpp" + +#include +#include + +using namespace std; + +const float GRAVITY_ACCEL = 9.81f; + +float calcAmmoFallTime(const AmmoParams& ammo, const float& attackSpeed, const float& droneHeight) { + /* + V₀ — швидкість атаки дрона + Z₀ — висота дрона (zd) + g = 9.81 м/с² + + a = d·g·m − 2d²·l·V₀ + b = −3g·m² + 3d·l·m·V₀ + c = 6m²·Z₀ + */ + + float drag² = ammo.drag * ammo.drag; + float mass² = ammo.mass * ammo.mass; + float a = ammo.drag * GRAVITY_ACCEL * ammo.mass - 2.0f * drag² * ammo.lift * attackSpeed; + float b = -3.0f * GRAVITY_ACCEL * mass² + 3.0f * ammo.drag * ammo.lift * ammo.mass * attackSpeed; + float c = 6.0f * mass² * droneHeight; + + /* + p = − b² / (3a²) + q = 2b³ / (27a³) + c / a + φ = arccos( 3q / (2p) · √(−3/p) ) + t = 2√(−p/3) · cos( (φ + 4π) / 3 ) − b / (3a) + */ + + float a² = a * a; + float a³ = a² * a; + float b² = b * b; + float b³ = b² * b; + + float p = -b² / static_cast(3.0 * a²); + float q = 2.0 * b³ / static_cast(27.0 * a³) + c / static_cast(a); + float phi = acos(3.0 * q / static_cast(2.0 * p) * sqrt(-3.0 / static_cast(p))); + float t = 2.0 * sqrt(-p / 3.0) * cos((phi + 4.0 * M_PI) / 3.0) - b / static_cast(3.0 * a); + + return t; +} + +float calcHorizontalDistance(const float& fallTime, DroneConfig& drone, const Coord& targetPosition) { + /* + h = V₀t − t²d·V₀/(2m) + t³(6d·g·l·m − 6d²(l²-1)·V₀)/(36m²) + + + t⁴ (−6d²g·l·(1+l²+l⁴)m + 3d³l²(1+l²)V₀ + 6d³l⁴(1+l²)V₀) / (36(1+l²)²m³) + + t⁵(3d³g·l³m − 3d⁴l²(1+l²)V₀) / (36(1+l²)m⁴) + */ + float t = fallTime; + float drag = drone.ammo.drag; + float lift = drone.ammo.lift; + float mass = drone.ammo.mass; + float attackSpeed = drone.attackSpeed; + + float t² = t * t; + float t³ = t² * t; + float t⁴ = t³ * t; + float t⁵ = t⁴ * t; + float drag² = drag * drag; + float drag³ = drag² * drag; + float drag⁴ = drag³ * drag; + float lift² = lift * lift; + float lift³ = lift² * lift; + float lift⁴ = lift³ * lift; + float mass² = mass * mass; + float mass³ = mass² * mass; + float mass⁴ = mass³ * mass; + + float h = + attackSpeed * t - t² * drag * attackSpeed / (2.0 * mass) + t³ * (6.0 * drag * GRAVITY_ACCEL * lift * mass - 6.0 * drag² * (lift² - 1.0) * attackSpeed) / (36.0 * mass²) + t⁴ * (-6.0 * drag² * GRAVITY_ACCEL * lift * (1.0 + lift² + lift⁴) * mass + 3.0 * drag³ * lift² * (1.0 + lift²) * attackSpeed + 6.0 * drag³ * lift⁴ * (1.0 + lift²) * attackSpeed) / (36.0 * pow(1.0 + lift², 2) * mass³) + t⁵ * (3.0 * drag³ * GRAVITY_ACCEL * lift³ * mass - 3.0 * drag⁴ * lift² * (1.0 + lift²) * attackSpeed) / (36.0 * (1.0 + lift²) * mass⁴); + + // CHECK IF DRONE IS PRECISELY OVER THE TARGET SO DISTANCE IS NOT ZERO ------ + if (targetPosition.x == drone.startPos.x && targetPosition.y == drone.startPos.y) { + drone.startPos.x = drone.startPos.x - 0.0001f; + drone.startPos.y = drone.startPos.y - 0.0001f; + } + + return h; +} + +float calcDistance(const Coord& a, const Coord& b) { + /* + D = √( (targetX − xd)² + (targetY − yd)² ) + */ + + float distance = sqrt(pow(b.x - a.x, 2) + pow(b.y - a.y, 2)); + + return distance; +} From c7614af299f23147a47b6055c46c259695dbba91 Mon Sep 17 00:00:00 2001 From: mandelbroo Date: Sun, 17 May 2026 11:23:34 +0000 Subject: [PATCH 04/15] extract more functions --- homework_06/include/ballistics.hpp | 11 ++++++--- homework_06/src/ballistics.cpp | 39 +++++++++++++++++++++++++++++- 2 files changed, 45 insertions(+), 5 deletions(-) diff --git a/homework_06/include/ballistics.hpp b/homework_06/include/ballistics.hpp index e5a7075..d84cad2 100644 --- a/homework_06/include/ballistics.hpp +++ b/homework_06/include/ballistics.hpp @@ -6,7 +6,7 @@ struct Coord { double z = 0.0f; }; -struct AmmoParams { +struct Ammo { std::string name = "Unknown"; float mass; // маса (кг) float drag; // коефіцієнт опору @@ -15,7 +15,7 @@ struct AmmoParams { struct DroneConfig { Coord startPos; // початкова позиція (x, y) - AmmoParams ammo; + Ammo ammo; float altitude; // висота float initialDir; // початковий напрямок (рад) float attackSpeed; // швидкість атаки (м/с) @@ -28,6 +28,9 @@ struct DroneConfig { float turnThreshold; // поріг повороту (рад) }; -float calcAmmoFallTime(const AmmoParams& ammo, const float& attackSpeed, const float& droneHeight); -float calcHorizontalDistance(const float& fallTime, DroneConfig& drone, const Coord& targetPosition); +float calcAmmoFallTime(const Ammo& ammo, const float& attackSpeed, const float& droneHeight); float calcDistance(const Coord& a, const Coord& b); +Coord calcFireCoordinates(const float& horizontalDistance, const float& distanceToTarget, const float& xd, const float& yd, const float& targetX, const float& targetY); +float calcHorizontalDistance(const float& fallTime, DroneConfig& drone, const Coord& targetPosition); +bool isManoeuvreNeeded(const float& horizontalDistance, const float& accelerationPath, const float& distanceToTarget); +void processManouvre(DroneConfig& drone, const Coord& targetPosition); \ No newline at end of file diff --git a/homework_06/src/ballistics.cpp b/homework_06/src/ballistics.cpp index 55b25d5..2daf232 100644 --- a/homework_06/src/ballistics.cpp +++ b/homework_06/src/ballistics.cpp @@ -8,7 +8,7 @@ using namespace std; const float GRAVITY_ACCEL = 9.81f; -float calcAmmoFallTime(const AmmoParams& ammo, const float& attackSpeed, const float& droneHeight) { +float calcAmmoFallTime(const Ammo& ammo, const float& attackSpeed, const float& droneHeight) { /* V₀ — швидкість атаки дрона Z₀ — висота дрона (zd) @@ -92,3 +92,40 @@ float calcDistance(const Coord& a, const Coord& b) { return distance; } + +Coord calcFireCoordinates(const float& horizontalDistance, const float& distanceToTarget, const float& xd, const float& yd, const float& targetX, const float& targetY) { + /* + ratio = (D − h) / D + fireX = xd + (targetX − xd) · ratio + fireY = yd + (targetY − yd) · ratio + */ + + float ratio = (distanceToTarget - horizontalDistance) / static_cast(distanceToTarget); + float fireX = xd + (targetX - xd) * ratio; + float fireY = yd + (targetY - yd) * ratio; + + return {fireX, fireY}; +} + +bool isManoeuvreNeeded(const float& horizontalDistance, const float& accelerationPath, const float& distanceToTarget) { + /* + Manoeuvre is needed if (accelerationPath + horizontalDistance) > distanceToTarget + */ + + return (accelerationPath + horizontalDistance) > distanceToTarget; +} + +void processManouvre(DroneConfig& drone, const Coord& targetPosition) { + /* + xd' = targetX − (targetX − xd) · (h + accelerationPath) / D + yd' = targetY − (targetY − yd) · (h + accelerationPath) / D + */ + + float h = calcHorizontalDistance(calcAmmoFallTime(drone.ammo, drone.attackSpeed, drone.startPos.z), drone, targetPosition); + float distanceToTarget = calcDistance(drone.startPos, targetPosition); + + if (isManoeuvreNeeded(h, drone.accelPath, distanceToTarget)) { + drone.startPos.x = targetPosition.x - (targetPosition.x - drone.startPos.x) * (h + drone.accelPath) / distanceToTarget; + drone.startPos.y = targetPosition.y - (targetPosition.y - drone.startPos.y) * (h + drone.accelPath) / distanceToTarget; + } +} \ No newline at end of file From 72c0eb495647f3678d0e035b2c0762fdec7695d4 Mon Sep 17 00:00:00 2001 From: mandelbroo Date: Sun, 17 May 2026 11:23:49 +0000 Subject: [PATCH 05/15] add main cpp --- homework_06/src/main.cpp | 110 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 110 insertions(+) create mode 100644 homework_06/src/main.cpp diff --git a/homework_06/src/main.cpp b/homework_06/src/main.cpp new file mode 100644 index 0000000..bda2edb --- /dev/null +++ b/homework_06/src/main.cpp @@ -0,0 +1,110 @@ +#define _USE_MATH_DEFINES +#include +#include +#include +#include +#include + +#include "ballistics.hpp" + +using namespace std; + +const int AMMO_TYPES_COUNT = 5; + +const Ammo ARSENAL[AMMO_TYPES_COUNT] = { + {.name = "VOG-17", .mass = 0.35f, .drag = 0.07f, .lift = 0.0f}, + {.name = "M67", .mass = 0.6f, .drag = 0.1f, .lift = 0.0f}, + {.name = "RKG-3", .mass = 1.2f, .drag = 0.1f, .lift = 0.0f}, + {.name = "GLIDING-VOG", .mass = 0.45f, .drag = 0.1f, .lift = 1.0f}, + {.name = "GLIDING-RKG", .mass = 1.4f, .drag = 0.1f, .lift = 1.0f}, +}; + +int main(int argc, char** argv) { + if (argc != 2) { + cerr << "Usage: ballistics_cli " << endl; + return 1; + } + + const string INPUT_FILE = argv[1]; + const float GRAVITY_ACCEL = 9.81f; + + // READ FILE AND INIT ------------------------------------------------------- + std::ifstream inputFile(INPUT_FILE); + + if (!inputFile.is_open()) { + std::cerr << "Error: Could not open the file:" << INPUT_FILE << std::endl; + return 1; + } + + float xd, yd, zd, targetX, targetY, attackSpeed, accelerationPath; + std::string ammoName; + + inputFile >> xd >> yd >> zd >> targetX >> targetY >> attackSpeed >> accelerationPath >> ammoName; + + float drag = 0.0f; + float mass = 0.0f; + float lift = 0.0f; + + DroneConfig drone = {{xd, yd, zd}, {}, attackSpeed, accelerationPath}; + + for (int i = 0; i < AMMO_TYPES_COUNT; i++) { + if (ammoName == ARSENAL[i].name) { + drone.ammo = ARSENAL[i]; + break; + } + } + + cout << "AMMO TYPE: " << ammoName << endl; + + float t = calcAmmoFallTime(drone.ammo, attackSpeed, zd); + + cout << "Fall time: " << t << "s" << endl; + + float horizontalDistance = calcHorizontalDistance(t, drone, {targetX, targetY}); + + float distance = calcDistance({xd, yd}, {targetX, targetY}); + + cout << "Distance to target: " << distance << "m" << endl; + + // IS MANOEUVRE NEEDED? ----------------------------------------------------- + + cout << "Horizontal distance: " << horizontalDistance << "m" << endl; + + bool isManoeuvrePerformed = false; + float oldXd = xd; + float oldYd = yd; + + if (isManoeuvreNeeded(horizontalDistance, accelerationPath, distance)) { + cout << endl + << "Manoeuvre is needed!" << endl; + + processManouvre(drone, {targetX, targetY}); + + isManoeuvrePerformed = true; + + cout << "Drone moves to new x: " << drone.startPos.x << " y: " << drone.startPos.y << endl + << endl; + } + + Coord fireCoordinates = calcFireCoordinates(horizontalDistance, distance, oldXd, oldYd, targetX, targetY); + + cout << endl + << "Fire x: " << fireCoordinates.x << " y: " << fireCoordinates.y << endl; + + // WRITE RESULTS INTO FILE -------------------------------------------------- + + std::ofstream outputFile("output.txt"); + + if (!outputFile.is_open()) { + cout << "Error. Cannot open output file."; + return 1; + } + + if (isManoeuvrePerformed) { + outputFile << drone.startPos.x << " " << drone.startPos.y << " "; + } + + outputFile << fireCoordinates.x << " " << fireCoordinates.y; + + cout << endl; +} \ No newline at end of file From 4532a4c4db5a19c7791c057df9ece61c9127fd5b Mon Sep 17 00:00:00 2001 From: mandelbroo Date: Sun, 17 May 2026 11:24:14 +0000 Subject: [PATCH 06/15] working sample file --- homework_06/data/sample_vog17.txt | 1 + 1 file changed, 1 insertion(+) create mode 100644 homework_06/data/sample_vog17.txt diff --git a/homework_06/data/sample_vog17.txt b/homework_06/data/sample_vog17.txt new file mode 100644 index 0000000..5a297de --- /dev/null +++ b/homework_06/data/sample_vog17.txt @@ -0,0 +1 @@ +100 100 100 200 200 10 10 VOG-17 From 4fa0c37aec2884af775561caabcb7c14f44bc0a2 Mon Sep 17 00:00:00 2001 From: mandelbroo Date: Sun, 17 May 2026 11:25:42 +0000 Subject: [PATCH 07/15] cmake for hw6 --- homework_06/CMakeLists.txt | 40 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) create mode 100644 homework_06/CMakeLists.txt diff --git a/homework_06/CMakeLists.txt b/homework_06/CMakeLists.txt new file mode 100644 index 0000000..a5184f2 --- /dev/null +++ b/homework_06/CMakeLists.txt @@ -0,0 +1,40 @@ +cmake_minimum_required(VERSION 3.20) +project(telemetry_check CXX) + +set(CMAKE_CXX_STANDARD 20) +set(CMAKE_CXX_STANDARD_REQUIRED ON) +set(CMAKE_CXX_EXTENSIONS OFF) + +add_library(ballistics src/ballistics.cpp) + +target_include_directories(ballistics PUBLIC include) + +add_executable(ballistics_cli src/main.cpp) + +target_link_libraries(ballistics_cli PRIVATE ballistics) + +if(NOT CMAKE_CROSSCOMPILING) + + include(FetchContent) + + include(GoogleTest) + + FetchContent_Declare( + + googletest + + URL https://github.com/google/googletest/archive/refs/tags/v1.14.0.zip + + DOWNLOAD_EXTRACT_TIMESTAMP TRUE + + ) + + FetchContent_MakeAvailable(googletest) + + add_executable(ballistics_tests tests/ballistics_tests.cpp) + + target_link_libraries(ballistics_tests PRIVATE ballistics GTest::gtest_main) + + gtest_discover_tests(ballistics_tests DISCOVERY_MODE PRE_TEST) + +endif() From 44d7e1a63a9b5e36bfbe5cca055c179d4cb6e073 Mon Sep 17 00:00:00 2001 From: mandelbroo Date: Sun, 17 May 2026 11:25:56 +0000 Subject: [PATCH 08/15] tests and debug --- .vscode/launch.json | 46 +++++++++++++++++++++++++- CMakeLists.txt | 3 +- homework_06/tests/ballistics_tests.cpp | 38 +++++++++++++++++++++ 3 files changed, 85 insertions(+), 2 deletions(-) create mode 100644 homework_06/tests/ballistics_tests.cpp diff --git a/.vscode/launch.json b/.vscode/launch.json index 7e724e0..d478271 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -51,6 +51,50 @@ "ignoreFailures": true } ] + }, + { + // Локальний debug стартера ДЗ 6. preLaunchTask збирає debug preset. + "name": "Debug homework_06: ballistics", + "type": "cppdbg", + "request": "launch", + "program": "${workspaceFolder}/build/debug/homework_06/ballistics", + "args": [ + "${workspaceFolder}/homework_06/data/input.txt" + ], + "stopAtEntry": false, + "cwd": "${workspaceFolder}", + "environment": [], + "externalConsole": false, + "MIMode": "gdb", + "miDebuggerPath": "/usr/bin/gdb", + "preLaunchTask": "CMake: configure and build", + "setupCommands": [ + { + "description": "Увімкнути pretty-printing для STL типів у GDB.", + "text": "-enable-pretty-printing", + "ignoreFailures": true + } + ] + }, + { + "type": "cppdbg", + "request": "launch", + "name": "Debug homework_06/ballistics_tests", + "program": "${workspaceFolder}/build/debug/homework_06/ballistics_tests", + "stopAtEntry": false, + "cwd": "${workspaceFolder}", + "environment": [], + "externalConsole": false, + "MIMode": "gdb", + "miDebuggerPath": "/usr/bin/gdb", + "preLaunchTask": "CMake: configure and build", + "setupCommands": [ + { + "description": "Увімкнути pretty-printing для STL типів у GDB.", + "text": "-enable-pretty-printing", + "ignoreFailures": true + } + ] } ] -} +} \ No newline at end of file diff --git a/CMakeLists.txt b/CMakeLists.txt index 277f717..e1ffdc9 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -15,10 +15,11 @@ set(CMAKE_CXX_EXTENSIONS OFF) set(CMAKE_EXPORT_COMPILE_COMMANDS ON) # Домашні роботи. По мірі появи додаємо кожну окремим add_subdirectory. +enable_testing() + add_subdirectory(homework_04) add_subdirectory(homework_06) # Demo-код для заняття 2.4: локальне і віддалене відлагодження через GDB. add_subdirectory(demos/lesson_2_4/debug_probe) -enable_testing() diff --git a/homework_06/tests/ballistics_tests.cpp b/homework_06/tests/ballistics_tests.cpp new file mode 100644 index 0000000..400b7b6 --- /dev/null +++ b/homework_06/tests/ballistics_tests.cpp @@ -0,0 +1,38 @@ +#include +#include + +#include "ballistics.hpp" + +TEST(BallisticsTests, TestCalcFallTime) { + DroneConfig drone; + + drone.ammo.mass = 1.0f; + drone.ammo.drag = 0.5f; + drone.startPos = {0.0f, 0.0f, 100.0f}; + + float fallTime = calcAmmoFallTime(drone.ammo, drone.attackSpeed, drone.startPos.z); + + EXPECT_NEAR(std::round(fallTime * 1000.0f) / 1000.0f, 4.515f, 0.001f); +} + +TEST(BallisticsTests, TestCalcHorizontalDistance) { + DroneConfig drone; + + drone.ammo.mass = 1.0f; + drone.ammo.drag = 0.5f; + drone.startPos = {0.0f, 0.0f, 100.0f}; + drone.attackSpeed = 50.0f; + + float fallTime = calcAmmoFallTime(drone.ammo, drone.attackSpeed, drone.startPos.z); + Coord targetPosition = {100.0f, 0.0f}; + float horizontalDistance = calcHorizontalDistance(fallTime, drone, targetPosition); + EXPECT_NEAR(horizontalDistance, 224.5f, 0.1f); +} + +TEST(BallisticsTests, TestCalcDistance) { + Coord a = {0.0f, 0.0f}; + Coord b = {3.0f, 4.0f}; + + float distance = calcDistance(a, b); + EXPECT_NEAR(distance, 5.0f, 0.001f); +} From c8c6f1b1935a98cb3b326a994cc1eee7e9890c72 Mon Sep 17 00:00:00 2001 From: mandelbroo Date: Mon, 18 May 2026 14:04:18 +0000 Subject: [PATCH 09/15] correct launch descriptions --- .vscode/launch.json | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/.vscode/launch.json b/.vscode/launch.json index d478271..163d3f7 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -29,7 +29,7 @@ ] }, { - // Локальний debug стартера ДЗ 4. preLaunchTask збирає debug preset. + // debug ДЗ 4. preLaunchTask збирає debug preset. "name": "Debug homework_04: ugv_odometry", "type": "cppdbg", "request": "launch", @@ -53,7 +53,7 @@ ] }, { - // Локальний debug стартера ДЗ 6. preLaunchTask збирає debug preset. + // debug ДЗ 6. preLaunchTask збирає debug preset. "name": "Debug homework_06: ballistics", "type": "cppdbg", "request": "launch", @@ -77,9 +77,10 @@ ] }, { + // debug тестів ДЗ 6. preLaunchTask збирає debug preset. "type": "cppdbg", "request": "launch", - "name": "Debug homework_06/ballistics_tests", + "name": "Debug homework_06: ballistics_tests", "program": "${workspaceFolder}/build/debug/homework_06/ballistics_tests", "stopAtEntry": false, "cwd": "${workspaceFolder}", @@ -92,7 +93,7 @@ { "description": "Увімкнути pretty-printing для STL типів у GDB.", "text": "-enable-pretty-printing", - "ignoreFailures": true + "ignoreFailures": false } ] } From 1d9daf3913a280510c805402d851b72df38fc2d5 Mon Sep 17 00:00:00 2001 From: mandelbroo Date: Mon, 18 May 2026 15:13:58 +0000 Subject: [PATCH 10/15] create cmake test data dir variable --- homework_06/CMakeLists.txt | 3 +++ 1 file changed, 3 insertions(+) diff --git a/homework_06/CMakeLists.txt b/homework_06/CMakeLists.txt index a5184f2..45cc0dc 100644 --- a/homework_06/CMakeLists.txt +++ b/homework_06/CMakeLists.txt @@ -33,6 +33,9 @@ if(NOT CMAKE_CROSSCOMPILING) add_executable(ballistics_tests tests/ballistics_tests.cpp) + target_compile_definitions(ballistics_tests PRIVATE + TEST_DATA_DIR="${CMAKE_SOURCE_DIR}/homework_06/data") + target_link_libraries(ballistics_tests PRIVATE ballistics GTest::gtest_main) gtest_discover_tests(ballistics_tests DISCOVERY_MODE PRE_TEST) From 764e2d195985ad75857001b4f93a426b088d7fef Mon Sep 17 00:00:00 2001 From: mandelbroo Date: Mon, 18 May 2026 15:16:06 +0000 Subject: [PATCH 11/15] move rest of the code to domain logic; add higher level test --- homework_06/include/ballistics.hpp | 32 ++++++-- homework_06/src/ballistics.cpp | 103 ++++++++++++++++++++++++- homework_06/src/main.cpp | 96 +---------------------- homework_06/tests/ballistics_tests.cpp | 57 ++++++++++---- 4 files changed, 175 insertions(+), 113 deletions(-) diff --git a/homework_06/include/ballistics.hpp b/homework_06/include/ballistics.hpp index d84cad2..1e91d88 100644 --- a/homework_06/include/ballistics.hpp +++ b/homework_06/include/ballistics.hpp @@ -16,11 +16,10 @@ struct Ammo { struct DroneConfig { Coord startPos; // початкова позиція (x, y) Ammo ammo; - float altitude; // висота - float initialDir; // початковий напрямок (рад) - float attackSpeed; // швидкість атаки (м/с) - float accelPath; // шлях розгону (м) - // char ammoName[32]; // обрані боєприпаси + float altitude; // висота + float initialDir; // початковий напрямок (рад) + float attackSpeed; // швидкість атаки (м/с) + float accelPath; // шлях розгону (м) float arrayTimeStep; // крок часу масиву цілей float simTimeStep; // крок симуляції float hitRadius; // радіус влучення @@ -28,9 +27,30 @@ struct DroneConfig { float turnThreshold; // поріг повороту (рад) }; +struct BallisticsInput { + float droneX; + float droneY; + float droneZ; + float targetX; + float targetY; + float attackSpeed; + float accelerationPath; + std::string ammoName; +}; +struct DropSolution { + float fireX; + float fireY; + bool isManoeuvrePerformed = false; + float manouvreX; + float manouvreY; +}; + float calcAmmoFallTime(const Ammo& ammo, const float& attackSpeed, const float& droneHeight); float calcDistance(const Coord& a, const Coord& b); Coord calcFireCoordinates(const float& horizontalDistance, const float& distanceToTarget, const float& xd, const float& yd, const float& targetX, const float& targetY); float calcHorizontalDistance(const float& fallTime, DroneConfig& drone, const Coord& targetPosition); bool isManoeuvreNeeded(const float& horizontalDistance, const float& accelerationPath, const float& distanceToTarget); -void processManouvre(DroneConfig& drone, const Coord& targetPosition); \ No newline at end of file +void processManouvre(DroneConfig& drone, const Coord& targetPosition); +BallisticsInput parseInputFile(const std::string& filePath); +DropSolution computeDropSolution(const BallisticsInput& input); +void writeOutputFile(const std::string& filePath, const DropSolution& dropSolution); diff --git a/homework_06/src/ballistics.cpp b/homework_06/src/ballistics.cpp index 2daf232..8c18931 100644 --- a/homework_06/src/ballistics.cpp +++ b/homework_06/src/ballistics.cpp @@ -2,11 +2,23 @@ #include "ballistics.hpp" #include +#include +#include +#include #include using namespace std; const float GRAVITY_ACCEL = 9.81f; +const int AMMO_TYPES_COUNT = 5; + +const Ammo ARSENAL[AMMO_TYPES_COUNT] = { + {.name = "VOG-17", .mass = 0.35f, .drag = 0.07f, .lift = 0.0f}, + {.name = "M67", .mass = 0.6f, .drag = 0.1f, .lift = 0.0f}, + {.name = "RKG-3", .mass = 1.2f, .drag = 0.1f, .lift = 0.0f}, + {.name = "GLIDING-VOG", .mass = 0.45f, .drag = 0.1f, .lift = 1.0f}, + {.name = "GLIDING-RKG", .mass = 1.4f, .drag = 0.1f, .lift = 1.0f}, +}; float calcAmmoFallTime(const Ammo& ammo, const float& attackSpeed, const float& droneHeight) { /* @@ -128,4 +140,93 @@ void processManouvre(DroneConfig& drone, const Coord& targetPosition) { drone.startPos.x = targetPosition.x - (targetPosition.x - drone.startPos.x) * (h + drone.accelPath) / distanceToTarget; drone.startPos.y = targetPosition.y - (targetPosition.y - drone.startPos.y) * (h + drone.accelPath) / distanceToTarget; } -} \ No newline at end of file +} + +BallisticsInput parseInputFile(const string& filePath) { + ifstream inputFile(filePath); + + if (!inputFile.is_open()) { + cerr << "Error: Could not open the file:" << filePath << endl; + exit(1); + } + + BallisticsInput input; + + inputFile >> input.droneX >> input.droneY >> input.droneZ >> input.targetX >> input.targetY >> input.attackSpeed >> input.accelerationPath >> input.ammoName; + + inputFile.close(); + + return input; +} + +DropSolution computeDropSolution(const BallisticsInput& input) { + DroneConfig drone = {{input.droneX, input.droneY, input.droneZ}, {}, input.attackSpeed, input.accelerationPath}; + + for (int i = 0; i < AMMO_TYPES_COUNT; i++) { + if (input.ammoName == ARSENAL[i].name) { + drone.ammo = ARSENAL[i]; + break; + } + } + + cout << "AMMO TYPE: " << drone.ammo.name << endl; + + float fallTime = calcAmmoFallTime(drone.ammo, drone.attackSpeed, drone.startPos.z); + + cout << "Fall time: " << fallTime << "s" << endl; + + Coord targetPosition = {input.targetX, input.targetY, 0.}; + + float horizontalDistance = calcHorizontalDistance(fallTime, drone, targetPosition); + + cout << "Horizontal distance: " << horizontalDistance << "m" << endl; + + float distanceToTarget = calcDistance(drone.startPos, targetPosition); + + cout << "Distance to target: " << distanceToTarget << "m" << endl; + + DropSolution dropSolution{}; + + if (isManoeuvreNeeded(horizontalDistance, drone.accelPath, distanceToTarget)) { + cout << endl + << "Manoeuvre is needed!" << endl; + + dropSolution.isManoeuvrePerformed = true; + dropSolution.manouvreX = drone.startPos.x; + dropSolution.manouvreY = drone.startPos.y; + + processManouvre(drone, targetPosition); + horizontalDistance = calcHorizontalDistance(calcAmmoFallTime(drone.ammo, drone.attackSpeed, drone.startPos.z), drone, targetPosition); + distanceToTarget = calcDistance(drone.startPos, targetPosition); + + cout << "Drone moves to new x: " << drone.startPos.x << " y: " << drone.startPos.y << endl + << endl; + } + + Coord fireCoordinates = calcFireCoordinates(horizontalDistance, distanceToTarget, drone.startPos.x, drone.startPos.y, targetPosition.x, targetPosition.y); + + cout << endl + << "Fire x: " << fireCoordinates.x << " y: " << fireCoordinates.y << endl; + + dropSolution.fireX = fireCoordinates.x; + dropSolution.fireY = fireCoordinates.y; + + return dropSolution; +} + +void writeOutputFile(const string& filePath, const DropSolution& dropSolution) { + ofstream outputFile(filePath); + + if (!outputFile.is_open()) { + cerr << "Error: Could not open the file for writing:" << filePath << endl; + exit(1); + } + + if (dropSolution.isManoeuvrePerformed) { + outputFile << dropSolution.manouvreX << " " << dropSolution.manouvreY << " "; + } + + outputFile << dropSolution.fireX << " " << dropSolution.fireY; + + outputFile.close(); +} diff --git a/homework_06/src/main.cpp b/homework_06/src/main.cpp index bda2edb..71922cb 100644 --- a/homework_06/src/main.cpp +++ b/homework_06/src/main.cpp @@ -1,24 +1,9 @@ -#define _USE_MATH_DEFINES -#include -#include -#include #include -#include #include "ballistics.hpp" using namespace std; -const int AMMO_TYPES_COUNT = 5; - -const Ammo ARSENAL[AMMO_TYPES_COUNT] = { - {.name = "VOG-17", .mass = 0.35f, .drag = 0.07f, .lift = 0.0f}, - {.name = "M67", .mass = 0.6f, .drag = 0.1f, .lift = 0.0f}, - {.name = "RKG-3", .mass = 1.2f, .drag = 0.1f, .lift = 0.0f}, - {.name = "GLIDING-VOG", .mass = 0.45f, .drag = 0.1f, .lift = 1.0f}, - {.name = "GLIDING-RKG", .mass = 1.4f, .drag = 0.1f, .lift = 1.0f}, -}; - int main(int argc, char** argv) { if (argc != 2) { cerr << "Usage: ballistics_cli " << endl; @@ -26,85 +11,10 @@ int main(int argc, char** argv) { } const string INPUT_FILE = argv[1]; - const float GRAVITY_ACCEL = 9.81f; - - // READ FILE AND INIT ------------------------------------------------------- - std::ifstream inputFile(INPUT_FILE); - - if (!inputFile.is_open()) { - std::cerr << "Error: Could not open the file:" << INPUT_FILE << std::endl; - return 1; - } - - float xd, yd, zd, targetX, targetY, attackSpeed, accelerationPath; - std::string ammoName; - - inputFile >> xd >> yd >> zd >> targetX >> targetY >> attackSpeed >> accelerationPath >> ammoName; - - float drag = 0.0f; - float mass = 0.0f; - float lift = 0.0f; - - DroneConfig drone = {{xd, yd, zd}, {}, attackSpeed, accelerationPath}; - - for (int i = 0; i < AMMO_TYPES_COUNT; i++) { - if (ammoName == ARSENAL[i].name) { - drone.ammo = ARSENAL[i]; - break; - } - } - - cout << "AMMO TYPE: " << ammoName << endl; - - float t = calcAmmoFallTime(drone.ammo, attackSpeed, zd); - - cout << "Fall time: " << t << "s" << endl; - - float horizontalDistance = calcHorizontalDistance(t, drone, {targetX, targetY}); - - float distance = calcDistance({xd, yd}, {targetX, targetY}); - cout << "Distance to target: " << distance << "m" << endl; - - // IS MANOEUVRE NEEDED? ----------------------------------------------------- - - cout << "Horizontal distance: " << horizontalDistance << "m" << endl; - - bool isManoeuvrePerformed = false; - float oldXd = xd; - float oldYd = yd; - - if (isManoeuvreNeeded(horizontalDistance, accelerationPath, distance)) { - cout << endl - << "Manoeuvre is needed!" << endl; - - processManouvre(drone, {targetX, targetY}); - - isManoeuvrePerformed = true; - - cout << "Drone moves to new x: " << drone.startPos.x << " y: " << drone.startPos.y << endl - << endl; - } - - Coord fireCoordinates = calcFireCoordinates(horizontalDistance, distance, oldXd, oldYd, targetX, targetY); - - cout << endl - << "Fire x: " << fireCoordinates.x << " y: " << fireCoordinates.y << endl; - - // WRITE RESULTS INTO FILE -------------------------------------------------- - - std::ofstream outputFile("output.txt"); - - if (!outputFile.is_open()) { - cout << "Error. Cannot open output file."; - return 1; - } - - if (isManoeuvrePerformed) { - outputFile << drone.startPos.x << " " << drone.startPos.y << " "; - } + BallisticsInput input = parseInputFile(INPUT_FILE); - outputFile << fireCoordinates.x << " " << fireCoordinates.y; + DropSolution dropSolution = computeDropSolution(input); - cout << endl; + writeOutputFile("output.txt", dropSolution); } \ No newline at end of file diff --git a/homework_06/tests/ballistics_tests.cpp b/homework_06/tests/ballistics_tests.cpp index 400b7b6..8423b53 100644 --- a/homework_06/tests/ballistics_tests.cpp +++ b/homework_06/tests/ballistics_tests.cpp @@ -1,32 +1,48 @@ #include + #include #include "ballistics.hpp" +const Ammo TEST_AMMO = { + .name = "VOG-17", + .mass = 0.35f, + .drag = 0.07f, + .lift = 0.0f}; + +TEST(BallisticsTests, TestComputesKnownDropPoint) { + BallisticsInput input = parseInputFile(TEST_DATA_DIR "/sample_vog17.txt"); + + const DropSolution solution = computeDropSolution(input); + + EXPECT_NEAR(solution.fireX, 173.759, 0.01); + EXPECT_NEAR(solution.fireY, 173.759, 0.01); +} + TEST(BallisticsTests, TestCalcFallTime) { - DroneConfig drone; + float attackSpeed = 50.0f; + float droneHeight = 100.0f; - drone.ammo.mass = 1.0f; - drone.ammo.drag = 0.5f; - drone.startPos = {0.0f, 0.0f, 100.0f}; - - float fallTime = calcAmmoFallTime(drone.ammo, drone.attackSpeed, drone.startPos.z); + float fallTime = calcAmmoFallTime(TEST_AMMO, attackSpeed, droneHeight); - EXPECT_NEAR(std::round(fallTime * 1000.0f) / 1000.0f, 4.515f, 0.001f); + EXPECT_NEAR(fallTime, 5.74f, 0.01f); } TEST(BallisticsTests, TestCalcHorizontalDistance) { - DroneConfig drone; + DroneConfig drone = { + .startPos = {10.0f, 10.0f, 100.0f}, + .ammo = TEST_AMMO, + .attackSpeed = 50.0f}; - drone.ammo.mass = 1.0f; - drone.ammo.drag = 0.5f; - drone.startPos = {0.0f, 0.0f, 100.0f}; drone.attackSpeed = 50.0f; float fallTime = calcAmmoFallTime(drone.ammo, drone.attackSpeed, drone.startPos.z); - Coord targetPosition = {100.0f, 0.0f}; + + Coord targetPosition = {100.0f, 100.0f, 0.}; + float horizontalDistance = calcHorizontalDistance(fallTime, drone, targetPosition); - EXPECT_NEAR(horizontalDistance, 224.5f, 0.1f); + + EXPECT_NEAR(horizontalDistance, 185.5f, 0.06f); } TEST(BallisticsTests, TestCalcDistance) { @@ -34,5 +50,20 @@ TEST(BallisticsTests, TestCalcDistance) { Coord b = {3.0f, 4.0f}; float distance = calcDistance(a, b); + EXPECT_NEAR(distance, 5.0f, 0.001f); } + +TEST(BallisticsTests, TestCalcFireCoordinates) { + float horizontalDistance = 185.5f; + float distanceToTarget = 190.0f; + float xd = 10.0f; + float yd = 10.0f; + float targetX = 100.0f; + float targetY = 0.0f; + + Coord fireCoords = calcFireCoordinates(horizontalDistance, distanceToTarget, xd, yd, targetX, targetY); + + EXPECT_NEAR(fireCoords.x, 12.1f, 0.1f); + EXPECT_NEAR(fireCoords.y, 9.7f, 0.1f); +} \ No newline at end of file From 269bd0691cd5b123940b41ee614ac9b9b15b99b2 Mon Sep 17 00:00:00 2001 From: mandelbroo Date: Mon, 18 May 2026 18:03:14 +0000 Subject: [PATCH 12/15] handle unknown ammo --- homework_06/data/unknown_ammo.txt | 1 + homework_06/include/ballistics.hpp | 1 + homework_06/src/ballistics.cpp | 13 +++++++++++-- homework_06/src/main.cpp | 5 +++++ homework_06/tests/ballistics_tests.cpp | 8 ++++++++ 5 files changed, 26 insertions(+), 2 deletions(-) create mode 100644 homework_06/data/unknown_ammo.txt diff --git a/homework_06/data/unknown_ammo.txt b/homework_06/data/unknown_ammo.txt new file mode 100644 index 0000000..dd8481d --- /dev/null +++ b/homework_06/data/unknown_ammo.txt @@ -0,0 +1 @@ +100 100 100 200 200 10 10 F1 \ No newline at end of file diff --git a/homework_06/include/ballistics.hpp b/homework_06/include/ballistics.hpp index 1e91d88..65152c6 100644 --- a/homework_06/include/ballistics.hpp +++ b/homework_06/include/ballistics.hpp @@ -43,6 +43,7 @@ struct DropSolution { bool isManoeuvrePerformed = false; float manouvreX; float manouvreY; + std::string errorMessage; }; float calcAmmoFallTime(const Ammo& ammo, const float& attackSpeed, const float& droneHeight); diff --git a/homework_06/src/ballistics.cpp b/homework_06/src/ballistics.cpp index 8c18931..8a0a596 100644 --- a/homework_06/src/ballistics.cpp +++ b/homework_06/src/ballistics.cpp @@ -160,7 +160,12 @@ BallisticsInput parseInputFile(const string& filePath) { } DropSolution computeDropSolution(const BallisticsInput& input) { - DroneConfig drone = {{input.droneX, input.droneY, input.droneZ}, {}, input.attackSpeed, input.accelerationPath}; + DroneConfig drone = { + .startPos = {.x = input.droneX, + .y = input.droneY, + .z = input.droneZ}, + .attackSpeed = input.attackSpeed, + .accelPath = input.accelerationPath}; for (int i = 0; i < AMMO_TYPES_COUNT; i++) { if (input.ammoName == ARSENAL[i].name) { @@ -169,13 +174,17 @@ DropSolution computeDropSolution(const BallisticsInput& input) { } } + if (drone.ammo.name == "Unknown") { + return {.errorMessage = "Unknown ammo type: " + input.ammoName}; + } + cout << "AMMO TYPE: " << drone.ammo.name << endl; float fallTime = calcAmmoFallTime(drone.ammo, drone.attackSpeed, drone.startPos.z); cout << "Fall time: " << fallTime << "s" << endl; - Coord targetPosition = {input.targetX, input.targetY, 0.}; + Coord targetPosition = {input.targetX, input.targetY}; float horizontalDistance = calcHorizontalDistance(fallTime, drone, targetPosition); diff --git a/homework_06/src/main.cpp b/homework_06/src/main.cpp index 71922cb..9433e3b 100644 --- a/homework_06/src/main.cpp +++ b/homework_06/src/main.cpp @@ -15,6 +15,11 @@ int main(int argc, char** argv) { BallisticsInput input = parseInputFile(INPUT_FILE); DropSolution dropSolution = computeDropSolution(input); + + if (!dropSolution.errorMessage.empty()) { + cerr << "Error: " << dropSolution.errorMessage << endl; + return 1; + } writeOutputFile("output.txt", dropSolution); } \ No newline at end of file diff --git a/homework_06/tests/ballistics_tests.cpp b/homework_06/tests/ballistics_tests.cpp index 8423b53..502a763 100644 --- a/homework_06/tests/ballistics_tests.cpp +++ b/homework_06/tests/ballistics_tests.cpp @@ -19,6 +19,14 @@ TEST(BallisticsTests, TestComputesKnownDropPoint) { EXPECT_NEAR(solution.fireY, 173.759, 0.01); } +TEST(BallisticsTests, TestComputesUnknownAmmo) { + BallisticsInput input = parseInputFile(TEST_DATA_DIR "/unknown_ammo.txt"); + + const DropSolution solution = computeDropSolution(input); + + EXPECT_EQ(solution.errorMessage, "Unknown ammo type: F1"); +} + TEST(BallisticsTests, TestCalcFallTime) { float attackSpeed = 50.0f; float droneHeight = 100.0f; From 9dcf4da3aafd44a2543afed8923c53793dc3406e Mon Sep 17 00:00:00 2001 From: mandelbroo Date: Mon, 18 May 2026 18:17:06 +0000 Subject: [PATCH 13/15] clang-format --- homework_06/include/ballistics.hpp | 52 +++++++----- homework_06/src/ballistics.cpp | 113 +++++++++++++++++-------- homework_06/src/main.cpp | 4 +- homework_06/tests/ballistics_tests.cpp | 21 +++-- 4 files changed, 120 insertions(+), 70 deletions(-) diff --git a/homework_06/include/ballistics.hpp b/homework_06/include/ballistics.hpp index 65152c6..5e96103 100644 --- a/homework_06/include/ballistics.hpp +++ b/homework_06/include/ballistics.hpp @@ -8,23 +8,23 @@ struct Coord { struct Ammo { std::string name = "Unknown"; - float mass; // маса (кг) - float drag; // коефіцієнт опору - float lift; // коефіцієнт підйому + float mass; // маса (кг) + float drag; // коефіцієнт опору + float lift; // коефіцієнт підйому }; struct DroneConfig { - Coord startPos; // початкова позиція (x, y) + Coord startPos; // початкова позиція (x, y) Ammo ammo; - float altitude; // висота - float initialDir; // початковий напрямок (рад) - float attackSpeed; // швидкість атаки (м/с) - float accelPath; // шлях розгону (м) - float arrayTimeStep; // крок часу масиву цілей - float simTimeStep; // крок симуляції - float hitRadius; // радіус влучення - float angularSpeed; // кутова швидкість (рад/с) - float turnThreshold; // поріг повороту (рад) + float altitude; // висота + float initialDir; // початковий напрямок (рад) + float attackSpeed; // швидкість атаки (м/с) + float accelPath; // шлях розгону (м) + float arrayTimeStep; // крок часу масиву цілей + float simTimeStep; // крок симуляції + float hitRadius; // радіус влучення + float angularSpeed; // кутова швидкість (рад/с) + float turnThreshold; // поріг повороту (рад) }; struct BallisticsInput { @@ -46,12 +46,20 @@ struct DropSolution { std::string errorMessage; }; -float calcAmmoFallTime(const Ammo& ammo, const float& attackSpeed, const float& droneHeight); -float calcDistance(const Coord& a, const Coord& b); -Coord calcFireCoordinates(const float& horizontalDistance, const float& distanceToTarget, const float& xd, const float& yd, const float& targetX, const float& targetY); -float calcHorizontalDistance(const float& fallTime, DroneConfig& drone, const Coord& targetPosition); -bool isManoeuvreNeeded(const float& horizontalDistance, const float& accelerationPath, const float& distanceToTarget); -void processManouvre(DroneConfig& drone, const Coord& targetPosition); -BallisticsInput parseInputFile(const std::string& filePath); -DropSolution computeDropSolution(const BallisticsInput& input); -void writeOutputFile(const std::string& filePath, const DropSolution& dropSolution); +float calcAmmoFallTime(const Ammo &ammo, const float &attackSpeed, + const float &droneHeight); +float calcDistance(const Coord &a, const Coord &b); +Coord calcFireCoordinates(const float &horizontalDistance, + const float &distanceToTarget, const float &xd, + const float &yd, const float &targetX, + const float &targetY); +float calcHorizontalDistance(const float &fallTime, DroneConfig &drone, + const Coord &targetPosition); +bool isManoeuvreNeeded(const float &horizontalDistance, + const float &accelerationPath, + const float &distanceToTarget); +void processManouvre(DroneConfig &drone, const Coord &targetPosition); +BallisticsInput parseInputFile(const std::string &filePath); +DropSolution computeDropSolution(const BallisticsInput &input); +void writeOutputFile(const std::string &filePath, + const DropSolution &dropSolution); diff --git a/homework_06/src/ballistics.cpp b/homework_06/src/ballistics.cpp index 8a0a596..411ee89 100644 --- a/homework_06/src/ballistics.cpp +++ b/homework_06/src/ballistics.cpp @@ -20,7 +20,8 @@ const Ammo ARSENAL[AMMO_TYPES_COUNT] = { {.name = "GLIDING-RKG", .mass = 1.4f, .drag = 0.1f, .lift = 1.0f}, }; -float calcAmmoFallTime(const Ammo& ammo, const float& attackSpeed, const float& droneHeight) { +float calcAmmoFallTime(const Ammo &ammo, const float &attackSpeed, + const float &droneHeight) { /* V₀ — швидкість атаки дрона Z₀ — висота дрона (zd) @@ -33,8 +34,10 @@ float calcAmmoFallTime(const Ammo& ammo, const float& attackSpeed, const float& float drag² = ammo.drag * ammo.drag; float mass² = ammo.mass * ammo.mass; - float a = ammo.drag * GRAVITY_ACCEL * ammo.mass - 2.0f * drag² * ammo.lift * attackSpeed; - float b = -3.0f * GRAVITY_ACCEL * mass² + 3.0f * ammo.drag * ammo.lift * ammo.mass * attackSpeed; + float a = ammo.drag * GRAVITY_ACCEL * ammo.mass - + 2.0f * drag² * ammo.lift * attackSpeed; + float b = -3.0f * GRAVITY_ACCEL * mass² + + 3.0f * ammo.drag * ammo.lift * ammo.mass * attackSpeed; float c = 6.0f * mass² * droneHeight; /* @@ -50,14 +53,18 @@ float calcAmmoFallTime(const Ammo& ammo, const float& attackSpeed, const float& float b³ = b² * b; float p = -b² / static_cast(3.0 * a²); - float q = 2.0 * b³ / static_cast(27.0 * a³) + c / static_cast(a); - float phi = acos(3.0 * q / static_cast(2.0 * p) * sqrt(-3.0 / static_cast(p))); - float t = 2.0 * sqrt(-p / 3.0) * cos((phi + 4.0 * M_PI) / 3.0) - b / static_cast(3.0 * a); + float q = + 2.0 * b³ / static_cast(27.0 * a³) + c / static_cast(a); + float phi = acos(3.0 * q / static_cast(2.0 * p) * + sqrt(-3.0 / static_cast(p))); + float t = 2.0 * sqrt(-p / 3.0) * cos((phi + 4.0 * M_PI) / 3.0) - + b / static_cast(3.0 * a); return t; } -float calcHorizontalDistance(const float& fallTime, DroneConfig& drone, const Coord& targetPosition) { +float calcHorizontalDistance(const float &fallTime, DroneConfig &drone, + const Coord &targetPosition) { /* h = V₀t − t²d·V₀/(2m) + t³(6d·g·l·m − 6d²(l²-1)·V₀)/(36m²) + + t⁴ (−6d²g·l·(1+l²+l⁴)m + 3d³l²(1+l²)V₀ + 6d³l⁴(1+l²)V₀) / (36(1+l²)²m³) @@ -84,10 +91,24 @@ float calcHorizontalDistance(const float& fallTime, DroneConfig& drone, const Co float mass⁴ = mass³ * mass; float h = - attackSpeed * t - t² * drag * attackSpeed / (2.0 * mass) + t³ * (6.0 * drag * GRAVITY_ACCEL * lift * mass - 6.0 * drag² * (lift² - 1.0) * attackSpeed) / (36.0 * mass²) + t⁴ * (-6.0 * drag² * GRAVITY_ACCEL * lift * (1.0 + lift² + lift⁴) * mass + 3.0 * drag³ * lift² * (1.0 + lift²) * attackSpeed + 6.0 * drag³ * lift⁴ * (1.0 + lift²) * attackSpeed) / (36.0 * pow(1.0 + lift², 2) * mass³) + t⁵ * (3.0 * drag³ * GRAVITY_ACCEL * lift³ * mass - 3.0 * drag⁴ * lift² * (1.0 + lift²) * attackSpeed) / (36.0 * (1.0 + lift²) * mass⁴); + attackSpeed * t - t² * drag * attackSpeed / (2.0 * mass) + + t³ * + (6.0 * drag * GRAVITY_ACCEL * lift * mass - + 6.0 * drag² * (lift² - 1.0) * attackSpeed) / + (36.0 * mass²) + + t⁴ * + (-6.0 * drag² * GRAVITY_ACCEL * lift * (1.0 + lift² + lift⁴) * mass + + 3.0 * drag³ * lift² * (1.0 + lift²) * attackSpeed + + 6.0 * drag³ * lift⁴ * (1.0 + lift²) * attackSpeed) / + (36.0 * pow(1.0 + lift², 2) * mass³) + + t⁵ * + (3.0 * drag³ * GRAVITY_ACCEL * lift³ * mass - + 3.0 * drag⁴ * lift² * (1.0 + lift²) * attackSpeed) / + (36.0 * (1.0 + lift²) * mass⁴); // CHECK IF DRONE IS PRECISELY OVER THE TARGET SO DISTANCE IS NOT ZERO ------ - if (targetPosition.x == drone.startPos.x && targetPosition.y == drone.startPos.y) { + if (targetPosition.x == drone.startPos.x && + targetPosition.y == drone.startPos.y) { drone.startPos.x = drone.startPos.x - 0.0001f; drone.startPos.y = drone.startPos.y - 0.0001f; } @@ -95,7 +116,7 @@ float calcHorizontalDistance(const float& fallTime, DroneConfig& drone, const Co return h; } -float calcDistance(const Coord& a, const Coord& b) { +float calcDistance(const Coord &a, const Coord &b) { /* D = √( (targetX − xd)² + (targetY − yd)² ) */ @@ -105,44 +126,57 @@ float calcDistance(const Coord& a, const Coord& b) { return distance; } -Coord calcFireCoordinates(const float& horizontalDistance, const float& distanceToTarget, const float& xd, const float& yd, const float& targetX, const float& targetY) { +Coord calcFireCoordinates(const float &horizontalDistance, + const float &distanceToTarget, const float &xd, + const float &yd, const float &targetX, + const float &targetY) { /* ratio = (D − h) / D fireX = xd + (targetX − xd) · ratio fireY = yd + (targetY − yd) · ratio */ - float ratio = (distanceToTarget - horizontalDistance) / static_cast(distanceToTarget); + float ratio = (distanceToTarget - horizontalDistance) / + static_cast(distanceToTarget); float fireX = xd + (targetX - xd) * ratio; float fireY = yd + (targetY - yd) * ratio; return {fireX, fireY}; } -bool isManoeuvreNeeded(const float& horizontalDistance, const float& accelerationPath, const float& distanceToTarget) { +bool isManoeuvreNeeded(const float &horizontalDistance, + const float &accelerationPath, + const float &distanceToTarget) { /* - Manoeuvre is needed if (accelerationPath + horizontalDistance) > distanceToTarget + Manoeuvre is needed if (accelerationPath + horizontalDistance) > + distanceToTarget */ return (accelerationPath + horizontalDistance) > distanceToTarget; } -void processManouvre(DroneConfig& drone, const Coord& targetPosition) { +void processManouvre(DroneConfig &drone, const Coord &targetPosition) { /* xd' = targetX − (targetX − xd) · (h + accelerationPath) / D yd' = targetY − (targetY − yd) · (h + accelerationPath) / D */ - float h = calcHorizontalDistance(calcAmmoFallTime(drone.ammo, drone.attackSpeed, drone.startPos.z), drone, targetPosition); + float h = calcHorizontalDistance( + calcAmmoFallTime(drone.ammo, drone.attackSpeed, drone.startPos.z), drone, + targetPosition); float distanceToTarget = calcDistance(drone.startPos, targetPosition); if (isManoeuvreNeeded(h, drone.accelPath, distanceToTarget)) { - drone.startPos.x = targetPosition.x - (targetPosition.x - drone.startPos.x) * (h + drone.accelPath) / distanceToTarget; - drone.startPos.y = targetPosition.y - (targetPosition.y - drone.startPos.y) * (h + drone.accelPath) / distanceToTarget; + drone.startPos.x = + targetPosition.x - (targetPosition.x - drone.startPos.x) * + (h + drone.accelPath) / distanceToTarget; + drone.startPos.y = + targetPosition.y - (targetPosition.y - drone.startPos.y) * + (h + drone.accelPath) / distanceToTarget; } } -BallisticsInput parseInputFile(const string& filePath) { +BallisticsInput parseInputFile(const string &filePath) { ifstream inputFile(filePath); if (!inputFile.is_open()) { @@ -152,18 +186,18 @@ BallisticsInput parseInputFile(const string& filePath) { BallisticsInput input; - inputFile >> input.droneX >> input.droneY >> input.droneZ >> input.targetX >> input.targetY >> input.attackSpeed >> input.accelerationPath >> input.ammoName; + inputFile >> input.droneX >> input.droneY >> input.droneZ >> input.targetX >> + input.targetY >> input.attackSpeed >> input.accelerationPath >> + input.ammoName; inputFile.close(); return input; } -DropSolution computeDropSolution(const BallisticsInput& input) { +DropSolution computeDropSolution(const BallisticsInput &input) { DroneConfig drone = { - .startPos = {.x = input.droneX, - .y = input.droneY, - .z = input.droneZ}, + .startPos = {.x = input.droneX, .y = input.droneY, .z = input.droneZ}, .attackSpeed = input.attackSpeed, .accelPath = input.accelerationPath}; @@ -180,13 +214,15 @@ DropSolution computeDropSolution(const BallisticsInput& input) { cout << "AMMO TYPE: " << drone.ammo.name << endl; - float fallTime = calcAmmoFallTime(drone.ammo, drone.attackSpeed, drone.startPos.z); + float fallTime = + calcAmmoFallTime(drone.ammo, drone.attackSpeed, drone.startPos.z); cout << "Fall time: " << fallTime << "s" << endl; Coord targetPosition = {input.targetX, input.targetY}; - float horizontalDistance = calcHorizontalDistance(fallTime, drone, targetPosition); + float horizontalDistance = + calcHorizontalDistance(fallTime, drone, targetPosition); cout << "Horizontal distance: " << horizontalDistance << "m" << endl; @@ -196,26 +232,32 @@ DropSolution computeDropSolution(const BallisticsInput& input) { DropSolution dropSolution{}; - if (isManoeuvreNeeded(horizontalDistance, drone.accelPath, distanceToTarget)) { - cout << endl - << "Manoeuvre is needed!" << endl; + if (isManoeuvreNeeded(horizontalDistance, drone.accelPath, + distanceToTarget)) { + cout << endl << "Manoeuvre is needed!" << endl; dropSolution.isManoeuvrePerformed = true; dropSolution.manouvreX = drone.startPos.x; dropSolution.manouvreY = drone.startPos.y; processManouvre(drone, targetPosition); - horizontalDistance = calcHorizontalDistance(calcAmmoFallTime(drone.ammo, drone.attackSpeed, drone.startPos.z), drone, targetPosition); + horizontalDistance = calcHorizontalDistance( + calcAmmoFallTime(drone.ammo, drone.attackSpeed, drone.startPos.z), + drone, targetPosition); distanceToTarget = calcDistance(drone.startPos, targetPosition); - cout << "Drone moves to new x: " << drone.startPos.x << " y: " << drone.startPos.y << endl + cout << "Drone moves to new x: " << drone.startPos.x + << " y: " << drone.startPos.y << endl << endl; } - Coord fireCoordinates = calcFireCoordinates(horizontalDistance, distanceToTarget, drone.startPos.x, drone.startPos.y, targetPosition.x, targetPosition.y); + Coord fireCoordinates = calcFireCoordinates( + horizontalDistance, distanceToTarget, drone.startPos.x, drone.startPos.y, + targetPosition.x, targetPosition.y); cout << endl - << "Fire x: " << fireCoordinates.x << " y: " << fireCoordinates.y << endl; + << "Fire x: " << fireCoordinates.x << " y: " << fireCoordinates.y + << endl; dropSolution.fireX = fireCoordinates.x; dropSolution.fireY = fireCoordinates.y; @@ -223,7 +265,7 @@ DropSolution computeDropSolution(const BallisticsInput& input) { return dropSolution; } -void writeOutputFile(const string& filePath, const DropSolution& dropSolution) { +void writeOutputFile(const string &filePath, const DropSolution &dropSolution) { ofstream outputFile(filePath); if (!outputFile.is_open()) { @@ -232,7 +274,8 @@ void writeOutputFile(const string& filePath, const DropSolution& dropSolution) { } if (dropSolution.isManoeuvrePerformed) { - outputFile << dropSolution.manouvreX << " " << dropSolution.manouvreY << " "; + outputFile << dropSolution.manouvreX << " " << dropSolution.manouvreY + << " "; } outputFile << dropSolution.fireX << " " << dropSolution.fireY; diff --git a/homework_06/src/main.cpp b/homework_06/src/main.cpp index 9433e3b..bac05c2 100644 --- a/homework_06/src/main.cpp +++ b/homework_06/src/main.cpp @@ -4,7 +4,7 @@ using namespace std; -int main(int argc, char** argv) { +int main(int argc, char **argv) { if (argc != 2) { cerr << "Usage: ballistics_cli " << endl; return 1; @@ -15,7 +15,7 @@ int main(int argc, char** argv) { BallisticsInput input = parseInputFile(INPUT_FILE); DropSolution dropSolution = computeDropSolution(input); - + if (!dropSolution.errorMessage.empty()) { cerr << "Error: " << dropSolution.errorMessage << endl; return 1; diff --git a/homework_06/tests/ballistics_tests.cpp b/homework_06/tests/ballistics_tests.cpp index 502a763..dc449c8 100644 --- a/homework_06/tests/ballistics_tests.cpp +++ b/homework_06/tests/ballistics_tests.cpp @@ -5,10 +5,7 @@ #include "ballistics.hpp" const Ammo TEST_AMMO = { - .name = "VOG-17", - .mass = 0.35f, - .drag = 0.07f, - .lift = 0.0f}; + .name = "VOG-17", .mass = 0.35f, .drag = 0.07f, .lift = 0.0f}; TEST(BallisticsTests, TestComputesKnownDropPoint) { BallisticsInput input = parseInputFile(TEST_DATA_DIR "/sample_vog17.txt"); @@ -37,18 +34,19 @@ TEST(BallisticsTests, TestCalcFallTime) { } TEST(BallisticsTests, TestCalcHorizontalDistance) { - DroneConfig drone = { - .startPos = {10.0f, 10.0f, 100.0f}, - .ammo = TEST_AMMO, - .attackSpeed = 50.0f}; + DroneConfig drone = {.startPos = {10.0f, 10.0f, 100.0f}, + .ammo = TEST_AMMO, + .attackSpeed = 50.0f}; drone.attackSpeed = 50.0f; - float fallTime = calcAmmoFallTime(drone.ammo, drone.attackSpeed, drone.startPos.z); + float fallTime = + calcAmmoFallTime(drone.ammo, drone.attackSpeed, drone.startPos.z); Coord targetPosition = {100.0f, 100.0f, 0.}; - float horizontalDistance = calcHorizontalDistance(fallTime, drone, targetPosition); + float horizontalDistance = + calcHorizontalDistance(fallTime, drone, targetPosition); EXPECT_NEAR(horizontalDistance, 185.5f, 0.06f); } @@ -70,7 +68,8 @@ TEST(BallisticsTests, TestCalcFireCoordinates) { float targetX = 100.0f; float targetY = 0.0f; - Coord fireCoords = calcFireCoordinates(horizontalDistance, distanceToTarget, xd, yd, targetX, targetY); + Coord fireCoords = calcFireCoordinates(horizontalDistance, distanceToTarget, + xd, yd, targetX, targetY); EXPECT_NEAR(fireCoords.x, 12.1f, 0.1f); EXPECT_NEAR(fireCoords.y, 9.7f, 0.1f); From a437d2836e2f8a4f8750a6b30fc1f4c9eae9e04a Mon Sep 17 00:00:00 2001 From: mandelbroo Date: Mon, 18 May 2026 18:29:33 +0000 Subject: [PATCH 14/15] cmake-format --- homework_06/CMakeLists.txt | 18 +++++++----------- 1 file changed, 7 insertions(+), 11 deletions(-) diff --git a/homework_06/CMakeLists.txt b/homework_06/CMakeLists.txt index 45cc0dc..8412669 100644 --- a/homework_06/CMakeLists.txt +++ b/homework_06/CMakeLists.txt @@ -1,5 +1,5 @@ cmake_minimum_required(VERSION 3.20) -project(telemetry_check CXX) +project(telemetry_check CXX) set(CMAKE_CXX_STANDARD 20) set(CMAKE_CXX_STANDARD_REQUIRED ON) @@ -20,21 +20,17 @@ if(NOT CMAKE_CROSSCOMPILING) include(GoogleTest) FetchContent_Declare( - - googletest - - URL https://github.com/google/googletest/archive/refs/tags/v1.14.0.zip - - DOWNLOAD_EXTRACT_TIMESTAMP TRUE - - ) + googletest + URL https://github.com/google/googletest/archive/refs/tags/v1.14.0.zip + DOWNLOAD_EXTRACT_TIMESTAMP TRUE) FetchContent_MakeAvailable(googletest) add_executable(ballistics_tests tests/ballistics_tests.cpp) - target_compile_definitions(ballistics_tests PRIVATE - TEST_DATA_DIR="${CMAKE_SOURCE_DIR}/homework_06/data") + target_compile_definitions( + ballistics_tests + PRIVATE TEST_DATA_DIR="${CMAKE_SOURCE_DIR}/homework_06/data") target_link_libraries(ballistics_tests PRIVATE ballistics GTest::gtest_main) From 56552c4ff7afd79d82476820b4e1bbe0cb6b8a79 Mon Sep 17 00:00:00 2001 From: mandelbroo Date: Mon, 18 May 2026 18:35:20 +0000 Subject: [PATCH 15/15] okay clang-tidy. It was too cool for ya --- homework_06/src/ballistics.cpp | 74 +++++++++++++++++----------------- 1 file changed, 37 insertions(+), 37 deletions(-) diff --git a/homework_06/src/ballistics.cpp b/homework_06/src/ballistics.cpp index 411ee89..f1ad452 100644 --- a/homework_06/src/ballistics.cpp +++ b/homework_06/src/ballistics.cpp @@ -32,13 +32,13 @@ float calcAmmoFallTime(const Ammo &ammo, const float &attackSpeed, c = 6m²·Z₀ */ - float drag² = ammo.drag * ammo.drag; - float mass² = ammo.mass * ammo.mass; + float drag2 = ammo.drag * ammo.drag; + float mass2 = ammo.mass * ammo.mass; float a = ammo.drag * GRAVITY_ACCEL * ammo.mass - - 2.0f * drag² * ammo.lift * attackSpeed; - float b = -3.0f * GRAVITY_ACCEL * mass² + + 2.0f * drag2 * ammo.lift * attackSpeed; + float b = -3.0f * GRAVITY_ACCEL * mass2 + 3.0f * ammo.drag * ammo.lift * ammo.mass * attackSpeed; - float c = 6.0f * mass² * droneHeight; + float c = 6.0f * mass2 * droneHeight; /* p = − b² / (3a²) @@ -47,14 +47,14 @@ float calcAmmoFallTime(const Ammo &ammo, const float &attackSpeed, t = 2√(−p/3) · cos( (φ + 4π) / 3 ) − b / (3a) */ - float a² = a * a; - float a³ = a² * a; - float b² = b * b; - float b³ = b² * b; + float a2 = a * a; + float a3 = a2 * a; + float b2 = b * b; + float b3 = b2 * b; - float p = -b² / static_cast(3.0 * a²); + float p = -b2 / static_cast(3.0 * a2); float q = - 2.0 * b³ / static_cast(27.0 * a³) + c / static_cast(a); + 2.0 * b3 / static_cast(27.0 * a3) + c / static_cast(a); float phi = acos(3.0 * q / static_cast(2.0 * p) * sqrt(-3.0 / static_cast(p))); float t = 2.0 * sqrt(-p / 3.0) * cos((phi + 4.0 * M_PI) / 3.0) - @@ -76,35 +76,35 @@ float calcHorizontalDistance(const float &fallTime, DroneConfig &drone, float mass = drone.ammo.mass; float attackSpeed = drone.attackSpeed; - float t² = t * t; - float t³ = t² * t; - float t⁴ = t³ * t; - float t⁵ = t⁴ * t; - float drag² = drag * drag; - float drag³ = drag² * drag; - float drag⁴ = drag³ * drag; - float lift² = lift * lift; - float lift³ = lift² * lift; - float lift⁴ = lift³ * lift; - float mass² = mass * mass; - float mass³ = mass² * mass; - float mass⁴ = mass³ * mass; + float t2 = t * t; + float t3 = t2 * t; + float t4 = t3 * t; + float t5 = t4 * t; + float drag2 = drag * drag; + float drag3 = drag2 * drag; + float drag4 = drag3 * drag; + float lift2 = lift * lift; + float lift3 = lift2 * lift; + float lift4 = lift3 * lift; + float mass2 = mass * mass; + float mass3 = mass2 * mass; + float mass4 = mass3 * mass; float h = - attackSpeed * t - t² * drag * attackSpeed / (2.0 * mass) + - t³ * + attackSpeed * t - t2 * drag * attackSpeed / (2.0 * mass) + + t3 * (6.0 * drag * GRAVITY_ACCEL * lift * mass - - 6.0 * drag² * (lift² - 1.0) * attackSpeed) / - (36.0 * mass²) + - t⁴ * - (-6.0 * drag² * GRAVITY_ACCEL * lift * (1.0 + lift² + lift⁴) * mass + - 3.0 * drag³ * lift² * (1.0 + lift²) * attackSpeed + - 6.0 * drag³ * lift⁴ * (1.0 + lift²) * attackSpeed) / - (36.0 * pow(1.0 + lift², 2) * mass³) + - t⁵ * - (3.0 * drag³ * GRAVITY_ACCEL * lift³ * mass - - 3.0 * drag⁴ * lift² * (1.0 + lift²) * attackSpeed) / - (36.0 * (1.0 + lift²) * mass⁴); + 6.0 * drag2 * (lift2 - 1.0) * attackSpeed) / + (36.0 * mass2) + + t4 * + (-6.0 * drag2 * GRAVITY_ACCEL * lift * (1.0 + lift2 + lift4) * mass + + 3.0 * drag3 * lift2 * (1.0 + lift2) * attackSpeed + + 6.0 * drag3 * lift4 * (1.0 + lift2) * attackSpeed) / + (36.0 * pow(1.0 + lift2, 2) * mass3) + + t5 * + (3.0 * drag3 * GRAVITY_ACCEL * lift3 * mass - + 3.0 * drag4 * lift2 * (1.0 + lift2) * attackSpeed) / + (36.0 * (1.0 + lift2) * mass4); // CHECK IF DRONE IS PRECISELY OVER THE TARGET SO DISTANCE IS NOT ZERO ------ if (targetPosition.x == drone.startPos.x &&