{
  "schema_version": 1,
  "source_commit": "7d52673791834caa743907dde83a1949134ef2f0",
  "upstream": {
    "test": "Tests/UnderwaterTest",
    "compiled_branch": "programmatic",
    "parsed_scenario_macro_defined": false,
    "evidence": "Tests/UnderwaterTest/UnderwaterTestManager.h line 31 reads '//#define PARSED_SCENARIO'; the #else branch of BuildScenario is the code that compiles. Tests/Data/underwater_test.scn and girona500auv_full.scn are not read by this binary.",
    "steps_per_second": 200,
    "realtime_factor": 1.0,
    "controller": null,
    "success_predicate": null,
    "notes": "The compiled scenario has one GIRONA500 vehicle with a four-joint arm and two fingers, an ocean with a uniform current and a jet, a heightfield seabed, one cylinder obstacle and two lights. Its thrusters are created with zero setpoint and nothing in the test commands them. SimulationStepCompleted has no active statement."
  },
  "variant": {
    "label": "Authored variant of Tests/UnderwaterTest: one-vehicle transit and station hold, ocean currents disabled",
    "authored_changes": [
      "Fixed-step loop driven by the adapter: one 0.005 s physics step per call, no wall-clock simulation thread and no real-time pacing",
      "Start-up console thread of GraphicalSimulationApp replaced by a no-op through an SDL_CreateThread definition in the adapter, so that one thread issues GL calls",
      "Ocean currents of the compiled scenario disabled with Ocean::DisableCurrents() after the scenario is built",
      "World-fixed native ColorCamera observer added after the compiled scenario is built",
      "Sensor noise generator seeded with a fixed value in place of std::random_device",
      "Station-keeping controller that reads the native Odometry sensor and writes the five native thruster setpoints",
      "State, sensor, thruster and command journals; no change to upstream source files, meshes, textures, materials, masses, thruster models or sensor placement",
      "Process ends with _Exit(0) after all journals are closed, because the pinned scenario teardown faults once the GL context is released"
    ]
  },
  "initialization": {
    "vehicle_origin_ned_m": [0.0, 0.0, 2.0],
    "vehicle_yaw_rad": 0.0,
    "source": "AddRobot(auv, Transform(Quaternion(0,0,0), Vector3(0,0,2))) in the compiled branch; the native initial-condition solver runs before sample 0",
    "sensor_noise_seed": 248,
    "threads": 1
  },
  "environment": {
    "currents": "disabled",
    "compiled_currents": "Uniform 1 m/s toward north and a 5 m/s jet of radius 0.3 m from NED (0,0,1) toward east, both enabled",
    "decision": "Made from open-loop runs, before any controller run. With currents as compiled and zero command the vehicle drifted 17.9 m north in 20 s. With both surge thrusters at -1 (full astern, against the current) for 10 s it was still moving north at 0.65 m/s over ground and had yawed 58 degrees. The compiled thrusters cannot hold station in the compiled current, so the hold is recorded with currents disabled and labelled as such.",
    "buoyancy": "The compiled vehicle is positively buoyant; with zero command and no current it rises. Depth is held by thrust."
  },
  "clocks": {
    "physics_hz": 200,
    "control_hz": 50,
    "camera_hz": 25,
    "horizon_s": 40.0,
    "physics_steps": 8000,
    "control_ticks": 2000,
    "samples": 1001,
    "alignment": "Sample 0 is the state after the native initial-condition solve, before any command. A command is issued before physics steps 0, 4, 8, ... and held for four steps. Sample i and camera frame i are taken after physics step 8*i."
  },
  "units": {
    "frame": "NED world frame as used by Stonefish: x north, y east, z down; depth is positive z",
    "length": "m",
    "angle": "rad",
    "time": "s",
    "quaternion": "xyzw, body to world",
    "thruster_command": "normalized setpoint in [-1, 1]; the native thruster scales it by its 105 rad/s setpoint limit",
    "thruster_measured": "rotor speed rad/s, thrust N, torque N m",
    "pressure": "Pa gauge"
  },
  "camera": {
    "sensor": "sf::ColorCamera, world-attached, updated every physics step and read at 25 Hz",
    "resolution": [960, 540],
    "horizontal_fov_deg": 60.0,
    "eye_ned_m": [-2.0, -4.5, 2.3],
    "look_at_ned_m": [0.75, 0.5, 2.35],
    "amendment": "Camera pose only, changed after the controller freeze and before any capture: the first declared pose (eye [-1.5, -3.5, 2.3], look-at [0.9, 0.6, 2.9]) would have cut off the upper hulls at the start pose, which sit about 0.7 m above the vehicle origin. Physics, controller and outcome are untouched."
  },
  "task": {
    "description": "Hold the start pose for 5 s, then transit to and hold a station 1.5 m north, 1.0 m east and 1.5 m deeper at heading 045.",
    "waypoints": [
      {
        "from_s": 0.0,
        "target_ned_m": [0.0, 0.0, 2.0],
        "target_yaw_rad": 0.0
      },
      {
        "from_s": 5.0,
        "target_ned_m": [1.5, 1.0, 3.5],
        "target_yaw_rad": 0.7853981633974483
      }
    ]
  },
  "controller": {
    "kind": "Authored four-axis PID on native Odometry (position, body velocity, attitude, yaw rate); no learned policy",
    "inputs": "The Odom sensor of robot GIRONA500 only. The compiled scenario sets no noise on this sensor, so it is privileged simulator state rather than an estimated navigation solution.",
    "law": "error = target - measured in NED and wrapped heading; integral clamped per axis; north/east P+I rotated into the body frame by measured heading; derivative on measured body velocity and yaw rate; each axis effort clamped to [-1, 1]",
    "allocation_law": "sway thruster = a0*effort_sway; surge port = a1*(effort_surge + effort_yaw); surge starboard = a2*(effort_surge - effort_yaw); heave stern = a3*effort_heave; heave bow = a4*effort_heave; command = sign(d)*sqrt(|d|) after clamping d to [-1, 1]",
    "frozen_utc": "2026-10-09T19:32:33+00:00",
    "kp": {
      "north": 2.0,
      "east": 2.0,
      "down": 2.5,
      "yaw": 1.5
    },
    "ki": {
      "north": 0.3,
      "east": 0.3,
      "down": 1.2,
      "yaw": 0.4
    },
    "kd": {
      "north": 3.0,
      "east": 4.0,
      "down": 4.0,
      "yaw": 2.5
    },
    "integral_limit": {
      "north": 1.0,
      "east": 1.0,
      "down": 0.8,
      "yaw": 0.6
    },
    "allocation": {
      "ThrusterSway": -1.0,
      "ThrusterSurgePort": 1.0,
      "ThrusterSurgeStarboard": 1.0,
      "ThrusterHeaveStern": 1.0,
      "ThrusterHeaveBow": 1.0
    },
    "thruster_signs_measured_open_loop": "Positive normalized command: surge thrusters push forward, the starboard one turning the bow to port; heave thrusters push down; the sway thruster pushes to port. Allocation signs follow from these runs.",
    "kd_note": "kd north and east act on measured body surge and sway velocity, down on body heave velocity, yaw on body yaw rate"
  },
  "outcome": {
    "origin": "Authored. Thresholds frozen before any controller run; upstream defines no success predicate, reward or end condition.",
    "evaluated_on": "every physics step (200 Hz) of the vehicle origin frame",
    "hold_window_s": [25.0, 40.0],
    "maximum_horizontal_error_m": 0.1,
    "maximum_depth_error_m": 0.1,
    "maximum_heading_error_rad": 0.08726646259971647,
    "maximum_speed_m_per_s": 0.1,
    "maximum_contact_points": 0,
    "minimum_depth_m": 0.8,
    "maximum_roll_pitch_rad": 0.35,
    "definition": "Success needs every listed check. Hold-window checks apply to every physics step from 25 s to 40 s against the active waypoint. Contact, depth, attitude and finiteness apply to every physics step of the horizon. Contact points are those of Bullet manifolds between bodies that both have contact response."
  },
  "replay": {
    "method": "Fresh process, same compiled scenario and seed, no controller code path and no observer camera. The 2,000 recorded command rows are applied before their recorded physics steps. No state is set from the recording.",
    "compared": "every numeric channel of the 1,001 samples and of the 8,000 physics-step rows",
    "absolute_tolerance": 1e-09,
    "divergence_policy": "Report the maximum absolute difference per channel and every failing channel. Tolerances and seed are not changed after results are seen."
  },
  "bounds": {
    "max_wall_seconds": 1800,
    "one_native_experiment_at_a_time": true
  },
  "development": {
    "policy": "Open-loop runs (no controller) may precede the outcome freeze. Controller gains are tuned in declared development runs after the outcome freeze and before the controller freeze. One capture follows the controller freeze.",
    "outcome_frozen_utc": "2026-10-09T19:25:53+00:00",
    "runs_before_outcome_freeze": "Open-loop only (constant commands, no controller); listed in development.open_loop_runs",
    "runs_before_controller_freeze": {
      "closed_loop_runs": 8,
      "full_horizon_runs_that_met_the_frozen_predicate": 6,
      "full_horizon_runs_that_did_not": 2,
      "note": "One grid of eight gain sets, run once with the frozen outcome thresholds unchanged and without the observer camera. The set with the smallest hold-window horizontal error is frozen above; no further tuning followed.",
      "selected": "cl-05-more-i",
      "axis_order": [
        "north",
        "east",
        "down",
        "yaw"
      ],
      "runs": [
        {
          "name": "cl-01-base",
          "kp": [2.0, 2.0, 2.5, 1.5],
          "ki": [0.1, 0.1, 0.8, 0.2],
          "kd": [3.0, 4.0, 4.0, 2.5],
          "integral_limit": [2.0, 2.0, 1.0, 1.0],
          "met_predicate": true,
          "hold_window_maxima": {
            "depth_error_m": 0.0178,
            "heading_error_rad": 0.0522,
            "horizontal_error_m": 0.0978,
            "speed_m_per_s": 0.0064
          }
        },
        {
          "name": "cl-02-damped",
          "kp": [2.0, 2.0, 2.5, 1.5],
          "ki": [0.1, 0.1, 0.8, 0.2],
          "kd": [4.5, 6.0, 6.0, 3.75],
          "integral_limit": [2.0, 2.0, 1.0, 1.0],
          "met_predicate": false,
          "hold_window_maxima": {
            "depth_error_m": 0.0209,
            "heading_error_rad": 0.0686,
            "horizontal_error_m": 0.1076,
            "speed_m_per_s": 0.0134
          }
        },
        {
          "name": "cl-03-soft",
          "kp": [1.0, 1.0, 1.25, 0.75],
          "ki": [0.1, 0.1, 0.8, 0.2],
          "kd": [3.0, 4.0, 4.0, 2.5],
          "integral_limit": [2.0, 2.0, 1.0, 1.0],
          "met_predicate": false,
          "hold_window_maxima": {
            "depth_error_m": 0.0874,
            "heading_error_rad": 0.1164,
            "horizontal_error_m": 0.1785,
            "speed_m_per_s": 0.0295
          }
        },
        {
          "name": "cl-04-stiff",
          "kp": [4.0, 4.0, 5.0, 3.0],
          "ki": [0.1, 0.1, 0.8, 0.2],
          "kd": [4.5, 6.0, 6.0, 3.75],
          "integral_limit": [2.0, 2.0, 1.0, 1.0],
          "met_predicate": true,
          "hold_window_maxima": {
            "depth_error_m": 0.0127,
            "heading_error_rad": 0.0336,
            "horizontal_error_m": 0.0611,
            "speed_m_per_s": 0.0033
          }
        },
        {
          "name": "cl-05-more-i",
          "kp": [2.0, 2.0, 2.5, 1.5],
          "ki": [0.3, 0.3, 1.2, 0.4],
          "kd": [3.0, 4.0, 4.0, 2.5],
          "integral_limit": [1.0, 1.0, 0.8, 0.6],
          "met_predicate": true,
          "hold_window_maxima": {
            "depth_error_m": 0.0128,
            "heading_error_rad": 0.0127,
            "horizontal_error_m": 0.0355,
            "speed_m_per_s": 0.0163
          }
        },
        {
          "name": "cl-06-stiff-z",
          "kp": [2.0, 2.0, 5.0, 1.5],
          "ki": [0.1, 0.1, 1.2, 0.2],
          "kd": [3.0, 4.0, 8.0, 2.5],
          "integral_limit": [2.0, 2.0, 0.8, 1.0],
          "met_predicate": true,
          "hold_window_maxima": {
            "depth_error_m": 0.0029,
            "heading_error_rad": 0.0529,
            "horizontal_error_m": 0.0973,
            "speed_m_per_s": 0.0066
          }
        },
        {
          "name": "cl-07-stiff-yaw",
          "kp": [2.0, 2.0, 2.5, 3.0],
          "ki": [0.1, 0.1, 0.8, 0.3],
          "kd": [3.0, 4.0, 4.0, 4.0],
          "integral_limit": [2.0, 2.0, 1.0, 1.0],
          "met_predicate": true,
          "hold_window_maxima": {
            "depth_error_m": 0.0174,
            "heading_error_rad": 0.036,
            "horizontal_error_m": 0.0996,
            "speed_m_per_s": 0.0063
          }
        },
        {
          "name": "cl-08-slow",
          "kp": [1.5, 1.5, 2.0, 1.0],
          "ki": [0.05, 0.05, 0.6, 0.1],
          "kd": [4.0, 5.0, 5.0, 3.0],
          "integral_limit": [2.0, 2.0, 1.4, 1.0],
          "met_predicate": true,
          "hold_window_maxima": {
            "depth_error_m": 0.0286,
            "heading_error_rad": 0.0508,
            "horizontal_error_m": 0.0906,
            "speed_m_per_s": 0.0173
          }
        }
      ]
    },
    "open_loop_runs": [
      {
        "name": "ol-drift-upstream",
        "seconds": 20.0,
        "currents": "upstream",
        "command": [0.0, 0.0, 0.0, 0.0, 0.0],
        "final_origin_ned_m": [17.9314, 0.4957, 0.7939],
        "final_yaw_rad": -0.5732,
        "final_velocity_ned_m_per_s": [0.9999, 0.0, 0.0104]
      },
      {
        "name": "ol-surge-astern-upstream",
        "seconds": 10.0,
        "currents": "upstream",
        "command": [0.0, -1.0, -1.0, 0.0, 0.0],
        "final_origin_ned_m": [5.6007, 3.7479, 0.9923],
        "final_yaw_rad": -1.0156,
        "final_velocity_ned_m_per_s": [0.6482, 0.5492, -0.0345]
      },
      {
        "name": "ol-drift-disabled",
        "seconds": 10.0,
        "currents": "disabled",
        "command": [0.0, 0.0, 0.0, 0.0, 0.0],
        "final_origin_ned_m": [0.0049, -0.0002, 0.9113],
        "final_yaw_rad": -0.0004,
        "final_velocity_ned_m_per_s": [0.0017, -0.0, -0.0253]
      },
      {
        "name": "ol-sway",
        "seconds": 4.0,
        "currents": "disabled",
        "command": [0.5, 0.0, 0.0, 0.0, 0.0],
        "final_origin_ned_m": [0.0378, -0.1734, 1.5096],
        "final_yaw_rad": 0.0415,
        "final_velocity_ned_m_per_s": [-0.0057, -0.0685, -0.1864]
      },
      {
        "name": "ol-surge-port",
        "seconds": 4.0,
        "currents": "disabled",
        "command": [0.0, 0.5, 0.0, 0.0, 0.0],
        "final_origin_ned_m": [0.191, 0.0049, 1.4617],
        "final_yaw_rad": 0.1648,
        "final_velocity_ned_m_per_s": [0.0507, -0.002, -0.2129]
      },
      {
        "name": "ol-surge-starboard",
        "seconds": 4.0,
        "currents": "disabled",
        "command": [0.0, 0.0, 0.5, 0.0, 0.0],
        "final_origin_ned_m": [0.1912, -0.0042, 1.4624],
        "final_yaw_rad": -0.1632,
        "final_velocity_ned_m_per_s": [0.0507, 0.0017, -0.2127]
      },
      {
        "name": "ol-heave-stern",
        "seconds": 4.0,
        "currents": "disabled",
        "command": [0.0, 0.0, 0.0, 0.5, 0.0],
        "final_origin_ned_m": [0.0892, -0.0006, 1.6558],
        "final_yaw_rad": -0.0133,
        "final_velocity_ned_m_per_s": [0.0076, 0.0004, -0.1337]
      },
      {
        "name": "ol-heave-bow",
        "seconds": 4.0,
        "currents": "disabled",
        "command": [0.0, 0.0, 0.0, 0.0, 0.5],
        "final_origin_ned_m": [0.0214, -0.0005, 1.6624],
        "final_yaw_rad": -0.0158,
        "final_velocity_ned_m_per_s": [0.001, 0.0005, -0.1331]
      },
      {
        "name": "ol-heave-full",
        "seconds": 10.0,
        "currents": "disabled",
        "command": [0.0, 0.0, 0.0, 1.0, 1.0],
        "final_origin_ned_m": [0.5849, -0.0197, 4.8884],
        "final_yaw_rad": -0.3099,
        "final_velocity_ned_m_per_s": [0.0684, -0.0062, 0.3397]
      },
      {
        "name": "ol-surge-full",
        "seconds": 10.0,
        "currents": "disabled",
        "command": [0.0, 1.0, 1.0, 0.0, 0.0],
        "final_origin_ned_m": [4.809, 0.0278, 0.8562],
        "final_yaw_rad": 0.0212,
        "final_velocity_ned_m_per_s": [0.62, 0.0082, -0.197]
      },
      {
        "name": "ol-sway-full",
        "seconds": 10.0,
        "currents": "disabled",
        "command": [1.0, 0.0, 0.0, 0.0, 0.0],
        "final_origin_ned_m": [0.4431, -2.061, 0.8089],
        "final_yaw_rad": 0.5807,
        "final_velocity_ned_m_per_s": [0.1334, -0.2589, 0.1006]
      }
    ]
  },
  "credit": {
    "learned_policy_evaluations": 0,
    "benchmark_suites": 0,
    "upstream_registered_tasks": 0
  }
}
