Search The Support Knowledgebase


(database of technical support questions and answers):
   

Author Topic: 3DOF Motion Simulator Plugin

Offline zappadoc

  • Management
  • Suppreme Master Sensei
  • *
  • *
  • *
  • Posts: 5800
  • No email or PM support
    • ZDoc Dev Blog
3DOF Motion Simulator Plugin
« on: June 30, 2026, 03:14:56 PM »




Hi everyone,

The new OpenSR 3DOF motion plugin is now feature-complete and available in the current early access version!

Key Features:

    Full 3DOF forward kinematics (4-actuator support)
    Actuator-ready & UDP output
    Direct Thanos controller communication via COM port

Note on Thanos Support:
The Thanos implementation is complete and ready for use. Because we do not have a 4-actuator Thanos setup in-house, we haven't been able to validate it on physical hardware yet. We are opening this up for community feedback to ensure the implementation works perfectly across different builds.

Interested in testing?
If you own a four-actuator simulator with a Thanos controller, we would love to hear your feedback. Please reply here or email tech_support [at] eksimracing [dot] org with your hardware specs (simulator model, Thanos version, actuator type, and games played) so we can track the testing results.

Disclaimer: This plugin is in Early Access and may contain bugs. As it controls physical motion hardware, please exercise caution during testing.
No support will be provided by PM or EMAIL, PM has been disabled due to heavy spam and abuse, please post in the support boards, thanks for your understanding.

Offline zappadoc

  • Management
  • Suppreme Master Sensei
  • *
  • *
  • *
  • Posts: 5800
  • No email or PM support
    • ZDoc Dev Blog
Re: 3DOF Motion Simulator Plugin
« Reply #1 on: July 02, 2026, 06:18:35 PM »
I’ve added support for the Thanos AMC Motion simulator controller in the OpenSR Early Access v0.4.0.6!



Because we are still in the testing phase, parameters are currently read-only to keep everyone safe. This is a high-level feature designed for pro ;) once we release the ability to change settings, we recommend sticking to your default configurations unless you know exactly what you're doing.

We are actively looking for testers to help us improve the 3DOF Motion Simulator plugin which, like OpenSR, is free for everyone. If you are interested in helping us, please contact us.
No support will be provided by PM or EMAIL, PM has been disabled due to heavy spam and abuse, please post in the support boards, thanks for your understanding.

Offline zappadoc

  • Management
  • Suppreme Master Sensei
  • *
  • *
  • *
  • Posts: 5800
  • No email or PM support
    • ZDoc Dev Blog
Re: 3DOF Motion Simulator Plugin
« Reply #2 on: September 29, 2026, 03:52:38 PM »
New 3DOF Motion plugin available for download with last v0.5.0.2 update.

the 3D View of 3DOF Motion plugin now have the lateral actuator preview for traction loss,
you can zoom in/out with the mouse wheel,
and move the entire 3D View with the mouse
also, with default motion templates ready to use for 21 games.

Select Check For Update to get the latest plugin



No support will be provided by PM or EMAIL, PM has been disabled due to heavy spam and abuse, please post in the support boards, thanks for your understanding.

Offline zappadoc

  • Management
  • Suppreme Master Sensei
  • *
  • *
  • *
  • Posts: 5800
  • No email or PM support
    • ZDoc Dev Blog
Re: 3DOF Motion Simulator Plugin
« Reply #3 on: September 29, 2026, 04:06:14 PM »
Hi,
If you run a 3DOF motion simulator and want to build a bridge from OpenSR to your own platform (or want to pass the info the the manufacturer of your 3DOF), I've included a simple Python UDP client example in the plugin folder. It decodes the UDP packets sent by the plugin's built-in server, the same packet layout works in any language (C#, C++, Python, etc.).

Script path:
\Users\<username>\Documents\OpenSR\OutPlugins\3DOFViewSimulator\python\3dof_udp_server_test.py

A quick read through the script should be all you need to connect your motion platform.
Cheers,
z


Code: python
import socket
import ctypes
import time

# --- Configuration ---
UDP_IP = "0.0.0.0"
UDP_PORT = 33442  # <--- Updated Port
TIMEOUT_SECONDS = 1.5

# --- Struct Definition ---
# /*
# This plugin outputs physics telemetry in a unified Left-Handed Z-Forward, Y-Up convention.
# All games are normalized to this format before being sent over UDP.

    # Surge (+Z): Forward acceleration.
    # Sway (+X): Force pushing to the right.
    # Heave (+Y): Upward vertical force.

    # Pitch (+X rot): Nose up.
    # Roll (+Z rot): Tilt right.
    # Yaw (+Y rot): Turn right.
# */

# struct OpenSR3DOFMotionPacket {
    # uint32_t packetId;
    # uint8_t paused;
# uint8_t stroke; // 100mmm or 150mm
    # // 1. THE LAZY DEV CHANNEL (Direct Actuator Outputs)
    # float posFL                 // mm direct target lengths (0.0 to MaxStroke)
    # float posFR
    # float posRL
    # float posRR
    # float tractionLoss_pos;      // mm dedicated traction loss/rear-slide actuator move

    # // 2. THE ADVANCED DEV CHANNEL (Normalized Game Telemetry)
    # double pitch_X;             
    # double yaw_Y;               
    # double roll_Z;               

    # // local acceleration (Z-Forward, Y-Up)
    # double sway_X;               
    # double heave_Y;             
    # double surge_Z;             

    # // Rotational/Angular Velocities (Rates)
    # double pitchRate_X;         
    # double yawRate_Y;           
    # double rollRate_Z;           
# };

class MotionPacket(ctypes.Structure):
    _pack_ = 1
    _fields_ = [
        ("packetId",  ctypes.c_uint32),
        ("paused",    ctypes.c_uint8),
        ("stroke",    ctypes.c_uint8),
        ("posFL",     ctypes.c_float),
        ("posFR",     ctypes.c_float),
        ("posRL",     ctypes.c_float),
        ("posRR",     ctypes.c_float),
        ("tl",        ctypes.c_float),
        ("pitch",     ctypes.c_double),
        ("yaw",       ctypes.c_double),
        ("roll",      ctypes.c_double),
        ("sway",      ctypes.c_double),
        ("heave",     ctypes.c_double),
        ("surge",     ctypes.c_double),
        ("pitchRate", ctypes.c_double),
        ("yawRate",   ctypes.c_double),
        ("rollRate",  ctypes.c_double),
        # ("suspFL",    ctypes.c_float), // not yet implemented
        # ("suspFR",    ctypes.c_float),
        # ("suspRL",    ctypes.c_float),
        # ("suspRR",    ctypes.c_float),
    ]

def main():
    sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
   
    try:
        sock.bind((UDP_IP, UDP_PORT))
        sock.settimeout(TIMEOUT_SECONDS)
    except OSError as e:
        print(f"Error binding port {UDP_PORT}: {e}")
        return

    expected_size = ctypes.sizeof(MotionPacket)
    print(f"Listening on port {UDP_PORT}...")
    print(f"Packet Size: {expected_size} bytes")

    # --- UPS Calculation Variables ---
    last_time = time.time()
    packet_count = 0
    current_ups = 0

    while True:
        try:
            data, addr = sock.recvfrom(1024)

            # 1. Update Counters
            packet_count += 1
            curr_time = time.time()
           
            # 2. Check if 1 second has passed
            time_diff = curr_time - last_time
            if time_diff >= 1.0:
                current_ups = packet_count / time_diff # Accurate calc even if > 1.0s
                packet_count = 0
                last_time = curr_time

            # 3. Process Data
            if len(data) >= expected_size:
                pkt = MotionPacket.from_buffer_copy(data)
               
                status = "PAUSED" if pkt.paused else "RUNNING"

                # Print Data + UPS
                # I formatted UPS to show on the far left
                print(f"[UPS: {int(current_ups):<4}] ID: {pkt.packetId:<8} | {status:<7} | "
                      f"FL: {pkt.posFL:6.2f} | FR: {pkt.posFR:6.2f} | "
                      f"RL: {pkt.posRL:6.2f} | RR: {pkt.posRR:6.2f} | TL: {pkt.tl:6.2f} | "
                      f"SURGE: {pkt.surge:6.2f} | SWAY: {pkt.sway:6.2f} | "
                      f"HEAVE: {pkt.heave:6.2f} | YAW: {pkt.yaw:6.2f} | YAWRATE: {pkt.yawRate:6.2f}")
            else:
                print(f"Size Mismatch: {len(data)} bytes (expected {expected_size})")

        except socket.timeout:
            # If timed out, UPS is effectively 0
            if current_ups > 0:
                print("\n>>> PAUSED OR NO SIGNAL (UPS: 0) <<<\n")
                current_ups = 0
           
        except KeyboardInterrupt:
            break

    sock.close()

if __name__ == "__main__":
    main()

No support will be provided by PM or EMAIL, PM has been disabled due to heavy spam and abuse, please post in the support boards, thanks for your understanding.