-
-
Notifications
You must be signed in to change notification settings - Fork 56
Added option to join OpenGolfSim, Camera Saturation changes #172
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
Sunnyland
wants to merge
1
commit into
PiTracLM:main
Choose a base branch
from
Sunnyland:sunnyland-mildura
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from all commits
Commits
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. remove |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,132 @@ | ||
| {% extends "base.html" %} | ||
|
|
||
| {% block title %}PiTrac Launch Monitor{% endblock %} | ||
|
|
||
| {% block extra_css %} | ||
| <link rel="stylesheet" href="/static/css/dashboard.css"> | ||
| {% endblock %} | ||
|
|
||
| {% block content %} | ||
| <div class="ball-ready-indicator" id="ball-ready-indicator"> | ||
| <div class="ball-status-icon" id="ball-status-icon"> | ||
| <div class="ball-icon"></div> | ||
| </div> | ||
| <div class="ball-status-text"> | ||
| <div class="ball-status-title" id="ball-status-title">System Status</div> | ||
| <div class="ball-status-message" id="ball-status-message">Initializing...</div> | ||
|
|
||
| <!-- OpenGolfSim connection badge --> | ||
| <div class="sim-badge" id="ogs-badge" | ||
| data-connected="{{ 'true' if ogs_connected else 'false' }}"> | ||
| OpenGolfSim: | ||
| <span class="sim-badge__state"> | ||
| {{ 'Connected' if ogs_connected else 'Disconnected' }} | ||
| </span> | ||
| </div> | ||
|
|
||
| </div> | ||
| </div> | ||
|
|
||
| <div class="metrics-grid"> | ||
| <div class="metric-card"> | ||
| <div class="metric-header">Ball Speed</div> | ||
| <div class="metric-value"> | ||
| <span id="speed">{{ shot.speed }}</span> | ||
| <span class="metric-unit">mph</span> | ||
| </div> | ||
| </div> | ||
|
|
||
| <div class="metric-card"> | ||
| <div class="metric-header">Carry Distance</div> | ||
| <div class="metric-value"> | ||
| <span id="carry">{{ shot.carry }}</span> | ||
| <span class="metric-unit">yds</span> | ||
| </div> | ||
| </div> | ||
|
|
||
| <div class="metric-card"> | ||
| <div class="metric-header">Launch Angle</div> | ||
| <div class="metric-value"> | ||
| <span id="launch_angle">{{ shot.launch_angle }}</span> | ||
| <span class="metric-unit">°</span> | ||
| </div> | ||
| </div> | ||
|
|
||
| <div class="metric-card"> | ||
| <div class="metric-header">Side Angle</div> | ||
| <div class="metric-value"> | ||
| <span id="side_angle">{{ shot.side_angle }}</span> | ||
| <span class="metric-unit">°</span> | ||
| </div> | ||
| </div> | ||
|
|
||
| <div class="metric-card"> | ||
| <div class="metric-header">Back Spin</div> | ||
| <div class="metric-value"> | ||
| <span id="back_spin">{{ shot.back_spin }}</span> | ||
| <span class="metric-unit">rpm</span> | ||
| </div> | ||
| </div> | ||
|
|
||
| <div class="metric-card"> | ||
| <div class="metric-header">Side Spin</div> | ||
| <div class="metric-value"> | ||
| <span id="side_spin">{{ shot.side_spin }}</span> | ||
| <span class="metric-unit">rpm</span> | ||
| </div> | ||
| </div> | ||
| </div> | ||
|
|
||
| <!-- <div class="message-section"> | ||
| <div class="result-type" id="result_type">{{ shot.result_type }}</div> | ||
| <div class="message" id="message">{{ shot.message }}</div> | ||
| </div> --> | ||
|
|
||
| <!-- NEW: OpenGolfSim Controls --> | ||
| {% if ogs_connected %} | ||
| <div class="ogs-controls" id="ogs-controls"> | ||
| <h2>OpenGolfSim Controls</h2> | ||
|
|
||
| <div class="ogs-controls-grid"> | ||
| <!-- Aim pad (supports hold) --> | ||
| <button class="ogs-btn hold" data-command="up">▲</button> | ||
| <div></div> | ||
| <button class="ogs-btn hold" data-command="club-up">Club +</button> | ||
|
|
||
| <button class="ogs-btn hold" data-command="left">◀</button> | ||
| <button class="ogs-btn press" data-command="drop">Drop</button> | ||
| <button class="ogs-btn hold" data-command="right">▶</button> | ||
|
|
||
| <button class="ogs-btn hold" data-command="down">▼</button> | ||
| <button class="ogs-btn press" data-command="mulligan">Mulligan</button> | ||
| <button class="ogs-btn press" data-command="rehit">Rehit</button> | ||
|
|
||
| <div></div> | ||
| <button class="ogs-btn press" data-command="club-down">Club -</button> | ||
| <div></div> | ||
| </div> | ||
|
|
||
| <div class="ogs-controls-hint"> | ||
| Tip: arrows support hold (down/up). Most other buttons use press. | ||
| </div> | ||
| </div> | ||
| {% endif %} | ||
|
|
||
| <div class="image-gallery"> | ||
| <h2>Shot Images</h2> | ||
| <div class="image-grid" id="image-grid"> | ||
| {% for image in shot.images %} | ||
| <img src="/images/{{ image }}" alt="Shot {{ loop.index }}" class="shot-image" loading="lazy"> | ||
| {% endfor %} | ||
| </div> | ||
| </div> | ||
|
|
||
| <div class="controls"> | ||
| <button class="btn btn-primary" onclick="resetShot()">Reset</button> | ||
| <button class="btn btn-secondary" onclick="location.reload()">Refresh</button> | ||
| </div> | ||
| {% endblock %} | ||
|
|
||
| {% block extra_scripts %} | ||
| <script src="/static/js/dashboard.js"></script> | ||
| {% endblock %} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -177,7 +177,8 @@ bool GolfSimConfiguration::ReadValues() { | |
| SetConstant("gs_config.cameras.kCamera1HighFPSGain", LibCameraInterface::kCamera1HighFPSGain); | ||
| SetConstant("gs_config.cameras.kCamera1Contrast", LibCameraInterface::kCamera1Contrast); | ||
| SetConstant("gs_config.cameras.kCamera2Gain", LibCameraInterface::kCamera2Gain); | ||
|
|
||
| SetConstant("gs_config.comeras.kCamera2Saturations", LibCameraInterface::kCamera2Saturation); | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. fix indentation |
||
|
|
||
| // Let the command-line gain parameter override the .json config file parameter | ||
| // TBD - May want to have separate gain options? | ||
| if (GolfSimOptions::GetCommandLineOptions().camera_gain_ > 0.0) { | ||
|
|
||
196 changes: 196 additions & 0 deletions
196
Software/LMSourceCode/ImageProcessing/gs_opengolfsim_interface.cpp
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,196 @@ | ||
| #include <string> | ||
| #include <cstdlib> | ||
|
|
||
| #include "logging_tools.h" | ||
| #include "gs_config.h" | ||
| #include "gs_options.h" | ||
| #include "gs_ui_system.h" | ||
|
|
||
| #include "gs_opengolfsim_interface.h" | ||
| #include "gs_opengolfsim_results.h" | ||
|
|
||
| namespace golf_sim { | ||
|
|
||
| static const int kDefaultOGSPort = 3111; | ||
| static const char* kDefaultOGSHost = ""; // empty = disabled by default | ||
|
|
||
| static std::string ToString(SimConnState s) { | ||
| switch (s) { | ||
| case SimConnState::kDisabled: return "Disabled"; | ||
| case SimConnState::kDisconnected: return "Disconnected"; | ||
| case SimConnState::kConnecting: return "Connecting"; | ||
| case SimConnState::kConnected: return "Connected"; | ||
| case SimConnState::kError: return "Error"; | ||
| } | ||
| return "Unknown"; | ||
| } | ||
|
|
||
| void GsOpenGolfSimInterface::OnConnectionStateChanged( | ||
| SimConnState /*from*/, SimConnState to, const std::string& reason) | ||
| { | ||
| GsIPCResultType uiType = GsIPCResultType::kWaitingForSimulatorArmed; | ||
| std::string human; | ||
|
|
||
| switch (to) { | ||
| case SimConnState::kDisabled: | ||
| human = "OpenGolfSim disabled (no host/port configured)"; | ||
| break; | ||
|
|
||
| case SimConnState::kConnecting: | ||
| human = "OpenGolfSim connecting to " + socket_connect_address_ + ":" + socket_connect_port_; | ||
| break; | ||
|
|
||
| case SimConnState::kConnected: | ||
| break; | ||
|
|
||
| case SimConnState::kDisconnected: | ||
| human = "OpenGolfSim disconnected" + (reason.empty() ? "" : (": " + reason)); | ||
| break; | ||
|
|
||
| case SimConnState::kError: | ||
| default: | ||
| uiType = GsIPCResultType::kError; | ||
| human = "OpenGolfSim socket error" + (reason.empty() ? "" : (": " + reason)); | ||
| break; | ||
| } | ||
|
|
||
| // machine tag (Python parses this) | ||
| std::string state = | ||
| (to == SimConnState::kConnected) ? "connected" : | ||
| (to == SimConnState::kConnecting) ? "connecting" : | ||
| (to == SimConnState::kDisabled) ? "disabled" : | ||
| (to == SimConnState::kDisconnected) ? "disconnected" : "error"; | ||
|
|
||
| std::string tag = "SIM_CONN OpenGolfSim state=" + state; | ||
| if (!reason.empty()) tag += " reason=" + reason; | ||
|
|
||
| // ONE message: easy parse + still readable | ||
| std::string combined = "[" + tag + "] " + human; | ||
|
|
||
| GsUISystem::SendIPCStatusMessage(uiType, combined); | ||
| } | ||
|
|
||
|
|
||
| std::string GsOpenGolfSimInterface::EnsureNewline(const std::string& s) { | ||
| if (!s.empty() && s.back() == '\n') return s; | ||
| return s + "\n"; | ||
| } | ||
|
|
||
| GsOpenGolfSimInterface::GsOpenGolfSimInterface() { | ||
| // Defaults | ||
| socket_connect_address_ = kDefaultOGSHost; | ||
| socket_connect_port_ = std::to_string(kDefaultOGSPort); // <-- port is a string in base class | ||
| last_device_status_.clear(); | ||
|
|
||
| // Pull from config | ||
| GolfSimConfiguration::SetConstant( | ||
| "gs_config.golf_simulator_interfaces.OpenGolfSim.kOGSConnectAddress", | ||
| socket_connect_address_ | ||
| ); | ||
| GolfSimConfiguration::SetConstant( | ||
| "gs_config.golf_simulator_interfaces.OpenGolfSim.kOGSConnectPort", | ||
| socket_connect_port_ | ||
| ); | ||
| } | ||
|
|
||
| GsOpenGolfSimInterface::~GsOpenGolfSimInterface() {} | ||
|
|
||
| bool GsOpenGolfSimInterface::InterfaceIsPresent() { | ||
| // Read config into locals (static function!) | ||
| std::string addr = kDefaultOGSHost; | ||
| std::string port_str = std::to_string(kDefaultOGSPort); | ||
|
|
||
| GolfSimConfiguration::SetConstant( | ||
| "gs_config.golf_simulator_interfaces.OpenGolfSim.kOGSConnectAddress", | ||
| addr | ||
| ); | ||
| GolfSimConfiguration::SetConstant( | ||
| "gs_config.golf_simulator_interfaces.OpenGolfSim.kOGSConnectPort", | ||
| port_str | ||
| ); | ||
|
|
||
| int port = -1; | ||
| try { port = std::stoi(port_str); } catch (...) { port = -1; } | ||
|
|
||
| // Treat empty/"disabled" as off | ||
| if (addr.empty() || addr == "disabled" || port <= 0) { | ||
| GS_LOG_TRACE_MSG(trace, "GsOpenGolfSimInterface::InterfaceIsPresent - Not Present"); | ||
| return false; | ||
| } | ||
|
|
||
| GS_LOG_TRACE_MSG(trace, | ||
| "GsOpenGolfSimInterface::InterfaceIsPresent - Present (addr=" + addr + | ||
| ", port=" + std::to_string(port) + ")" | ||
| ); | ||
| return true; | ||
| } | ||
|
|
||
| bool GsOpenGolfSimInterface::Initialize() { | ||
| GS_LOG_TRACE_MSG(trace, "GsOpenGolfSimInterface Initialize called."); | ||
|
|
||
| // Log what we will actually try to connect to | ||
| GS_LOG_MSG(info, "OpenGolfSim connect target: " + socket_connect_address_ + ":" + socket_connect_port_); | ||
|
|
||
| if (!GsSimSocketInterface::Initialize()) { | ||
| GS_LOG_MSG(error, "GsOpenGolfSimInterface could not Initialize."); | ||
| return false; | ||
| } | ||
|
|
||
| #ifdef __unix__ | ||
| usleep(500); | ||
| #endif | ||
|
|
||
| initialized_ = true; | ||
|
|
||
| // Always ready on connect | ||
| SendSimMessage(EnsureNewline(R"({"type":"device","status":"ready"})")); | ||
| last_device_status_ = "ready"; // avoid immediate duplicate heartbeat | ||
| return true; | ||
| } | ||
|
|
||
| void GsOpenGolfSimInterface::DeInitialize() { | ||
| GsSimSocketInterface::DeInitialize(); | ||
| } | ||
|
|
||
| void GsOpenGolfSimInterface::SetSimSystemArmed(const bool /*is_armed*/) {} | ||
|
|
||
| bool GsOpenGolfSimInterface::GetSimSystemArmed() { | ||
| return true; | ||
| } | ||
|
|
||
| bool GsOpenGolfSimInterface::SendResults(const GsResults& r) { | ||
| if (!initialized_) return false; | ||
|
|
||
| // Heartbeat -> device status | ||
| if (r.result_message_is_keepalive_) { | ||
| const char* status = r.heartbeat_ball_detected_ ? "ready" : "busy"; | ||
| if (last_device_status_ == status) return true; | ||
| last_device_status_ = status; | ||
|
|
||
| std::string msg = std::string(R"({"type":"device","status":")") + status + R"("})"; | ||
| SendSimMessage(EnsureNewline(msg)); | ||
| return true; | ||
| } | ||
|
|
||
| // Normal shot path | ||
| const std::string msg = GenerateResultsDataToSend(r); | ||
| if (msg.empty()) return true; | ||
| SendSimMessage(msg); | ||
| return true; | ||
| } | ||
|
|
||
|
|
||
| std::string GsOpenGolfSimInterface::GenerateResultsDataToSend(const GsResults& r) { | ||
| GsOpenGolfSimResults ogs(r); | ||
| const std::string payload = ogs.Format(); | ||
| if (payload.empty()) | ||
| return ""; | ||
| return EnsureNewline(payload); | ||
| } | ||
|
|
||
| bool GsOpenGolfSimInterface::ProcessReceivedData(const std::string received_data) { | ||
| GS_LOG_MSG(info, "Received from OpenGolfSim: " + received_data); | ||
| return true; | ||
| } | ||
|
|
||
| } // namespace golf_sim |
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
what is this?