diff --git a/CHANGELOGS.md b/CHANGELOGS.md index a69991c15f..15c383ec40 100644 --- a/CHANGELOGS.md +++ b/CHANGELOGS.md @@ -10,6 +10,9 @@ sunnypilot - 0.9.2.3 (2023-06-xx) * NEW❗: Fleet Manager PIN Requirement toggle * User can now enable or disable PIN requirement on the comma device before accessing Fleet Manager * NEW❗: Reset all sunnypilot settings toggle +* NEW❗: Turn signals display on screen when blinker is used + * Green: Blinker is on + * Red: Blinker is on, car detected in the adjacent blind spot or road edge detected * IMPROVED: mapd: better exceptions handling when loading dependencies * UPDATED: Green Traffic Light Chime no longer displays an orange border when executed * FIXED: Ram HD (2500/3500): Ignore paramsd sanity check diff --git a/selfdrive/ui/qt/onroad.cc b/selfdrive/ui/qt/onroad.cc index ad3cb12034..7226ff1b5c 100644 --- a/selfdrive/ui/qt/onroad.cc +++ b/selfdrive/ui/qt/onroad.cc @@ -478,6 +478,10 @@ void AnnotatedCameraWidget::updateState(const UIState &s) { setProperty("btnPerc", s.scene.sleep_btn_opacity * 0.05); + setProperty("left_blinker", sm["carState"].getCarState().getLeftBlinker()); + setProperty("right_blinker", sm["carState"].getCarState().getRightBlinker()); + setProperty("lane_change_edge_block", sm["lateralPlan"].getLateralPlan().getLaneChangeEdgeBlock()); + // update engageability/experimental mode button experimental_btn->updateState(s); @@ -1414,6 +1418,131 @@ void AnnotatedCameraWidget::drawE2eStatus(QPainter &p, int x, int y, int w, int p.drawEllipse(e2eStatusIcon); } +void AnnotatedCameraWidget::drawLeftTurnSignal(QPainter &painter, int x, int y, int state) { + painter.setRenderHint(QPainter::Antialiasing, true); + + QColor circle_color, circle_color_0, circle_color_1; + QColor arrow_color, arrow_color_0, arrow_color_1; + if ((left_blindspot || lane_change_edge_block) && !(left_blinker && right_blinker)) { + circle_color_0 = QColor(164, 0, 1); + circle_color_1 = QColor(204, 0, 1); + arrow_color_0 = QColor(72, 1, 1); + arrow_color_1 = QColor(255, 255, 255); + } else { + circle_color_0 = QColor(22, 156, 69); + circle_color_1 = QColor(30, 215, 96); + arrow_color_0 = QColor(9, 56, 27); + arrow_color_1 = QColor(255, 255, 255); + } + + if (state == 1) { + circle_color = circle_color_1; + arrow_color = arrow_color_1; + } else if (state == 0) { + circle_color = circle_color_0; + arrow_color = arrow_color_0; + } + + // Draw the circle + int circleSize = 120; + int circleX = x; + int circleY = y; + painter.setPen(Qt::NoPen); + painter.setBrush(circle_color); + painter.drawEllipse(circleX, circleY, circleSize, circleSize); + + // Draw the arrow + int arrowSize = 50; + int arrowX = circleX + (circleSize - arrowSize) / 4; + int arrowY = circleY + (circleSize - arrowSize) / 2; + painter.setPen(Qt::NoPen); + painter.setBrush(arrow_color); + + // Draw the arrow shape + QPolygon arrowPolygon; + arrowPolygon << QPoint(arrowX + 10, arrowY + arrowSize / 2) + << QPoint(arrowX + arrowSize - 3, arrowY) + << QPoint(arrowX + arrowSize, arrowY) + << QPoint(arrowX + arrowSize, arrowY + arrowSize) + << QPoint(arrowX + arrowSize - 3, arrowY + arrowSize) + << QPoint(arrowX + 10, arrowY + arrowSize / 2); + painter.drawPolygon(arrowPolygon); + + // Draw the tail rectangle + int tailWidth = arrowSize / 2.25; + int tailHeight = arrowSize / 2; + QRect tailRect(arrowX + arrowSize - 3, arrowY + arrowSize / 4, tailWidth, tailHeight); + painter.fillRect(tailRect, arrow_color); +} + +void AnnotatedCameraWidget::drawRightTurnSignal(QPainter &painter, int x, int y, int state) { + painter.setRenderHint(QPainter::Antialiasing, true); + + QColor circle_color, circle_color_0, circle_color_1; + QColor arrow_color, arrow_color_0, arrow_color_1; + if ((right_blindspot || lane_change_edge_block) && !(left_blinker && right_blinker)) { + circle_color_0 = QColor(164, 0, 1); + circle_color_1 = QColor(204, 0, 1); + arrow_color_0 = QColor(72, 1, 1); + arrow_color_1 = QColor(255, 255, 255); + } else { + circle_color_0 = QColor(22, 156, 69); + circle_color_1 = QColor(30, 215, 96); + arrow_color_0 = QColor(9, 56, 27); + arrow_color_1 = QColor(255, 255, 255); + } + + if (state == 1) { + circle_color = circle_color_1; + arrow_color = arrow_color_1; + } else if (state == 0) { + circle_color = circle_color_0; + arrow_color = arrow_color_0; + } + + + // Draw the circle + int circleSize = 120; + int circleX = x; + int circleY = y; + painter.setPen(Qt::NoPen); + painter.setBrush(circle_color); + painter.drawEllipse(circleX, circleY, circleSize, circleSize); + + // Draw the arrow + int arrowSize = 50; + int arrowX = circleX + (circleSize - arrowSize) / 2 + (arrowSize / 2.5) - 3; + int arrowY = circleY + (circleSize - arrowSize) / 2; + painter.setPen(Qt::NoPen); + painter.setBrush(arrow_color); + + // Draw the arrow shape + QPolygon arrowPolygon; + arrowPolygon << QPoint(arrowX + arrowSize - 10, arrowY + arrowSize / 2) + << QPoint(arrowX + 3, arrowY) + << QPoint(arrowX, arrowY) + << QPoint(arrowX, arrowY + arrowSize) + << QPoint(arrowX + 3, arrowY + arrowSize) + << QPoint(arrowX + arrowSize - 10, arrowY + arrowSize / 2); + painter.drawPolygon(arrowPolygon); + + // Draw the tail rectangle + int tailWidth = arrowSize / 2.25; + int tailHeight = arrowSize / 2; + QRect tailRect(arrowX - tailWidth + 3, arrowY + arrowSize / 4, tailWidth, tailHeight); + painter.fillRect(tailRect, arrow_color); +} + +int AnnotatedCameraWidget::blinkerPulse(int frame) { + if (frame % UI_FREQ < (UI_FREQ / 2)) { + blinker_state = 1; + } else { + blinker_state = 0; + } + + return blinker_state; +} + void AnnotatedCameraWidget::initializeGL() { CameraWidget::initializeGL(); @@ -1794,6 +1923,19 @@ void AnnotatedCameraWidget::paintEvent(QPaintEvent *event) { drawHud(painter); + if (left_blinker || right_blinker) { + blinker_frame++; + int state = blinkerPulse(blinker_frame); + if (left_blinker) { + drawLeftTurnSignal(painter, rect().center().x() - 300, 90, state); + } + if (right_blinker) { + drawRightTurnSignal(painter, rect().center().x() + 180, 90, state); + } + } else { + blinker_frame = 0; + } + double cur_draw_t = millis_since_boot(); double dt = cur_draw_t - prev_draw_t; double fps = fps_filter.update(1. / dt * 1000); diff --git a/selfdrive/ui/qt/onroad.h b/selfdrive/ui/qt/onroad.h index 303ed6c8a7..7ca45aaa61 100644 --- a/selfdrive/ui/qt/onroad.h +++ b/selfdrive/ui/qt/onroad.h @@ -144,6 +144,10 @@ class AnnotatedCameraWidget : public CameraWidget { Q_PROPERTY(int e2eState MEMBER e2eState); Q_PROPERTY(int e2eStatus MEMBER e2eStatus); + Q_PROPERTY(bool left_blinker MEMBER left_blinker); + Q_PROPERTY(bool right_blinker MEMBER right_blinker); + Q_PROPERTY(bool lane_change_edge_block MEMBER lane_change_edge_block); + public: explicit AnnotatedCameraWidget(VisionStreamType type, QWidget* parent = 0); void updateState(const UIState &s); @@ -175,6 +179,10 @@ private: void drawE2eStatus(QPainter &p, int x, int y, int w, int h, int e2e_long_status); + void drawLeftTurnSignal(QPainter &painter, int x, int y, int state); + void drawRightTurnSignal(QPainter &painter, int x, int y, int state); + int blinkerPulse(int frame); + uint64_t last_update_params; ExperimentalButton *experimental_btn; @@ -280,6 +288,10 @@ private: int e2eState; int e2eStatus; + bool left_blinker, right_blinker, lane_change_edge_block; + int blinker_frame; + int blinker_state = 0; + protected: void paintGL() override; void initializeGL() override;