5-state display mode: auto day/night, nightrider, screen off

New ScreenDisplayMode param (fixes ScreenDisaplayMode typo):
  0=auto-normal, 1=auto-nightrider, 2=normal, 3=screen-off, 4=nightrider

Nightrider mode: black background (no camera feed), path/lane lines
drawn as 2px outlines only. Auto mode uses NOAA solar position calc
in gpsd to switch between day/night based on GPS lat/lon and UTC time.
First calc on GPS fix, then every 30 seconds.

Button cycle onroad: 0→4, 1→2, 2→3, 3→4, 4→2 (never back to auto).
Offroad: any→3, 3→0. bench_cmd debugbutton simulates the button press.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-04-13 06:49:38 +00:00
parent d801177d2a
commit 5b91dac33e
7 changed files with 164 additions and 44 deletions

View File

@@ -94,8 +94,8 @@ void HomeWindow::updateState(const UIState &s) {
// CLEARPILOT: honor display on/off while showing splash in park
if (parked && ready->isVisible()) {
int screenMode = paramsMemory.getInt("ScreenDisaplayMode");
if (screenMode == 1) {
int screenMode = paramsMemory.getInt("ScreenDisplayMode");
if (screenMode == 3) {
Hardware::set_display_power(false);
} else {
Hardware::set_display_power(true);

View File

@@ -395,15 +395,12 @@ if (edgeColor != bgColor) {
}
void AnnotatedCameraWidget::drawHud(QPainter &p) {
// Blank when screenDisplayMode=1
// CLEARPILOT: display power control based on ScreenDisplayMode
p.save();
int screenDisaplayMode = paramsMemory.getInt("ScreenDisaplayMode");
if (screenDisaplayMode == 1 && !alert_is_visible) {
// Draw black, filled, full-size rectangle to blank the screen
// p.fillRect(0, 0, width(), height(), Qt::black);
// p.restore();
if (displayMode == 3 && !alert_is_visible) {
Hardware::set_display_power(false);
p.restore();
return;
} else {
Hardware::set_display_power(true);
@@ -599,17 +596,36 @@ void AnnotatedCameraWidget::drawLaneLines(QPainter &painter, const UIState *s) {
SubMaster &sm = *(s->sm);
// CLEARPILOT: nightrider mode — outline only, no fill
bool outlineOnly = nightriderMode;
// lanelines
for (int i = 0; i < std::size(scene.lane_line_vertices); ++i) {
painter.setBrush(QColor::fromRgbF(1.0, 1.0, 1.0, std::clamp<float>(scene.lane_line_probs[i], 0.0, 0.7)));
QColor lineColor = QColor::fromRgbF(1.0, 1.0, 1.0, std::clamp<float>(scene.lane_line_probs[i], 0.0, 0.7));
if (outlineOnly) {
painter.setPen(QPen(lineColor, 2));
painter.setBrush(Qt::NoBrush);
} else {
painter.setPen(Qt::NoPen);
painter.setBrush(lineColor);
}
painter.drawPolygon(scene.lane_line_vertices[i]);
}
if (outlineOnly) painter.setPen(Qt::NoPen);
// road edges
for (int i = 0; i < std::size(scene.road_edge_vertices); ++i) {
painter.setBrush(QColor::fromRgbF(1.0, 0, 0, std::clamp<float>(1.0 - scene.road_edge_stds[i], 0.0, 1.0)));
QColor edgeCol = QColor::fromRgbF(1.0, 0, 0, std::clamp<float>(1.0 - scene.road_edge_stds[i], 0.0, 1.0));
if (outlineOnly) {
painter.setPen(QPen(edgeCol, 2));
painter.setBrush(Qt::NoBrush);
} else {
painter.setPen(Qt::NoPen);
painter.setBrush(edgeCol);
}
painter.drawPolygon(scene.road_edge_vertices[i]);
}
if (outlineOnly) painter.setPen(Qt::NoPen);
// paint center lane path
// QColor bg_colors[CHANGE_LANE_PATH_COLOR];
@@ -674,12 +690,20 @@ void AnnotatedCameraWidget::drawLaneLines(QPainter &painter, const UIState *s) {
path_gradient.setColorAt(1.0, QColor(center_lane_color.red(), center_lane_color.green(), center_lane_color.blue(), static_cast<int>(CENTER_LANE_ALPHA * 255 * 0.0)));
}
painter.setBrush(path_gradient);
if (outlineOnly) {
painter.setPen(QPen(QColor(center_lane_color.red(), center_lane_color.green(),
center_lane_color.blue(), 180), 2));
painter.setBrush(Qt::NoBrush);
} else {
painter.setPen(Qt::NoPen);
painter.setBrush(path_gradient);
}
painter.drawPolygon(scene.track_vertices);
if (outlineOnly) painter.setPen(Qt::NoPen);
}
// Paint path edges ,Use current background color
if (edgeColor != bg_colors[STATUS_DISENGAGED]) {
if (edgeColor != bg_colors[STATUS_DISENGAGED] && !outlineOnly) {
QLinearGradient edge_gradient;
edge_gradient.setColorAt(0.0, QColor(edgeColor.red(), edgeColor.green(), edgeColor.blue(), static_cast<int>(255)));
edge_gradient.setColorAt(0.5, QColor(edgeColor.red(), edgeColor.green(), edgeColor.blue(), static_cast<int>(255 * 0.8) ));
@@ -695,18 +719,24 @@ void AnnotatedCameraWidget::drawLaneLines(QPainter &painter, const UIState *s) {
// Paint blindspot path
if (scene.blind_spot_path) {
QLinearGradient bs(0, height(), 0, 0);
bs.setColorAt(0.0, QColor::fromHslF(0 / 360., 0.75, 0.50, 0.6));
bs.setColorAt(0.5, QColor::fromHslF(0 / 360., 0.75, 0.50, 0.4));
bs.setColorAt(1.0, QColor::fromHslF(0 / 360., 0.75, 0.50, 0.2));
painter.setBrush(bs);
QColor bsColor = QColor::fromHslF(0 / 360., 0.75, 0.50, 0.6);
if (outlineOnly) {
painter.setPen(QPen(bsColor, 2));
painter.setBrush(Qt::NoBrush);
} else {
QLinearGradient bs(0, height(), 0, 0);
bs.setColorAt(0.0, QColor::fromHslF(0 / 360., 0.75, 0.50, 0.6));
bs.setColorAt(0.5, QColor::fromHslF(0 / 360., 0.75, 0.50, 0.4));
bs.setColorAt(1.0, QColor::fromHslF(0 / 360., 0.75, 0.50, 0.2));
painter.setBrush(bs);
}
if (blindSpotLeft) {
painter.drawPolygon(scene.track_adjacent_vertices[4]);
}
if (blindSpotRight) {
painter.drawPolygon(scene.track_adjacent_vertices[5]);
}
if (outlineOnly) painter.setPen(Qt::NoPen);
}
// Paint adjacent lane paths
@@ -717,16 +747,19 @@ void AnnotatedCameraWidget::drawLaneLines(QPainter &painter, const UIState *s) {
float maxLaneWidth = laneDetectionWidth * 1.5;
auto paintLane = [=](QPainter &painter, const QPolygonF &lane, float laneWidth, bool blindspot) {
QLinearGradient al(0, height(), 0, 0);
bool redPath = laneWidth < minLaneWidth || laneWidth > maxLaneWidth || blindspot;
float hue = redPath ? 0.0 : 120.0 * (laneWidth - minLaneWidth) / (maxLaneWidth - minLaneWidth);
al.setColorAt(0.0, QColor::fromHslF(hue / 360.0, 0.75, 0.50, 0.6));
al.setColorAt(0.5, QColor::fromHslF(hue / 360.0, 0.75, 0.50, 0.4));
al.setColorAt(1.0, QColor::fromHslF(hue / 360.0, 0.75, 0.50, 0.2));
painter.setBrush(al);
if (outlineOnly) {
painter.setPen(QPen(QColor::fromHslF(hue / 360.0, 0.75, 0.50, 0.6), 2));
painter.setBrush(Qt::NoBrush);
} else {
QLinearGradient al(0, height(), 0, 0);
al.setColorAt(0.0, QColor::fromHslF(hue / 360.0, 0.75, 0.50, 0.6));
al.setColorAt(0.5, QColor::fromHslF(hue / 360.0, 0.75, 0.50, 0.4));
al.setColorAt(1.0, QColor::fromHslF(hue / 360.0, 0.75, 0.50, 0.2));
painter.setBrush(al);
}
painter.drawPolygon(lane);
painter.setFont(InterFont(30, QFont::DemiBold));
@@ -814,6 +847,10 @@ void AnnotatedCameraWidget::paintEvent(QPaintEvent *event) {
const cereal::ModelDataV2::Reader &model = sm["modelV2"].getModelV2();
const float v_ego = sm["carState"].getCarState().getVEgo();
// CLEARPILOT: read display mode early — needed for camera suppression
displayMode = paramsMemory.getInt("ScreenDisplayMode");
nightriderMode = (displayMode == 1 || displayMode == 4);
// draw camera frame
{
std::lock_guard lk(frame_lock);
@@ -855,8 +892,14 @@ void AnnotatedCameraWidget::paintEvent(QPaintEvent *event) {
CameraWidget::updateCalibration(DEFAULT_CALIBRATION);
}
painter.beginNativePainting();
CameraWidget::setFrameId(model.getFrameId());
CameraWidget::paintGL();
if (nightriderMode) {
// CLEARPILOT: black background, no camera feed
glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
} else {
CameraWidget::setFrameId(model.getFrameId());
CameraWidget::paintGL();
}
painter.endNativePainting();
}

View File

@@ -56,6 +56,8 @@ private:
QPixmap dm_img;
float speed;
bool has_gps_speed = false;
bool nightriderMode = false;
int displayMode = 0;
QString speedUnit;
float setSpeed;
float speedLimit;