Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 15 additions & 1 deletion app.py
Original file line number Diff line number Diff line change
Expand Up @@ -76,11 +76,25 @@
)

# Initialize IP camera (SMTSEC SIP-K327GS) via RTSP
ip_cam_url = os.getenv("IP_CAMERA_URL", "rtsp://10.77.0.3:554/stream1")
_ip_camera_config = _config.get_section("ip_camera") or {}
ip_cam_active_ip = _ip_camera_config.get("active_ip") or "10.77.0.4"
ip_cam_url = os.getenv("IP_CAMERA_URL")
if ip_cam_url:
ip_cam_active_ip = None
else:
ip_cam_url = f"rtsp://{ip_cam_active_ip}:554/stream1"
ip_cam_out_width = int(os.getenv("IP_CAMERA_OUT_WIDTH", "960"))
ip_cam_out_height = int(os.getenv("IP_CAMERA_OUT_HEIGHT", "540"))
ip_cam_jpeg_quality = int(os.getenv("IP_CAMERA_JPEG_QUALITY", "70"))
ip_cam_flip_180 = os.getenv("IP_CAMERA_FLIP_180", "false").strip().lower() in {"1", "true", "yes", "on"}
app.config["IP_CAMERA_ACTIVE_IP"] = ip_cam_active_ip
app.config["IP_CAMERA_ACTIVE_URL"] = ip_cam_url
app.config["IP_CAMERA_SETTINGS"] = {
"out_width": ip_cam_out_width,
"out_height": ip_cam_out_height,
"jpeg_quality": ip_cam_jpeg_quality,
"flip_180": ip_cam_flip_180,
}
app.config["IP_CAMERA"] = init_ip_camera(
url=ip_cam_url,
out_width=ip_cam_out_width,
Expand Down
6 changes: 5 additions & 1 deletion data/config.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,5 +13,9 @@
"x": "+x",
"y": "+y",
"z": "+z"
},
"ip_camera": {
"active_ip": "10.77.0.4",
"presets": []
}
}
}
55 changes: 1 addition & 54 deletions docs/swagger.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ info:
description: >
REST and streaming endpoints exposed by the Topside ROV Flask application.
The API provides camera status and feeds, telemetry, command/uplink status,
IMU configuration, debug overrides, controller gains, and PID tuning.
IMU configuration, debug overrides, controller input, and PID tuning.
version: "1.0.0"

host: localhost:5000
Expand All @@ -28,8 +28,6 @@ tags:
description: IMU receiver status, tare, offsets, and axis mappings
- name: Debug
description: Manual debug overrides and attitude setpoints
- name: Controller
description: Gamepad controller input and gain settings
- name: PID
description: PID gain tuning, setpoints, and saved presets
- name: System
Expand Down Expand Up @@ -695,46 +693,6 @@ paths:
504:
description: Thruster axes were neutralized, but the MCU did not confirm zero PID gains

/api/controller/gains:
get:
tags: [Controller]
summary: Get controller gain settings
responses:
200:
description: Current controller gains
schema:
type: object
properties:
ok:
type: boolean
example: true
gains:
$ref: "#/definitions/ControllerGains"
503:
description: Controller not available
post:
tags: [Controller]
summary: Set controller gain settings
parameters:
- in: body
name: body
required: true
schema:
$ref: "#/definitions/ControllerGains"
responses:
200:
description: Gains updated
schema:
type: object
properties:
ok:
type: boolean
example: true
gains:
$ref: "#/definitions/ControllerGains"
503:
description: Controller not available

/api/pid/gains:
get:
tags: [PID]
Expand Down Expand Up @@ -1020,17 +978,6 @@ definitions:
type: number
example: [0, 0, 1, 0, 0, 0, 0.4, 0]

ControllerGains:
type: object
properties:
master: {type: number, minimum: 0.0, maximum: 1.0, example: 1.0}
surge: {type: number, minimum: 0.0, maximum: 1.0, example: 1.0}
sway: {type: number, minimum: 0.0, maximum: 1.0, example: 1.0}
heave: {type: number, minimum: 0.0, maximum: 1.0, example: 1.0}
roll: {type: number, minimum: 0.0, maximum: 1.0, example: 1.0}
pitch: {type: number, minimum: 0.0, maximum: 1.0, example: 1.0}
yaw: {type: number, minimum: 0.0, maximum: 1.0, example: 1.0}

CameraStatusBase:
type: object
properties:
Expand Down
24 changes: 12 additions & 12 deletions lib/camera.py
Original file line number Diff line number Diff line change
Expand Up @@ -338,14 +338,14 @@ def _build_placeholder_jpeg(self):
return buf.tobytes() if ok else b""

def _run(self):
print("[RPi Camera] Trying OpenCV+GStreamer ")
print("[RPi Camera] Trying OpenCV+GStreamer ...")
if self._opencv_gstreamer_available():
if self._run_opencv_gstreamer():
return
else:
print("[RPi Camera] OpenCV has no GStreamer support, skipping.")

print("[RPi Camera] Trying gst-launch-1.0 ")
print("[RPi Camera] Trying gst-launch-1.0 ...")
self._run_gst_subprocess()

def _opencv_gstreamer_available(self):
Expand Down Expand Up @@ -373,14 +373,14 @@ def _run_opencv_gstreamer(self):
self.last_error = "OpenCV GStreamer pipeline failed to open"
return False

print(f"[RPi Camera] Listening on UDP port {self.port} ")
print(f"[RPi Camera] Listening on UDP port {self.port} ...")
self.is_listening = True
had_frame = False
while not self._stop_event.is_set():
ok, frame = cap.read()
if ok and frame is not None and frame.size > 0:
if not had_frame:
print("[RPi Camera] Receiving frames")
print("[RPi Camera] Receiving frames")
had_frame = True
self._set_frame(frame)
else:
Expand Down Expand Up @@ -454,7 +454,7 @@ def _run_gst_subprocess(self):
"async=false",
]

print(f"[RPi Camera] Listening on UDP port {self.port} ")
print(f"[RPi Camera] Listening on UDP port {self.port} ...")
self.is_listening = True
proc = subprocess.Popen(
cmd,
Expand Down Expand Up @@ -504,7 +504,7 @@ def _run_gst_subprocess(self):
del buffer[: end + 2]

if not had_frame:
print("[RPi Camera] Receiving frames")
print("[RPi Camera] Receiving frames")
had_frame = True
# JPEG is already encoded by GStreamer; avoid re-decode/re-encode.
self._set_jpeg_bytes(jpg)
Expand Down Expand Up @@ -741,31 +741,31 @@ def _release_cap(self):
self._cap = None

def _run_loop(self):
"""Main thread: connectread frames reconnect on failure."""
"""Main thread: connect, read frames, then reconnect on failure."""
while not self._stop_event.is_set():
print(f"[IP Camera] Connecting to {self.url} ")
print(f"[IP Camera] Connecting to {self.url} ...")
if not self._open_stream():
self.last_error = f"Failed to open: {self.url}"
print(f"[IP Camera] {self.last_error}")
print(f"[IP Camera] error: {self.last_error}")
self.is_connected = False
if self._stop_event.wait(self.RECONNECT_DELAY):
break
continue

print("[IP Camera] Stream connected")
print("[IP Camera] Stream connected")
self.last_error = None
had_frame = False

while not self._stop_event.is_set():
ok, frame = self._cap.read()
if not ok or frame is None:
print("[IP Camera] Lost connection, will reconnect ")
print("[IP Camera] Lost connection, will reconnect ...")
self.is_connected = False
self._release_cap()
break

if not had_frame:
print("[IP Camera] Receiving frames")
print("[IP Camera] Receiving frames")
had_frame = True

if frame.shape[1] != self.out_width or frame.shape[0] != self.out_height:
Expand Down
39 changes: 0 additions & 39 deletions lib/controller.py
Original file line number Diff line number Diff line change
Expand Up @@ -83,17 +83,6 @@ def __init__(self, bitmask_client: BitmaskClient = None, rate_hz: float = 60.0):
self._debug_lock = threading.Lock()
self._input_status_lock = threading.Lock()
self._input_status = self._empty_input_status()
# Gain settings (per-axis and master)
self._gain_lock = threading.Lock()
self._master_gain = 1.0
self._axis_gains = {
"surge": 1.0,
"sway": 1.0,
"heave": 1.0,
"roll": 1.0,
"pitch": 1.0,
"yaw": 1.0,
}
self._try_connect()

def _try_connect(self):
Expand Down Expand Up @@ -287,26 +276,6 @@ def _update_input_status(self, buttons):
}
)

# --- Gain API ---
def set_gains(self, master=None, **axis_gains):
"""Set master and/or per-axis gains. Values should be 0.0 – 1.0."""
with self._gain_lock:
if master is not None:
self._master_gain = max(0.0, min(1.0, float(master)))
for key in ("surge", "sway", "heave", "roll", "pitch", "yaw"):
if key in axis_gains:
self._axis_gains[key] = max(0.0, min(1.0, float(axis_gains[key])))

def get_gains(self):
"""Return current gain settings."""
with self._gain_lock:
return {"master": self._master_gain, **self._axis_gains}

def _apply_gain(self, axis_name, value):
"""Multiply a value by its per-axis gain and the master gain."""
with self._gain_lock:
return value * self._axis_gains.get(axis_name, 1.0) * self._master_gain

# --- Light API ---
def set_light(self, level):
"""Set light brightness from a normalized 0.0-1.0 level.
Expand Down Expand Up @@ -499,14 +468,6 @@ def update(self):
self._prev_dpad_down = dpad_down
self._update_input_status(buttons)

# Apply gain to each axis
surge = self._apply_gain("surge", surge)
sway = self._apply_gain("sway", sway)
heave = self._apply_gain("heave", heave)
roll = self._apply_gain("roll", roll)
pitch = self._apply_gain("pitch", pitch)
yaw = self._apply_gain("yaw", yaw)

# Send to ROV!
if self.bm:
self.bm.set_from_axes(
Expand Down
Loading
Loading