diff --git a/3D Printed Parts/Enclosure Models/Base Layer - Power Side - Wall/Base Layer - Power Side - Wall.20241201-105709.FCBak b/3D Printed Parts/Enclosure Models/Base Layer - Power Side - Wall/Base Layer - Power Side - Wall.20241201-105709.FCBak deleted file mode 100644 index 64099e4..0000000 Binary files a/3D Printed Parts/Enclosure Models/Base Layer - Power Side - Wall/Base Layer - Power Side - Wall.20241201-105709.FCBak and /dev/null differ diff --git a/Software/LMSourceCode/ImageProcessing/ClosedSourceObjectFiles/gs_e6_response.cpp.o b/Software/LMSourceCode/ImageProcessing/ClosedSourceObjectFiles/gs_e6_response.cpp.o index a6e4a4f..8ebb699 100644 Binary files a/Software/LMSourceCode/ImageProcessing/ClosedSourceObjectFiles/gs_e6_response.cpp.o and b/Software/LMSourceCode/ImageProcessing/ClosedSourceObjectFiles/gs_e6_response.cpp.o differ diff --git a/Software/LMSourceCode/ImageProcessing/ball_image_proc.cpp b/Software/LMSourceCode/ImageProcessing/ball_image_proc.cpp index b6aefe4..97cc797 100644 --- a/Software/LMSourceCode/ImageProcessing/ball_image_proc.cpp +++ b/Software/LMSourceCode/ImageProcessing/ball_image_proc.cpp @@ -2468,8 +2468,8 @@ namespace golf_sim { // Re-center the ball's x and y position in the new, smaller picture // This will change the ball that was sent in - ball.set_x( (int)std::round(rInc + ball.measured_radius_pixels_)); - ball.set_y( (int)std::round(rInc + ball.measured_radius_pixels_)); + ball.set_x( (float)std::round(rInc + ball.measured_radius_pixels_)); + ball.set_y( (float)std::round(rInc + ball.measured_radius_pixels_)); cv::Point offset_sub_to_full; cv::Point offset_full_to_sub; @@ -2581,12 +2581,12 @@ namespace golf_sim { // Adjust relevant ball radius information accordingly local_ball1.measured_radius_pixels_ = local_ball1.measured_radius_pixels_ * ball1RadiusMultiplier; local_ball1.ball_circle_[2] = local_ball1.ball_circle_[2] * (float)ball1RadiusMultiplier; - local_ball1.set_x( (int)((double)local_ball1.x() * ball1RadiusMultiplier)); - local_ball1.set_y( (int)((double)local_ball1.y() * ball1RadiusMultiplier)); + local_ball1.set_x( (float)((double)local_ball1.x() * ball1RadiusMultiplier)); + local_ball1.set_y( (float)((double)local_ball1.y() * ball1RadiusMultiplier)); local_ball2.measured_radius_pixels_ = local_ball2.measured_radius_pixels_ * ball2RadiusMultiplier; local_ball2.ball_circle_[2] = local_ball2.ball_circle_[2] * (float)ball2RadiusMultiplier; - local_ball2.set_x( (int)((double)local_ball2.x() * ball2RadiusMultiplier)); - local_ball2.set_y( (int)((double)local_ball2.y() * ball2RadiusMultiplier)); + local_ball2.set_x( (float)((double)local_ball2.x() * ball2RadiusMultiplier)); + local_ball2.set_y( (float)((double)local_ball2.y() * ball2RadiusMultiplier)); std::vector < cv::Point > center1 = { cv::Point{(int)local_ball1.x(), (int)local_ball1.y()} }; @@ -2596,7 +2596,7 @@ namespace golf_sim { LoggingTools::DebugShowImage("Ball2 Image", ball_image2, center2); GS_LOG_TRACE_MSG(trace, "Updated (local) ball2 data: " + local_ball2.Format()); - int calibrated_binary_threshold = 0; + float calibrated_binary_threshold = 0; cv::Mat ball_image1DimpleEdges = ApplyGaborFilterToBall(ball_image1, local_ball1, calibrated_binary_threshold); // Suggest the same binary threshold between the images as a starting point for the second ball - they are probably similar cv::Mat ball_image2DimpleEdges = ApplyGaborFilterToBall(ball_image2, local_ball2, calibrated_binary_threshold, calibrated_binary_threshold); @@ -3112,7 +3112,7 @@ namespace golf_sim { return kernel; } - cv::Mat BallImageProc::ApplyGaborFilterToBall(const cv::Mat& image_gray, const GolfBall& ball, int & calibrated_binary_threshold, int prior_binary_threshold) { + cv::Mat BallImageProc::ApplyGaborFilterToBall(const cv::Mat& image_gray, const GolfBall& ball, float & calibrated_binary_threshold, float prior_binary_threshold) { // TBD - Not sure we will ever need the ball information? CV_Assert( (image_gray.type() == CV_8UC1) ); @@ -3177,19 +3177,19 @@ namespace golf_sim { if (ratheting_threshold_down) { if (kGaborMinWhitePercent - white_percent > 5) { - binary_threshold = (float)binary_threshold - 1.0; + binary_threshold = binary_threshold - 1.0F; } else { - binary_threshold = (float)binary_threshold - 0.5; + binary_threshold = binary_threshold - 0.5F; } GS_LOG_TRACE_MSG(trace, "Trying lower gabor binary_threshold setting of " + std::to_string(binary_threshold) + " for better balance."); } else { if (white_percent - kGaborMaxWhitePercent > 5) { - binary_threshold = (float)binary_threshold + 1.0; + binary_threshold = binary_threshold + 1.0F; } else { - binary_threshold = (float)binary_threshold + 0.5; + binary_threshold = binary_threshold + 0.5F; } GS_LOG_TRACE_MSG(trace, "Trying higher gabor binary_threshold setting of " + std::to_string(binary_threshold) + " for better balance."); } diff --git a/Software/LMSourceCode/ImageProcessing/ball_image_proc.h b/Software/LMSourceCode/ImageProcessing/ball_image_proc.h index 03cccf9..1e8d19e 100644 --- a/Software/LMSourceCode/ImageProcessing/ball_image_proc.h +++ b/Software/LMSourceCode/ImageProcessing/ball_image_proc.h @@ -343,7 +343,7 @@ class BallImageProc // If prior_binary_threshold < 0, then there is no prior threshold and a new one will be determined and returns // in the calibrated_binary_threshold variable. - static cv::Mat ApplyGaborFilterToBall(const cv::Mat& img, const GolfBall& ball, int& calibrated_binary_threshold, int prior_binary_threshold = -1); + static cv::Mat ApplyGaborFilterToBall(const cv::Mat& img, const GolfBall& ball, float& calibrated_binary_threshold, float prior_binary_threshold = -1); // Applies the gabor filter with the specified parameters and returns the final image and white percentage static cv::Mat ApplyTestGaborFilter(const cv::Mat& img_f32, diff --git a/Software/LMSourceCode/ImageProcessing/golf_ball.cpp b/Software/LMSourceCode/ImageProcessing/golf_ball.cpp index 9382617..56db0f2 100644 --- a/Software/LMSourceCode/ImageProcessing/golf_ball.cpp +++ b/Software/LMSourceCode/ImageProcessing/golf_ball.cpp @@ -225,8 +225,8 @@ void GolfBall::PrintBallFlightResults() const { void GolfBall::AverageBalls(const std::vector& ball_vector, GolfBall& averaged_ball) { double number_balls = (double)ball_vector.size(); - averaged_ball.set_x(0); - averaged_ball.set_y(0); + averaged_ball.set_x(0.0F); + averaged_ball.set_y(0.0F); averaged_ball.velocity_ = 0; averaged_ball.position_deltas_ball_perspective_ = { 0, 0, 0 }; averaged_ball.angles_ball_perspective_ = { 0,0 }; @@ -237,11 +237,11 @@ void GolfBall::AverageBalls(const std::vector& ball_vector, GolfBall& averaged_ball.velocity_ += b.velocity_ / number_balls; long x = averaged_ball.x(); - x += b.x() / number_balls; + x += (long)(b.x() / number_balls); averaged_ball.set_x(x); long y = averaged_ball.y(); - y += b.y() / number_balls; + y += (long)(b.y() / number_balls); averaged_ball.set_y(y); // NOTE - Not clear how often the position deltas should be averaged? diff --git a/Software/LMSourceCode/ImageProcessing/golf_ball.h b/Software/LMSourceCode/ImageProcessing/golf_ball.h index 346ce24..9bdb2a0 100644 --- a/Software/LMSourceCode/ImageProcessing/golf_ball.h +++ b/Software/LMSourceCode/ImageProcessing/golf_ball.h @@ -67,7 +67,11 @@ class GolfBall { long y() const { return y_; }; // In pixels in openCV coordinate system void set_x(long x) { x_ = x; ball_circle_[0] = (float)x; }; - void set_y(long y) { y_ = y; ball_circle_[1] = (float)y;}; + void set_y(long y) { y_ = y; ball_circle_[1] = (float)y; }; + + // Allows type-safe calls that use ball data to set ball data + void set_x(float x) { x_ = (long) x; ball_circle_[0] = x; }; + void set_y(float y) { y_ = (long) y; ball_circle_[1] = y; }; // The circle where the ball exists on the relevant image. Could (in theory?) be different from the // more definitive x and y and radius_at_calibration_pixels_! diff --git a/Software/LMSourceCode/ImageProcessing/golfsim_tomee_webapp/src/main/java/com/verdanttechs/jakarta/ee9/MonitorServlet.java b/Software/LMSourceCode/ImageProcessing/golfsim_tomee_webapp/src/main/java/com/verdanttechs/jakarta/ee9/MonitorServlet.java index fc96762..0b7942f 100644 --- a/Software/LMSourceCode/ImageProcessing/golfsim_tomee_webapp/src/main/java/com/verdanttechs/jakarta/ee9/MonitorServlet.java +++ b/Software/LMSourceCode/ImageProcessing/golfsim_tomee_webapp/src/main/java/com/verdanttechs/jakarta/ee9/MonitorServlet.java @@ -62,11 +62,12 @@ @WebServlet("/monitor") public class MonitorServlet extends HttpServlet { - + // NOTE - these should reflect types in gs_ipc_result public enum GsIPCResultType { kUnknown, kInitializing, kWaitingForBallToAppear, + kWaitingForSimulatorArmed, kPausingForBallStabilization, kMultipleBallsPresent, kBallPlacedAndReadyForHit, @@ -363,6 +364,7 @@ public String Format(HttpServletRequest request) { s += " Log Messages:\n" + log_messages_console_str; } + // Set the GUI top color to be white as a default. Special cases follow request.setAttribute("ball_ready_color", "\"item1 grid-item-text-center-white\""); // System.out.println("Checking for Ball placed"); @@ -373,6 +375,12 @@ public String Format(HttpServletRequest request) { request.setAttribute("ball_ready_color", "\"item1 grid-item-text-center-green\""); } + if (result_type_ == GsIPCResultType.kWaitingForSimulatorArmed) + { + System.out.println("kWaitingForSimulatorArmed - setting ball_ready_color to 'item1 grid-item-text-center-yellow'"); + request.setAttribute("ball_ready_color", "\"item1 grid-item-text-center-yellow\""); + } + // If the result is a hit, then include IMG images // Even if it's an error, we might still find the images (if they // exist) to be useful @@ -397,6 +405,7 @@ else if (result_type_ == GsIPCResultType.kError ) } else if (result_type_ == GsIPCResultType.kWaitingForBallToAppear ) { + // Make sure the user can see what the monitor is seeing, especially if the user may have placed the ball outside the ball search area request.setAttribute("images", "\"Ball"); } @@ -441,6 +450,11 @@ public String FormatResultType(GsIPCResultType t) { break; } + case kWaitingForSimulatorArmed: { + s = "Waiting for the simulator to be armed"; + break; + } + case kInitializing: { s = "Initializing launch monitor."; break; @@ -905,6 +919,7 @@ else if (message instanceof BytesMessage) { if (new_result.speed_mpers_ <= 0 && (new_result.result_type_ != GsIPCResultType.kBallPlacedAndReadyForHit && new_result.result_type_ != GsIPCResultType.kInitializing && + new_result.result_type_ != GsIPCResultType.kWaitingForSimulatorArmed && new_result.result_type_ != GsIPCResultType.kError )) { // Don't replace the current result, as the user may still be looking at it diff --git a/Software/LMSourceCode/ImageProcessing/golfsim_tomee_webapp/src/main/webapp/WEB-INF/gs_dashboard.jsp b/Software/LMSourceCode/ImageProcessing/golfsim_tomee_webapp/src/main/webapp/WEB-INF/gs_dashboard.jsp index 028c88c..373ba37 100644 --- a/Software/LMSourceCode/ImageProcessing/golfsim_tomee_webapp/src/main/webapp/WEB-INF/gs_dashboard.jsp +++ b/Software/LMSourceCode/ImageProcessing/golfsim_tomee_webapp/src/main/webapp/WEB-INF/gs_dashboard.jsp @@ -136,6 +136,18 @@ border-radius: 10px; } + .grid-item-text-center-yellow { + background-color: #FFFF00; + border: 2px solid rgb(0, 0, 0); + border-width: medium; + font-size: 30px; + text-align: center; + padding: 10px; + padding-left: 20px; + font-size: 40px; + border-radius: 10px; + } + .grid-item-empty-row { background-color: rgba(255, 255, 255, 0.8); text-align: center; diff --git a/Software/LMSourceCode/ImageProcessing/gs_events.cpp b/Software/LMSourceCode/ImageProcessing/gs_events.cpp index b2bf983..76eb6b4 100644 --- a/Software/LMSourceCode/ImageProcessing/gs_events.cpp +++ b/Software/LMSourceCode/ImageProcessing/gs_events.cpp @@ -50,6 +50,8 @@ namespace golf_sim { PossibleEvent possible_event; GolfSimEvent::BeginWaitingForBallPlaced* beginWaitingForBallPlaced = nullptr; + GolfSimEvent::SimulatorIsArmed* simulatorIsArmed = nullptr; + GolfSimEvent::BeginWaitingForSimulatorArmed* beginWaitingForSimulatorArmed = nullptr; GolfSimEvent::EventLoopTick* eventLoopTick = nullptr; GolfSimEvent::BallStabilized* ballStabilized = nullptr; GolfSimEvent::BallHit* ballHit = nullptr; @@ -66,7 +68,13 @@ namespace golf_sim { GolfSimEvent::ArmCamera2MessageReceived* armCamera2MessageReceived = nullptr; - if ((beginWaitingForBallPlaced = dynamic_cast(event))) { + if ((beginWaitingForSimulatorArmed = dynamic_cast(event))) { + possible_event = *beginWaitingForSimulatorArmed; + } + else if ((simulatorIsArmed = dynamic_cast(event))) { + possible_event = *simulatorIsArmed; + } + else if ((beginWaitingForBallPlaced = dynamic_cast(event))) { possible_event = *beginWaitingForBallPlaced; } else if ((eventLoopTick = dynamic_cast(event))) { diff --git a/Software/LMSourceCode/ImageProcessing/gs_events.h b/Software/LMSourceCode/ImageProcessing/gs_events.h index 16104ba..1888765 100644 --- a/Software/LMSourceCode/ImageProcessing/gs_events.h +++ b/Software/LMSourceCode/ImageProcessing/gs_events.h @@ -5,8 +5,9 @@ */ // This module defines the events, event queue, and associated processing for the various -// types of events that occur in the launch monitor system. +// types of events that occur within the launch monitor system. // These events largely drive the transitions in the system's finite state machine +// These events are separate from IPC events that deal with external messaging. #pragma once @@ -44,6 +45,15 @@ namespace golf_sim { virtual std::string Format() override { return "EventLoopTick"; }; }; + class BeginWatchingForBallHit : public GolfSimEventBase + { + public: + BeginWatchingForBallHit() {}; + ~BeginWatchingForBallHit() {}; + + virtual std::string Format() override { return "BeginWatchingForBallHit"; }; + }; + class BeginWaitingForBallPlaced : public GolfSimEventBase { public: @@ -96,14 +106,22 @@ namespace golf_sim { GsIPCControlMsgType message_type_; }; + class BeginWaitingForSimulatorArmed : public GolfSimEventBase + { + public: + BeginWaitingForSimulatorArmed() {}; + ~BeginWaitingForSimulatorArmed() {}; - class BeginWatchingForBallHit : public GolfSimEventBase + virtual std::string Format() override { return "BeginWaitingForSimulatorArmed"; }; + }; + + class SimulatorIsArmed : public GolfSimEventBase { public: - BeginWatchingForBallHit() {}; - ~BeginWatchingForBallHit() {}; + SimulatorIsArmed() {}; + ~SimulatorIsArmed() {}; - virtual std::string Format() override { return "BeginWatchingForBallHit"; }; + virtual std::string Format() override { return "SimulatorIsArmed"; }; }; class CheckForCam2ImageReceived : public GolfSimEventBase @@ -115,6 +133,7 @@ namespace golf_sim { virtual std::string Format() override { return "CheckForCam2ImageReceived"; }; }; + // TBD - this error event isn't really handled properly yet class FoundMultipleBalls : public GolfSimEventBase { public: @@ -207,6 +226,8 @@ namespace golf_sim { } using PossibleEvent = std::variant< GolfSimEvent::EventLoopTick, + GolfSimEvent::BeginWaitingForSimulatorArmed, + GolfSimEvent::SimulatorIsArmed, GolfSimEvent::BeginWaitingForBallPlaced, GolfSimEvent::CheckForBallStable, GolfSimEvent::BallStabilized, diff --git a/Software/LMSourceCode/ImageProcessing/gs_fsm.cpp b/Software/LMSourceCode/ImageProcessing/gs_fsm.cpp index 55f300f..86ecfc8 100644 --- a/Software/LMSourceCode/ImageProcessing/gs_fsm.cpp +++ b/Software/LMSourceCode/ImageProcessing/gs_fsm.cpp @@ -121,15 +121,24 @@ namespace golf_sim { GolfSimState onEvent(const state::InitializingCamera1System& initializing, const GolfSimEvent::Restart& restart) { - GS_LOG_MSG(debug, "GolfSim state transition: Initializing - Received Restart - Next state WaitingForBall. "); - - GolfSimEventElement beginWaitingForBallPlacedEvent{ new GolfSimEvent::BeginWaitingForBallPlaced{ } }; - GolfSimEventQueue::QueueEvent(beginWaitingForBallPlacedEvent); + GS_LOG_MSG(debug, "GolfSim state transition: Initializing - Received Restart - Next state WaitingForSimArmed or WaitingForBall. "); // Let the monitor interface know what's happening GsUISystem::SendIPCStatusMessage(GsIPCResultType::kInitializing); - return state::WaitingForBall{ std::chrono::steady_clock::now() }; + // If we're already armed, just start waiting for a ball to appear. + if (GsSimInterface::GetAllSystemsArmed()) { + GolfSimEventElement beginWaitingForBallPlacedEvent{ new GolfSimEvent::BeginWaitingForBallPlaced{ } }; + GolfSimEventQueue::QueueEvent(beginWaitingForBallPlacedEvent); + + return state::WaitingForBall{ std::chrono::steady_clock::now() }; + } + + GolfSimEventElement beginWaitingForSimulatorArmedEvent{ new GolfSimEvent::BeginWaitingForSimulatorArmed{ } }; + GolfSimEventQueue::QueueEvent(beginWaitingForSimulatorArmedEvent); + + return state::WaitingForSimulatorArmed{ std::chrono::steady_clock::now() }; + } @@ -301,6 +310,7 @@ namespace golf_sim { // to figure out what it needs to display GsUISystem::ClearWebserverImages(); + // TBD - Probably remove. Pre-image subtraction was an idea that never panned out as well as we'd hoped. if (GolfSimCamera::kUsePreImageSubtraction) { return state::WaitingForCamera2PreImage{ std::chrono::steady_clock::now(), ball, img }; } @@ -334,6 +344,50 @@ namespace golf_sim { camera2PreImageReceived.GetBallFlightPreImage() }; } + + + /*********** WaitingForSimulatorArmed ************/ + + GolfSimState onEvent(const state::WaitingForSimulatorArmed& waitingForSimulatorArmed, + const GolfSimEvent::BeginWaitingForSimulatorArmed& beginWaitingForSimulatorArmed) { + GS_LOG_MSG(debug, "GolfSim state transition: WaitingForSimulatorArmed - Received SimulatorArmed."); + + // Let the monitor interface know what's happening so it can alert the user + GsUISystem::SendIPCStatusMessage(GsIPCResultType::kWaitingForSimulatorArmed); + + // Wait a moment so that we're not spinning too much + sleep(1); + + if (GsSimInterface::GetAllSystemsArmed()) { + GolfSimEventElement beginWaitingForBallPlacedEvent{ new GolfSimEvent::BeginWaitingForBallPlaced{ } }; + GolfSimEventQueue::QueueEvent(beginWaitingForBallPlacedEvent); + + return state::WaitingForBall{ std::chrono::steady_clock::now() }; + } + + // Otherwise, keep in waiting state + GolfSimEventElement nextBeginWaitingForSimulatorArmed{ new GolfSimEvent::BeginWaitingForSimulatorArmed{ } }; + GolfSimEventQueue::QueueEvent(nextBeginWaitingForSimulatorArmed); + + return state::WaitingForSimulatorArmed{ std::chrono::steady_clock::now() }; + } + + // TBD - Not certain we are going to use this state + GolfSimState onEvent(const state::WaitingForSimulatorArmed& waitingForSimulatorArmed, + const GolfSimEvent::SimulatorIsArmed& simulatorArmed) { + GS_LOG_MSG(debug, "GolfSim state transition: WaitingForSimulatorArmed - Received SimulatorArmed."); + + // Let the monitor interface know what's happening so it can alert the user + GsUISystem::SendIPCStatusMessage(GsIPCResultType::kWaitingForSimulatorArmed); + + // The simulator is now armed. + // The following will cause the waitingForBall state to begin watching for the ball + GolfSimEventElement beginWaitingForSimulatorArmed{ new GolfSimEvent::BeginWaitingForSimulatorArmed{ } }; + GolfSimEventQueue::QueueEvent(beginWaitingForSimulatorArmed); + + return state::WaitingForBall{ std::chrono::steady_clock::now() }; + } + /*********** WaitingForBallHit ************/ @@ -624,6 +678,9 @@ namespace golf_sim { [](const state::WaitingForCamera2PreImage& waitingForCamera2PreImage) { GS_LOG_TRACE_MSG(trace, "WaitingForCamera2PreImage."); }, + [](const state::WaitingForSimulatorArmed& waitingForSimulatorArmed) { + GS_LOG_TRACE_MSG(trace, "WaitingForSimulatorArmed."); + }, [](const state::InitializingCamera2System& initializingCamera1System) { GS_LOG_TRACE_MSG(trace, "InitializingCamera2System."); }, diff --git a/Software/LMSourceCode/ImageProcessing/gs_fsm.h b/Software/LMSourceCode/ImageProcessing/gs_fsm.h index b23b046..cc7d7dd 100644 --- a/Software/LMSourceCode/ImageProcessing/gs_fsm.h +++ b/Software/LMSourceCode/ImageProcessing/gs_fsm.h @@ -36,6 +36,10 @@ namespace golf_sim { std::chrono::steady_clock::time_point startTime_; }; + struct WaitingForSimulatorArmed { + std::chrono::steady_clock::time_point startTime_; + }; + struct WaitingForBallStabilization { std::chrono::steady_clock::time_point lastBallAcquisitionTime_; std::chrono::steady_clock::time_point startTime_; @@ -85,6 +89,7 @@ namespace golf_sim { // Must contain each of the above states using GolfSimState = std::variant< state::InitializingCamera1System, state::Exiting, + state::WaitingForSimulatorArmed, state::WaitingForBall, state::WaitingForBallStabilization, state::WaitingForBallHit, diff --git a/Software/LMSourceCode/ImageProcessing/gs_ipc_result.h b/Software/LMSourceCode/ImageProcessing/gs_ipc_result.h index 420066c..dd7469f 100644 --- a/Software/LMSourceCode/ImageProcessing/gs_ipc_result.h +++ b/Software/LMSourceCode/ImageProcessing/gs_ipc_result.h @@ -28,13 +28,14 @@ namespace golf_sim { enum class GsIPCResultType { kUnknown = 0, kInitializing = 1, kWaitingForBallToAppear = 2, - kPausingForBallStabilization = 3, - kMultipleBallsPresent = 4, - kBallPlacedAndReadyForHit = 5, - kHit = 6, - kError = 7, - kCalibrationResults = 8, - kControlMessage = 9}; + kWaitingForSimulatorArmed = 3, + kPausingForBallStabilization = 4, + kMultipleBallsPresent = 5, + kBallPlacedAndReadyForHit = 6, + kHit = 7, + kError = 8, + kCalibrationResults = 9, + kControlMessage = 10}; class GsIPCResult { diff --git a/Software/LMSourceCode/ImageProcessing/gs_ipc_system.cpp b/Software/LMSourceCode/ImageProcessing/gs_ipc_system.cpp index a0cf839..e42ed3e 100644 --- a/Software/LMSourceCode/ImageProcessing/gs_ipc_system.cpp +++ b/Software/LMSourceCode/ImageProcessing/gs_ipc_system.cpp @@ -230,6 +230,8 @@ namespace golf_sim { bool GolfSimIpcSystem::DispatchResultsMessage(const GolfSimIPCMessage& message) { + // The LM system doesn't currently do anything if it gets a results message. + // These messages are mostly destined for the PiTrac GUI GS_LOG_TRACE_MSG(trace, "DispatchResultsMessage Received Ipc Message."); return true; diff --git a/Software/LMSourceCode/ImageProcessing/gs_results.cpp b/Software/LMSourceCode/ImageProcessing/gs_results.cpp index 02803aa..d4a9378 100644 --- a/Software/LMSourceCode/ImageProcessing/gs_results.cpp +++ b/Software/LMSourceCode/ImageProcessing/gs_results.cpp @@ -31,6 +31,9 @@ namespace golf_sim { // TBD - Not sure club type should be set here, // but this is a reasonable default for now club_type_ = GolfSimClubs::GetCurrentClubType(); + + // TBD - Even though this is a constructor, it might be a reasonable + // place to calculate the Carry yardarge. } float GsResults::GetSpinAxis() const { @@ -52,6 +55,8 @@ namespace golf_sim { s += "Spin Axis (deg.): " + std::to_string(GetSpinAxis()) + "\n"; s += "Club Type: (1D 3P)" + std::to_string(club_type_) + "\n"; + // TBD - Add internal carry value. + return s; } diff --git a/Software/LMSourceCode/ImageProcessing/gs_sim_interface.cpp b/Software/LMSourceCode/ImageProcessing/gs_sim_interface.cpp index 60e9a9d..00e67a3 100644 --- a/Software/LMSourceCode/ImageProcessing/gs_sim_interface.cpp +++ b/Software/LMSourceCode/ImageProcessing/gs_sim_interface.cpp @@ -93,6 +93,29 @@ namespace golf_sim { } + bool GsSimInterface::SimIsConnected() { + + GS_LOG_TRACE_MSG(trace, "GsSimInterface::SimIsConnected()"); + + if (!sims_initialized_) { + // If we're not even initialized, there can't be any connected golf sims. + return false; + } + + bool sim_is_connected = false; + +#ifdef __unix__ // Ignore in Windows environment + + for (auto interface : interfaces_) { + if (interface != nullptr) { + sim_is_connected = true; + continue; + } + } +#endif + return sim_is_connected; + } + void GsSimInterface::DeInitializeSims() { GS_LOG_TRACE_MSG(trace, "GsSimInterface::DeInitializeSims()"); @@ -126,6 +149,11 @@ namespace golf_sim { bool GsSimInterface::GetSimSystemArmed() { boost::lock_guard lock(sim_arming_mutex_); + if (!SimIsConnected()) { + // If no sims, then consider the system armed as a reasonable fallback behavior + return true; + } + return sim_system_is_armed_; } diff --git a/Software/LMSourceCode/ImageProcessing/gs_sim_interface.h b/Software/LMSourceCode/ImageProcessing/gs_sim_interface.h index 542d888..6c96957 100644 --- a/Software/LMSourceCode/ImageProcessing/gs_sim_interface.h +++ b/Software/LMSourceCode/ImageProcessing/gs_sim_interface.h @@ -36,6 +36,9 @@ namespace golf_sim { // De-initialize and destory and sim interfaces that are configured static void DeInitializeSims(); + // Returns true if at least one golf sim is connected to the system. + static bool SimIsConnected(); + // To be called from the launch monitor static bool SendResultsToGolfSims(const GsResults& results); @@ -63,6 +66,8 @@ namespace golf_sim { // Returns the number of bytes written virtual int SendSimMessage(const std::string& message); + // Deals with whether or not ALL of the connected simulators are armed + // (ready to take a shot). Some sims just return true. virtual void SetSimSystemArmed(const bool is_armed); virtual bool GetSimSystemArmed(); diff --git a/Software/LMSourceCode/ImageProcessing/gs_ui_system.cpp b/Software/LMSourceCode/ImageProcessing/gs_ui_system.cpp index 5d185d5..6cc3198 100644 --- a/Software/LMSourceCode/ImageProcessing/gs_ui_system.cpp +++ b/Software/LMSourceCode/ImageProcessing/gs_ui_system.cpp @@ -79,6 +79,10 @@ namespace golf_sim { results.message_ = "Ball teed. Confirming ball is stable."; break; + case GsIPCResultType::kWaitingForSimulatorArmed: + results.message_ = "Waiting on the simulator to be armed (ready to accept a shot)."; + break; + case GsIPCResultType::kMultipleBallsPresent: results.message_ = "Multiple balls present."; break; diff --git a/Software/LMSourceCode/ImageProcessing/lm_main.cpp b/Software/LMSourceCode/ImageProcessing/lm_main.cpp index f047f9e..5377220 100644 --- a/Software/LMSourceCode/ImageProcessing/lm_main.cpp +++ b/Software/LMSourceCode/ImageProcessing/lm_main.cpp @@ -52,11 +52,7 @@ const double kLocationTolerancePercent = 10; // The result files we create will be prefixed with this static constexpr std::string_view TEST_IMAGE_PREFIX = "TEST_RESULT_GetBall_"; -#ifdef __unix__ -std::string kBaseTestDir = "/mnt/VerdantShare/dev/GolfSim/LM/Images/"; -#else -std::string kBaseTestDir = "V:\\Images\\"; // "D:\\GolfSim\\LM\\Images\\"; -#endif +std::string kBaseTestDir = "Will be set from the .json configuration file"; BallImageProc *get_image_processor() { @@ -237,7 +233,14 @@ bool testProjection() { GolfBall ball1, ball2; - const std::string kBaseTestDir = "D:\\GolfSim\\C++Code\\GolfSim\\ImageProcessing\\"; + std::string kBaseTestDir = "D:\\GolfSim\\C++Code\\GolfSim\\ImageProcessing\\"; + +#ifdef __unix__ + GolfSimConfiguration::SetConstant("gs_config.logging.kLinuxBaseImageLoggingDir", kBaseTestDir); +#else + GolfSimConfiguration::SetConstant("gs_config.logging.kPCBaseImageLoggingDir", kBaseTestDir); +#endif + const std::string k0_DegreeBallFileName_00 = kBaseTestDir + "test_ball_masked_0_deg_dulled.png"; const std::string k45_DegreeBallFileName_00 = kBaseTestDir + "test_ball_masked_45_deg_dulled.png"; @@ -1113,9 +1116,9 @@ bool TestExternalSimMessage() { GsGSProResults test_result(ball); test_result.speed_mph_ = 99; test_result.vla_deg_ = 23.4F; - test_result.hla_deg_ = 1.23; - test_result.back_spin_rpm_ = 3456.0; - test_result.side_spin_rpm_ = -5.678; + test_result.hla_deg_ = 1.23F; + test_result.back_spin_rpm_ = 3456; + test_result.side_spin_rpm_ = -567; #ifdef __unix__ @@ -1139,7 +1142,7 @@ bool TestExternalSimMessage() { } test_result.speed_mph_ = 55; - test_result.vla_deg_ = 12.3; + test_result.vla_deg_ = 12.3F; shot_number++; @@ -1167,7 +1170,7 @@ bool TestBallDeltaCalculations() { ball1.set_x(ball1.ball_circle_[0]); ball1.ball_circle_[1] = 424; ball1.set_y(ball1.ball_circle_[1]); - ball1.ball_circle_[2] = 50.41; + ball1.ball_circle_[2] = 50; ball1.measured_radius_pixels_ = ball1.ball_circle_[2]; ball1.distance_to_z_plane_from_lens_ = 0.761; ball1.distances_ortho_camera_perspective_[0] = -0.514;