Compare commits

...

129 Commits

Author SHA1 Message Date
royjr
672053db65 Update display_panel.cc 2025-11-08 09:57:57 -08:00
royjr
774710e95e Squashed commit of the following:
commit 669f3b7945
Author: royjr <royjr96@gmail.com>
Date:   Fri Nov 7 21:51:35 2025 -0500

    fix

commit 63122e1a33
Merge: c9b1afb15 c1d3ae427
Author: royjr <royjr96@gmail.com>
Date:   Fri Nov 7 21:25:46 2025 -0500

    Merge branch 'master' into visual-style

commit c9b1afb154
Merge: 6e3bd3fbe 9b92cdd2c
Author: royjr <royjr96@gmail.com>
Date:   Fri Oct 10 00:06:55 2025 -0400

    Merge branch 'master' into visual-style

commit 6e3bd3fbed
Author: royjr <royjr96@gmail.com>
Date:   Thu Oct 9 23:56:37 2025 -0400

    explicit radius

commit 42592dd550
Author: royjr <royjr96@gmail.com>
Date:   Thu Oct 9 23:54:34 2025 -0400

    match what we currently send

commit b2f7d72a33
Author: royjr <royjr96@gmail.com>
Date:   Thu Oct 9 23:53:07 2025 -0400

    no need

commit 2e0ce18c84
Author: royjr <royjr96@gmail.com>
Date:   Thu Oct 9 23:52:51 2025 -0400

    group

commit 2a9a4a9263
Merge: e63fb10fd d6317ffd2
Author: royjr <royjr96@gmail.com>
Date:   Thu Oct 9 23:51:18 2025 -0400

    Merge branch 'master' into visual-style

commit e63fb10fdb
Author: royjr <royjr96@gmail.com>
Date:   Wed Oct 8 21:35:23 2025 -0400

    Revert "metric threshold"

    This reverts commit b54941928d.

commit b54941928d
Author: royjr <royjr96@gmail.com>
Date:   Wed Oct 8 21:35:15 2025 -0400

    metric threshold

commit b85b8ffacf
Author: royjr <royjr96@gmail.com>
Date:   Wed Oct 8 21:28:10 2025 -0400

    better VisualStyleOverheadThreshold

commit 3ff2e9b26a
Author: royjr <royjr96@gmail.com>
Date:   Wed Oct 8 21:25:37 2025 -0400

    reorder

commit 4b3ffc722a
Author: royjr <royjr96@gmail.com>
Date:   Wed Oct 8 21:24:52 2025 -0400

    show visual_radar_tracks_delay_settings on VisualRadarTracks

commit 5f49066829
Author: royjr <royjr96@gmail.com>
Date:   Wed Oct 8 21:18:51 2025 -0400

    more

commit ab2eb218d5
Author: royjr <royjr96@gmail.com>
Date:   Wed Oct 8 21:16:06 2025 -0400

    clean

commit 6212b174e9
Author: royjr <royjr96@gmail.com>
Date:   Wed Oct 8 21:12:08 2025 -0400

    descs

commit a8a6e5708a
Merge: 54b060f17 ae21d40a1
Author: royjr <royjr96@gmail.com>
Date:   Wed Oct 8 20:47:03 2025 -0400

    Merge branch 'master' into visual-style

commit 54b060f178
Author: royjr <royjr96@gmail.com>
Date:   Wed Oct 8 20:45:17 2025 -0400

    move with

commit 8266386cd0
Author: royjr <royjr96@gmail.com>
Date:   Wed Oct 8 20:44:06 2025 -0400

    better

commit 8bbe87ee22
Author: royjr <royjr96@gmail.com>
Date:   Wed Oct 8 20:40:23 2025 -0400

    simple

commit d35ac0c145
Author: royjr <royjr96@gmail.com>
Date:   Wed Oct 8 20:34:26 2025 -0400

    a bit better

commit a134ae1e29
Author: royjr <royjr96@gmail.com>
Date:   Wed Oct 8 20:25:55 2025 -0400

    combine

commit 6a69759b9e
Author: royjr <royjr96@gmail.com>
Date:   Wed Oct 8 20:24:51 2025 -0400

    hide options based on options

commit b9063e2966
Author: royjr <royjr96@gmail.com>
Date:   Wed Oct 8 19:55:40 2025 -0400

    cleanup

commit 4397a4387a
Author: royjr <royjr96@gmail.com>
Date:   Wed Oct 8 19:49:14 2025 -0400

    mooooore fps

commit 32dc384524
Author: royjr <royjr96@gmail.com>
Date:   Wed Oct 8 19:47:08 2025 -0400

    not needed for now

commit 68fa239b97
Author: royjr <royjr96@gmail.com>
Date:   Wed Oct 8 19:45:37 2025 -0400

    unused

commit c8367fbc25
Author: royjr <royjr96@gmail.com>
Date:   Wed Oct 8 19:44:45 2025 -0400

    more fps remove

commit 76fc4514e1
Author: royjr <royjr96@gmail.com>
Date:   Wed Oct 8 19:43:32 2025 -0400

    reorder

commit 073ce2b4df
Author: royjr <royjr96@gmail.com>
Date:   Wed Oct 8 19:35:37 2025 -0400

    remove VisualFPS

commit 5d516ba89f
Merge: e819a0dcb 014baf8e9
Author: royjr <royjr96@gmail.com>
Date:   Wed Oct 8 12:26:38 2025 -0400

    Merge branch 'master' into visual-style

commit e819a0dcb1
Merge: ba4b583e6 8050c56a4
Author: royjr <royjr96@gmail.com>
Date:   Wed Oct 8 02:14:22 2025 -0400

    Merge branch 'master' into visual-style

commit ba4b583e6e
Author: royjr <royjr96@gmail.com>
Date:   Wed Oct 8 01:55:38 2025 -0400

    fix visual_style_overhead_zoom

commit 5954354356
Author: royjr <royjr96@gmail.com>
Date:   Wed Oct 8 01:48:06 2025 -0400

    fix visual_style_overhead

commit 23ff232333
Author: royjr <royjr96@gmail.com>
Date:   Wed Oct 8 01:40:10 2025 -0400

    fix visual_style_overhead_settings

commit 0bb47fcfa9
Author: royjr <royjr96@gmail.com>
Date:   Wed Oct 8 01:29:15 2025 -0400

    fix visual_style_overhead_threshold_settings

commit 99a5682371
Author: royjr <royjr96@gmail.com>
Date:   Wed Oct 8 01:09:08 2025 -0400

    todo

commit 22ca343050
Author: royjr <royjr96@gmail.com>
Date:   Wed Oct 8 01:07:53 2025 -0400

    fix visual_style_overhead_threshold_settings

commit 0049d20151
Author: royjr <royjr96@gmail.com>
Date:   Wed Oct 8 01:05:17 2025 -0400

    VisualStyleZoom fix

commit b9f8f4e8ac
Author: royjr <royjr96@gmail.com>
Date:   Wed Oct 8 00:55:20 2025 -0400

    visual style better

commit 26564dd42f
Author: royjr <royjr96@gmail.com>
Date:   Wed Oct 8 00:07:31 2025 -0400

    better VisualWideCam

commit ec440e4568
Merge: 69817f887 408d52d72
Author: royjr <royjr96@gmail.com>
Date:   Tue Oct 7 22:38:57 2025 -0400

    Merge branch 'master' into visual-style

commit 69817f887b
Merge: df21208b7 0f4828df8
Author: royjr <royjr96@gmail.com>
Date:   Tue Sep 30 15:01:21 2025 -0400

    Merge branch 'master' into visual-style

commit df21208b7c
Merge: 660c994c5 082ea8119
Author: royjr <royjr96@gmail.com>
Date:   Thu Sep 25 02:08:38 2025 -0400

    Merge branch 'master' into visual-style

commit 660c994c5e
Author: royjr <royjr96@gmail.com>
Date:   Thu Sep 25 02:08:28 2025 -0400

    visual_style_blend  vs visual_style_overhead_blend

commit 904cc796b0
Author: royjr <royjr96@gmail.com>
Date:   Thu Sep 25 01:44:06 2025 -0400

    prevent jitter

commit 1a9a1e1b8a
Merge: 2ae7078c0 1465e38c7
Author: royjr <royjr96@gmail.com>
Date:   Tue Sep 23 23:00:46 2025 -0400

    Merge branch 'master' into visual-style

commit 2ae7078c0f
Author: royjr <royjr96@gmail.com>
Date:   Tue Sep 23 14:37:21 2025 -0400

    use ParamWatcher

commit 0fde830a30
Author: royjr <royjr96@gmail.com>
Date:   Tue Sep 23 10:35:46 2025 -0400

    fix params

commit 7c744a42e5
Author: royjr <royjr96@gmail.com>
Date:   Tue Sep 23 08:18:51 2025 -0400

    safe font

commit 90cc169dec
Author: royjr <royjr96@gmail.com>
Date:   Tue Sep 23 08:17:59 2025 -0400

    VisualRadarTracksDelay

commit 36c1e11a9e
Author: royjr <royjr96@gmail.com>
Date:   Tue Sep 23 07:47:16 2025 -0400

    add FPS toggle

commit 5e26a99337
Author: royjr <royjr96@gmail.com>
Date:   Tue Sep 23 07:40:59 2025 -0400

    better fps

commit 53683cf90b
Author: royjr <royjr96@gmail.com>
Date:   Tue Sep 23 07:25:00 2025 -0400

    constant track size

commit 95dcc23887
Author: royjr <royjr96@gmail.com>
Date:   Tue Sep 23 07:21:57 2025 -0400

    better tracks for overhead

commit cef3163c7c
Author: royjr <royjr96@gmail.com>
Date:   Tue Sep 23 06:54:46 2025 -0400

    more more more cached params

commit cbc34dd1f6
Author: royjr <royjr96@gmail.com>
Date:   Tue Sep 23 06:49:41 2025 -0400

    more cached params

commit fbd1f6bad1
Author: royjr <royjr96@gmail.com>
Date:   Tue Sep 23 06:42:14 2025 -0400

    cached params

commit f2ddf9abba
Author: royjr <royjr96@gmail.com>
Date:   Tue Sep 23 04:04:52 2025 -0400

    debug ui lag

commit 8a5eaf5ba6
Author: royjr <royjr96@gmail.com>
Date:   Tue Sep 23 03:35:06 2025 -0400

    prepare for lag compensation

commit ddb377ac5b
Author: royjr <royjr96@gmail.com>
Date:   Tue Sep 23 03:24:15 2025 -0400

    radar lag compensate

commit 512a01d28c
Author: royjr <royjr96@gmail.com>
Date:   Tue Sep 23 01:33:50 2025 -0400

    VisualWideCam toggle (untested)

commit b30e275169
Merge: ee9fe5c9e ecee67dd6
Author: royjr <royjr96@gmail.com>
Date:   Tue Sep 23 01:15:18 2025 -0400

    Merge branch 'master' into visual-style

commit ee9fe5c9ed
Author: royjr <royjr96@gmail.com>
Date:   Tue Sep 23 01:10:44 2025 -0400

    VisualRadarTracks toggle

commit e672a352d3
Author: royjr <royjr96@gmail.com>
Date:   Tue Sep 23 00:24:38 2025 -0400

    bigger points

commit 361d107040
Author: royjr <royjr96@gmail.com>
Date:   Mon Sep 22 23:55:55 2025 -0400

    basic radar

commit 36eb047cd3
Merge: 218c6172e d5a873ed8
Author: royjr <royjr96@gmail.com>
Date:   Mon Sep 22 00:10:31 2025 -0400

    Merge branch 'master' into visual-style

commit 218c6172e6
Author: royjr <royjr96@gmail.com>
Date:   Mon Sep 22 00:03:29 2025 -0400

    darker fills

commit 8f35e4fc3c
Author: royjr <royjr96@gmail.com>
Date:   Sun Sep 21 23:02:48 2025 -0400

    Revert "smooooth"

    This reverts commit c965df39d6.

commit c965df39d6
Author: royjr <royjr96@gmail.com>
Date:   Sun Sep 21 22:10:35 2025 -0400

    smooooth

commit 53ef69f3c3
Author: royjr <royjr96@gmail.com>
Date:   Sun Sep 21 21:55:23 2025 -0400

    hide horizon if no data

commit ea9ca18c8b
Author: royjr <royjr96@gmail.com>
Date:   Sun Sep 21 21:49:01 2025 -0400

    horizon at end

commit 225858261e
Author: royjr <royjr96@gmail.com>
Date:   Sun Sep 21 21:34:14 2025 -0400

    better horizon

commit 43de43b34a
Author: royjr <royjr96@gmail.com>
Date:   Sun Sep 21 21:25:39 2025 -0400

    better lines

commit 96ddbe35a1
Author: royjr <royjr96@gmail.com>
Date:   Sun Sep 21 15:37:54 2025 -0400

    dynamically adjust background

commit 7d547ad533
Author: royjr <royjr96@gmail.com>
Date:   Sun Sep 21 15:30:13 2025 -0400

    fix default

commit 13de58b845
Author: royjr <royjr96@gmail.com>
Date:   Sun Sep 21 15:30:06 2025 -0400

    add more speeds

commit 3d8c563a4b
Author: royjr <royjr96@gmail.com>
Date:   Sun Sep 21 15:19:57 2025 -0400

    dynamic zoom

commit bc75199d5a
Author: royjr <royjr96@gmail.com>
Date:   Sun Sep 21 14:34:38 2025 -0400

    overhead

commit ba6e18ed91
Author: royjr <royjr96@gmail.com>
Date:   Sun Sep 21 14:32:30 2025 -0400

    only for vision

commit 74dbcd699b
Author: royjr <royjr96@gmail.com>
Date:   Sun Sep 21 14:31:58 2025 -0400

    add road edges to vision

commit 35c6af0190
Author: royjr <royjr96@gmail.com>
Date:   Sun Sep 21 13:55:56 2025 -0400

    theme

commit 827de88c8b
Author: royjr <royjr96@gmail.com>
Date:   Sun Sep 21 13:03:37 2025 -0400

    stump top down

commit 54ca4b537d
Author: royjr <royjr96@gmail.com>
Date:   Sun Sep 21 12:48:32 2025 -0400

    VisualStyleBlendThreshold

commit c61f327076
Author: royjr <royjr96@gmail.com>
Date:   Sat Sep 20 13:04:07 2025 -0400

    allow always overhead view

commit d569913e5a
Author: royjr <royjr96@gmail.com>
Date:   Sat Sep 20 12:57:17 2025 -0400

    VisualStyleBlend

commit 418c93be06
Author: royjr <royjr96@gmail.com>
Date:   Sat Sep 20 12:16:49 2025 -0400

    animate overhead

commit 5acc040a89
Author: royjr <royjr96@gmail.com>
Date:   Sat Sep 20 11:24:24 2025 -0400

    overhead

commit e45b17c230
Author: royjr <royjr96@gmail.com>
Date:   Sat Sep 20 03:00:00 2025 -0400

    better

commit b14b6246d7
Author: royjr <royjr96@gmail.com>
Date:   Sat Sep 20 00:31:07 2025 -0400

    visual style init
2025-11-07 22:11:17 -05:00
royjr
8cdc236585 Merge branch 'master' into nav-commacon-visual-style 2025-11-07 22:10:20 -05:00
discountchubbs
ec031ffeb6 n overlap and more code diff! 2025-11-07 17:59:23 -08:00
discountchubbs
9c5ee2e12a start cleanup 2025-11-07 12:37:22 -08:00
discountchubbs
65386da6d4 yesssss 2025-11-07 09:34:52 -08:00
discountchubbs
00799154ef merge origin/nav-desires into nav-commacon 2025-11-07 07:47:16 -08:00
discountchubbs
d6731c30da play with moving stuff when not valid 2025-11-07 07:05:24 -08:00
discountchubbs
0b54b86476 rounding 2025-11-06 15:47:31 -08:00
discountchubbs
fd675d29a9 no more nav-events 2025-11-06 12:25:27 -08:00
discountchubbs
d58bbda789 merge origin/nav-events into nav-commacon 2025-11-05 19:28:38 -08:00
discountchubbs
50773a62ed fine 2025-11-05 17:47:16 -08:00
discountchubbs
f5dbdc192e YOUR DESTINATION IS ON THE RIGHT 2025-11-05 15:28:52 -08:00
discountchubbs
f9987b2d7d quick don't look 2025-11-05 14:37:44 -08:00
discountchubbs
d51af1513b add to navigationd-service , but add here first because yessss 2025-11-05 14:30:02 -08:00
discountchubbs
2807c949a9 nayan! 2025-11-05 09:58:18 -08:00
nayan
0268d4384d Move Elements 2025-11-05 11:27:49 -05:00
nayan
0e60a56e56 Update idx to allow next maneuver 2025-11-05 11:02:21 -05:00
nayan
c5445d2a8b Make it bigger. BIGGER! 2025-11-05 11:02:21 -05:00
discountchubbs
6df1edbbaf offroad panel stuffs 2025-11-05 06:57:21 -08:00
discountchubbs
a098a0805a fix index access 2025-11-05 06:08:31 -08:00
nayan
469f1e01ef LOL, keep these 2025-11-04 16:28:23 -05:00
nayan
0fc5414f7c NAV DIRECTIONS UI 2025-11-04 16:22:37 -05:00
James Vecellio
d798288b2c more 2025-11-03 06:54:03 -08:00
James Vecellio
0e02fc2449 commacon stuff 2025-11-02 18:53:53 -08:00
James Vecellio-Grant
7d15afe5bc Merge branch 'nav-desires' into nav-events 2025-11-01 08:02:31 -07:00
James Vecellio-Grant
b6dd2d14db Merge branch 'navigationd-service' into nav-desires 2025-11-01 08:02:18 -07:00
James Vecellio-Grant
7d4e5bedaf Merge branch 'navigationd-init' into navigationd-service 2025-11-01 08:02:08 -07:00
James Vecellio-Grant
1063114408 Merge branch 'master' into navigationd-init 2025-11-01 08:01:57 -07:00
discountchubbs
958b4df69f give slightly more leniancy for offline routing 2025-11-01 08:01:30 -07:00
discountchubbs
f5953c5d8c Quarter mile is what apple maps uses 2025-11-01 07:57:55 -07:00
discountchubbs
72998034e6 copyright 2025-10-30 06:26:01 -07:00
discountchubbs
cefb344183 copyright 2025-10-30 06:22:34 -07:00
discountchubbs
c0da31abb6 mild clean 2025-10-30 06:18:47 -07:00
discountchubbs
bd759a56cf ehh no, 50 ft increments is better 2025-10-29 20:05:16 -07:00
discountchubbs
befc73c53e more 2025-10-29 20:03:19 -07:00
discountchubbs
8dbfc267ac Merge remote-tracking branch 'origin/nav-desires' into nav-events 2025-10-29 19:41:51 -07:00
discountchubbs
d17e80ad94 Merge remote-tracking branch 'origin/navigationd-service' into nav-desires 2025-10-29 19:41:29 -07:00
discountchubbs
c2b7087723 Merge remote-tracking branch 'origin/navigationd-init' into navigationd-service 2025-10-29 19:40:19 -07:00
James Vecellio-Grant
81b37712f1 Merge branch 'master' into navigationd-init 2025-10-29 19:39:41 -07:00
discountchubbs
68270a13a3 Merge remote-tracking branch 'origin/nav-desires' into nav-events 2025-10-29 19:38:20 -07:00
discountchubbs
18cd3633e5 Merge remote-tracking branch 'origin/navigationd-service' into nav-desires 2025-10-29 19:37:41 -07:00
discountchubbs
9c6a4d4a57 Merge remote-tracking branch 'origin/navigationd-init' into navigationd-service 2025-10-29 19:37:10 -07:00
discountchubbs
1a4c48249b fix: handle empty maxspeed list in nav_instructions 2025-10-29 19:36:39 -07:00
James Vecellio-Grant
95d887a417 Update event_builder.py 2025-10-29 18:11:22 -07:00
James Vecellio-Grant
e297b4c03f Update event_builder.py 2025-10-29 18:06:28 -07:00
discountchubbs
1132377837 refactor event_builder into class with staticmethod for events.py call 2025-10-29 15:42:23 -07:00
discountchubbs
35f03ae001 Well im conflicted 2025-10-28 11:52:32 -07:00
discountchubbs
1c0b54a447 more 2025-10-28 06:55:28 -07:00
discountchubbs
8f0cdd514e weird 2025-10-28 06:54:50 -07:00
discountchubbs
3681caa717 Add nudge notif to event builder 2025-10-28 06:46:39 -07:00
discountchubbs
7446c43f69 Merge remote-tracking branch 'origin/nav-desires' into nav-events 2025-10-28 06:44:01 -07:00
discountchubbs
5f5e3668eb Add steering pressed and torque to desire for non blindspot cars, and a note! 2025-10-28 06:43:00 -07:00
discountchubbs
8c07958f6f Merge remote-tracking branch 'origin/navigationd-service' into nav-desires 2025-10-28 06:28:33 -07:00
discountchubbs
ca1ce9bcc9 Clear route cache more gracefully to allow we based route connection to happen quick ish 2025-10-28 06:27:51 -07:00
discountchubbs
34a0819bc5 Read the description:
It's ideal that we should always use the next instruction ahead instead of our current instruction that we are driving on. Sure, current instruction is great for grepping maxspeed tags, but for upcoming instructions we want to use index 1.

I updated the test to reflect the two indexes.
2025-10-28 06:23:40 -07:00
James Vecellio-Grant
c68ea82a5d hahaha it’ll fail the test but that’s a later problem 2025-10-27 19:27:37 -07:00
discountchubbs
3157054100 a bit more readible 2025-10-26 13:02:09 -07:00
discountchubbs
2486ef1825 Merge remote-tracking branch 'origin/nav-desires' into nav-events 2025-10-26 11:07:49 -07:00
discountchubbs
29f15dc8ed Merge remote-tracking branch 'origin/navigationd-service' into nav-desires 2025-10-26 11:07:16 -07:00
discountchubbs
31a5a3b3c0 assertion comparison operators 2025-10-26 11:06:00 -07:00
discountchubbs
d8fa3cfd04 Merge remote-tracking branch 'origin/nav-desires' into nav-events 2025-10-26 08:45:24 -07:00
discountchubbs
2a4b348497 Merge remote-tracking branch 'origin/navigationd-service' into nav-desires 2025-10-26 08:44:38 -07:00
discountchubbs
3ef3aceb4b Apply auto cancel route after 10 seconds off route. 2025-10-26 08:44:03 -07:00
James Vecellio-Grant
3d8763b3ce Merge branch 'master' into navigationd-init 2025-10-25 21:14:39 -07:00
James Vecellio-Grant
b2427a5f20 Merge branch 'master' into navigationd-init 2025-10-25 16:27:44 -07:00
discountchubbs
7ddafe62cd Merge remote-tracking branch 'origin/nav-desires' into nav-events 2025-10-23 19:21:25 -07:00
discountchubbs
ff4cc96a81 Merge remote-tracking branch 'origin/navigationd-service' into nav-desires 2025-10-23 19:21:04 -07:00
discountchubbs
3b1ada64be sync 2025-10-23 19:20:15 -07:00
discountchubbs
6a08186434 Merge remote-tracking branch 'origin/navigationd-init' into navigationd-service 2025-10-23 19:17:49 -07:00
discountchubbs
cf2b033c79 clean 2025-10-23 19:15:54 -07:00
discountchubbs
9fbef36c6b desire handling 2025-10-23 19:12:34 -07:00
discountchubbs
f5a38aa613 Add instruction field to maneuvers and update banner message logic 2025-10-23 19:08:52 -07:00
discountchubbs
25f5058430 Merge remote-tracking branch 'origin/nav-desires' into nav-events
# Conflicts:
#	sunnypilot/navd/navigation_helpers/nav_instructions.py
#	sunnypilot/navd/navigationd.py
2025-10-23 17:26:23 -07:00
discountchubbs
7b28c2f59a Merge remote-tracking branch 'origin/navigationd-service' into nav-desires 2025-10-23 17:24:16 -07:00
discountchubbs
99d954de10 sync 2025-10-23 17:23:47 -07:00
discountchubbs
b28f33481c Merge remote-tracking branch 'origin/navigationd-init' into navigationd-service 2025-10-23 17:21:56 -07:00
discountchubbs
589e33f665 sum red diff 2025-10-23 17:20:23 -07:00
discountchubbs
39342d7b5e diff is red so == good? 2025-10-23 17:16:29 -07:00
discountchubbs
fe70650f73 100 meters 2025-10-23 16:56:58 -07:00
discountchubbs
e3f9fe892a more 2025-10-23 12:21:04 -07:00
discountchubbs
f4373fa244 Revert "dead"
This reverts commit 2376802589.
2025-10-23 12:20:52 -07:00
James Vecellio
2376802589 dead 2025-10-23 09:44:28 -07:00
James Vecellio
c3b51d7335 long maneuvers fix 2025-10-23 09:35:43 -07:00
discountchubbs
d3d8802402 no mid, yet 2025-10-22 19:34:24 -07:00
discountchubbs
d866500c92 daughter wants to take a bath 2025-10-22 19:27:59 -07:00
discountchubbs
23879836d9 return validity 2025-10-22 14:26:15 -07:00
discountchubbs
06add21971 sm shenanigans 2025-10-22 13:47:03 -07:00
discountchubbs
66fd3d1a01 smh 2025-10-22 09:51:33 -07:00
James Vecellio-Grant
71f7754f51 Didn’t like it 2025-10-22 07:45:44 -07:00
James Vecellio-Grant
b5591cbd62 Update plannerd.py 2025-10-22 07:36:56 -07:00
discountchubbs
7430c450c2 longitudinal maneuvers no subscribey 2025-10-22 06:15:52 -07:00
discountchubbs
e54a39cf43 publish dem events 👀 2025-10-21 21:13:33 -07:00
discountchubbs
3afe0bcdb3 Merge remote-tracking branch 'origin/nav-desires' into nav-events 2025-10-21 16:27:46 -07:00
discountchubbs
b763f7aac1 Merge remote-tracking branch 'origin/navigationd-service' into nav-desires 2025-10-21 16:27:02 -07:00
discountchubbs
450fcd4d55 oopsie part two lol 2025-10-21 16:25:30 -07:00
James Vecellio
551b4dea31 oopsie 2025-10-21 16:20:20 -07:00
James Vecellio
bd269defb3 oopsie 2025-10-21 16:19:49 -07:00
James Vecellio
8998f63a28 oopsie 2025-10-21 16:18:27 -07:00
James Vecellio-Grant
399ed08926 Merge branch 'master' into navigationd-init 2025-10-21 15:29:45 -07:00
James Vecellio-Grant
90f02040fe Merge branch 'master' into navigationd-service 2025-10-21 15:29:42 -07:00
James Vecellio-Grant
8423ecedb1 Merge branch 'master' into nav-desires 2025-10-21 15:29:39 -07:00
James Vecellio-Grant
34d1514e11 Merge branch 'master' into nav-events 2025-10-21 15:29:35 -07:00
discountchubbs
c50d511616 Merge remote-tracking branch 'origin/nav-desires' into nav-events 2025-10-21 15:27:41 -07:00
discountchubbs
dd1479ed82 More macOS crap 🤠 2025-10-21 15:25:15 -07:00
discountchubbs
87ec262e39 Merge remote-tracking branch 'origin/nav-desires' into nav-events 2025-10-21 12:06:07 -07:00
discountchubbs
f82845ff42 Merge remote-tracking branch 'origin/navigationd-service' into nav-desires 2025-10-21 12:05:13 -07:00
discountchubbs
efcc5ccd15 Merge remote-tracking branch 'origin/navigationd-init' into navigationd-service 2025-10-21 12:03:44 -07:00
James Vecellio-Grant
6aac50ab56 Merge branch 'master' into navigationd-init 2025-10-21 12:03:05 -07:00
discountchubbs
da0920cb60 feat: navigationd onroad events 2025-10-21 11:59:13 -07:00
discountchubbs
091bce4a3a Merge remote-tracking branch 'origin/navigationd-service' into nav-desires 2025-10-21 05:54:42 -07:00
discountchubbs
088f6aa407 sync 2025-10-21 05:53:12 -07:00
James Vecellio-Grant
211c8adcce Merge branch 'master' into navigationd-init 2025-10-21 05:52:06 -07:00
discountchubbs
fe5366e5b2 rm unused import 2025-10-19 20:34:11 -07:00
discountchubbs
1ecb0b0f66 dumb 2025-10-19 20:16:36 -07:00
discountchubbs
51e455db79 stupid msgq always breaking macOS 2025-10-19 20:15:14 -07:00
discountchubbs
dc6672fa80 Merge remote-tracking branch 'origin/navigationd-init' into navigationd-service 2025-10-19 20:02:36 -07:00
discountchubbs
07b8e7783d Do i really need a readme 2025-10-19 19:47:21 -07:00
discountchubbs
f17b0f200c non blocking polling
SOME attribute protection. kids crying, so need to stop for now!
2025-10-19 17:20:13 -07:00
discountchubbs
ad9bde8b1f non blocking polling
SOME attribute protection. kids crying, so need to stop for now!
2025-10-19 17:17:37 -07:00
discountchubbs
8cf9f9fe23 params!
maybe these should be protected attributes, but thats a tomorrow problem
2025-10-19 17:03:04 -07:00
discountchubbs
713985d823 feat: navigationd desire loop
Notes: Maybe I should add a param for this, so its not automatic when a route is set.
2025-10-19 09:38:29 -07:00
James Vecellio-Grant
088f9d0b59 Merge branch 'master' into navigationd-service 2025-10-18 18:22:34 -07:00
James Vecellio-Grant
53bf5b0d41 Merge branch 'master' into navigationd-init 2025-10-18 18:22:27 -07:00
discountchubbs
8c33592628 Revert "feat: navigationd" bc it was supposed to be a branch lol
This reverts commit 3bbb33f6bd.
2025-10-18 18:18:28 -07:00
James Vecellio
3bbb33f6bd feat: navigationd
I changed the reroute counter to 9 updates, which is every 3 seconds.  compared to 3, which is one second.
2025-10-18 18:15:22 -07:00
discountchubbs
5bd9549bd1 some clean up for production 2025-10-16 15:43:34 -07:00
discountchubbs
3481702715 some suggestions applied 2025-10-16 06:55:15 -07:00
discountchubbs
c9781ee31d feat: mapbox navigation helpers 2025-10-16 06:43:48 -07:00
130 changed files with 2037 additions and 70 deletions

View File

@@ -21,11 +21,12 @@ env:
PYTHONWARNINGS: error
BASE_IMAGE: sunnypilot-base
AZURE_TOKEN: ${{ secrets.AZURE_COMMADATACI_OPENPILOTCI_TOKEN }}
MAPBOX_TOKEN_CI: ${{ secrets.MAPBOX_TOKEN_CI }}
DOCKER_LOGIN: docker login ghcr.io -u ${{ github.actor }} -p ${{ secrets.GITHUB_TOKEN }}
BUILD: release/ci/docker_build_sp.sh base
RUN: docker run --shm-size 2G -v $PWD:/tmp/openpilot -w /tmp/openpilot -e CI=1 -e PYTHONWARNINGS=error -e FILEREADER_CACHE=1 -e PYTHONPATH=/tmp/openpilot -e NUM_JOBS -e JOB_ID -e GITHUB_ACTION -e GITHUB_REF -e GITHUB_HEAD_REF -e GITHUB_SHA -e GITHUB_REPOSITORY -e GITHUB_RUN_ID -v $GITHUB_WORKSPACE/.ci_cache/scons_cache:/tmp/scons_cache -v $GITHUB_WORKSPACE/.ci_cache/comma_download_cache:/tmp/comma_download_cache -v $GITHUB_WORKSPACE/.ci_cache/openpilot_cache:/tmp/openpilot_cache $BASE_IMAGE /bin/bash -c
RUN: docker run --shm-size 2G -v $PWD:/tmp/openpilot -w /tmp/openpilot -e CI=1 -e PYTHONWARNINGS=error -e FILEREADER_CACHE=1 -e PYTHONPATH=/tmp/openpilot -e NUM_JOBS -e JOB_ID -e GITHUB_ACTION -e GITHUB_REF -e GITHUB_HEAD_REF -e GITHUB_SHA -e GITHUB_REPOSITORY -e GITHUB_RUN_ID -e MAPBOX_TOKEN_CI=$MAPBOX_TOKEN_CI -v $GITHUB_WORKSPACE/.ci_cache/scons_cache:/tmp/scons_cache -v $GITHUB_WORKSPACE/.ci_cache/comma_download_cache:/tmp/comma_download_cache -v $GITHUB_WORKSPACE/.ci_cache/openpilot_cache:/tmp/openpilot_cache $BASE_IMAGE /bin/bash -c
PYTEST: pytest --continue-on-collection-errors --durations=0 -n logical

View File

@@ -454,7 +454,20 @@ struct ModelDataV2SP @0xa1680744031fdb2d {
}
}
struct CustomReserved10 @0xcb9fd56c7057593a {
struct Navigationd @0xcb9fd56c7057593a {
upcomingTurn @0 :Text;
currentSpeedLimit @1 :UInt64;
bannerInstructions @2 :Text;
distanceFromRoute @3 :Float64;
allManeuvers @4 :List(Maneuver);
valid @5 :Bool;
struct Maneuver {
distance @0 :Float64;
type @1 :Text;
modifier @2 :Text;
instruction @3 :Text;
}
}
struct CustomReserved11 @0xc2243c65e0340384 {

View File

@@ -2632,7 +2632,7 @@ struct Event {
carStateSP @114 :Custom.CarStateSP;
liveMapDataSP @115 :Custom.LiveMapDataSP;
modelDataV2SP @116 :Custom.ModelDataV2SP;
customReserved10 @136 :Custom.CustomReserved10;
navigationd @136 :Custom.Navigationd;
customReserved11 @137 :Custom.CustomReserved11;
customReserved12 @138 :Custom.CustomReserved12;
customReserved13 @139 :Custom.CustomReserved13;

View File

@@ -89,6 +89,7 @@ _services: dict[str, tuple] = {
"carStateSP": (True, 100., 10),
"liveMapDataSP": (True, 1., 1),
"modelDataV2SP": (True, 20.),
"navigationd": (True, 3.),
"liveLocationKalman": (True, 20.),
# debug

View File

@@ -172,6 +172,14 @@ inline static std::unordered_map<std::string, ParamKeyAttributes> keys = {
{"ShowTurnSignals", {PERSISTENT | BACKUP, BOOL, "0"}},
{"StandstillTimer", {PERSISTENT | BACKUP, BOOL, "0"}},
{"TrueVEgoUI", {PERSISTENT | BACKUP, BOOL, "0"}},
{"VisualRadarTracks", {PERSISTENT | BACKUP, BOOL, "0"}},
{"VisualRadarTracksDelay", {PERSISTENT | BACKUP, FLOAT, "0.0"}},
{"VisualWideCam", {PERSISTENT | BACKUP, BOOL, "0"}},
{"VisualStyle", {PERSISTENT | BACKUP, INT, "0"}},
{"VisualStyleZoom", {PERSISTENT | BACKUP, BOOL, "0"}},
{"VisualStyleOverhead", {PERSISTENT | BACKUP, BOOL, "0"}},
{"VisualStyleOverheadZoom", {PERSISTENT | BACKUP, BOOL, "0"}},
{"VisualStyleOverheadThreshold", {PERSISTENT | BACKUP, INT, "20"}},
// MADS params
{"Mads", {PERSISTENT | BACKUP, BOOL, "1"}},
@@ -187,6 +195,14 @@ inline static std::unordered_map<std::string, ParamKeyAttributes> keys = {
{"ModelManager_LastSyncTime", {CLEAR_ON_MANAGER_START | CLEAR_ON_OFFROAD_TRANSITION, INT, "0"}},
{"ModelManager_ModelsCache", {PERSISTENT | BACKUP, JSON}},
// Navigation params
{"AllowNavigation", {PERSISTENT | BACKUP, BOOL, "0"}},
{"MapboxToken", {PERSISTENT | BACKUP, STRING}},
{"MapboxSettings", {CLEAR_ON_MANAGER_START, JSON}},
{"MapboxRoute", {PERSISTENT, STRING}},
{"MapboxRecompute", {PERSISTENT | BACKUP, BOOL, "0"}},
{"NavDesiresAllowed", {PERSISTENT | BACKUP, BOOL, "0"}},
// Neural Network Lateral Control
{"NeuralNetworkLateralControl", {PERSISTENT | BACKUP, BOOL, "0"}},

View File

@@ -3,6 +3,7 @@ from openpilot.common.constants import CV
from openpilot.common.realtime import DT_MDL
from openpilot.sunnypilot.selfdrive.controls.lib.auto_lane_change import AutoLaneChangeController, AutoLaneChangeMode
from openpilot.sunnypilot.selfdrive.controls.lib.lane_turn_desire import LaneTurnController
from openpilot.sunnypilot.navd.navigation_desires.navigation_desires import NavigationDesires
LaneChangeState = log.LaneChangeState
LaneChangeDirection = log.LaneChangeDirection
@@ -51,6 +52,7 @@ class DesireHelper:
self.alc = AutoLaneChangeController(self)
self.lane_turn_controller = LaneTurnController(self)
self.lane_turn_direction = TurnDirection.none
self.navigation_desires = NavigationDesires()
@staticmethod
def get_lane_change_direction(CS):
@@ -143,3 +145,7 @@ class DesireHelper:
self.desire = log.Desire.none
self.alc.update_state()
nav_desire = self.navigation_desires.update(carstate, lateral_active)
if nav_desire != log.Desire.none and (self.desire == log.Desire.none or self.desire in (log.Desire.turnLeft, log.Desire.turnRight)):
self.desire = nav_desire

View File

@@ -88,7 +88,7 @@ class SelfdriveD(CruiseHelper):
# TODO: de-couple selfdrived with card/conflate on carState without introducing controls mismatches
self.car_state_sock = messaging.sub_sock('carState', timeout=20)
ignore = self.sensor_packets + self.gps_packets + ['alertDebug'] + ['modelDataV2SP']
ignore = self.sensor_packets + self.gps_packets + ['alertDebug'] + ['modelDataV2SP'] + ['navigationd']
if SIMULATION:
ignore += ['driverCameraState', 'managerState']
if REPLAY:
@@ -98,8 +98,8 @@ class SelfdriveD(CruiseHelper):
'carOutput', 'driverMonitoringState', 'longitudinalPlan', 'livePose', 'liveDelay',
'managerState', 'liveParameters', 'radarState', 'liveTorqueParameters',
'controlsState', 'carControl', 'driverAssistance', 'alertDebug', 'userBookmark', 'audioFeedback',
'modelDataV2SP', 'longitudinalPlanSP'] + \
self.camera_packets + self.sensor_packets + self.gps_packets,
'modelDataV2SP', 'longitudinalPlanSP', 'navigationd'] + \
self.camera_packets + self.sensor_packets + self.gps_packets,
ignore_alive=ignore, ignore_avg_freq=ignore,
ignore_valid=ignore, frequency=int(1/DT_CTRL))
@@ -293,7 +293,7 @@ class SelfdriveD(CruiseHelper):
if self.sm['modelV2'].meta.laneChangeState == LaneChangeState.preLaneChange:
direction = self.sm['modelV2'].meta.laneChangeDirection
if (CS.leftBlindspot and direction == LaneChangeDirection.left) or \
(CS.rightBlindspot and direction == LaneChangeDirection.right):
(CS.rightBlindspot and direction == LaneChangeDirection.right):
self.events.add(EventName.laneChangeBlocked)
else:
if direction == LaneChangeDirection.left:
@@ -301,7 +301,7 @@ class SelfdriveD(CruiseHelper):
else:
self.events.add(EventName.preLaneChangeRight)
elif self.sm['modelV2'].meta.laneChangeState in (LaneChangeState.laneChangeStarting,
LaneChangeState.laneChangeFinishing):
LaneChangeState.laneChangeFinishing):
self.events.add(EventName.laneChange)
# Handle lane turn
@@ -496,7 +496,7 @@ class SelfdriveD(CruiseHelper):
# All pandas not in silent mode must have controlsAllowed when openpilot is enabled
if self.enabled and any(not ps.controlsAllowed for ps in self.sm['pandaStates']
if ps.safetyModel not in IGNORED_SAFETY_MODES):
if ps.safetyModel not in IGNORED_SAFETY_MODES):
self.mismatch_counter += 1
return CS

View File

@@ -25,6 +25,11 @@ void AnnotatedCameraWidget::updateState(const UIState &s) {
// update engageability/experimental mode button
experimental_btn->updateState(s);
dmon.updateState(s);
if (s.scene.visual_style == 0) {
setBackgroundColor(bg_colors[STATUS_DISENGAGED]);
} else {
setBackgroundColor(QColor(0, 0, 0));
}
}
void AnnotatedCameraWidget::initializeGL() {
@@ -35,7 +40,12 @@ void AnnotatedCameraWidget::initializeGL() {
qInfo() << "OpenGL language version:" << QString((const char*)glGetString(GL_SHADING_LANGUAGE_VERSION));
prev_draw_t = millis_since_boot();
setBackgroundColor(bg_colors[STATUS_DISENGAGED]);
auto *s = uiState();
if (s->scene.visual_style == 0) {
setBackgroundColor(bg_colors[STATUS_DISENGAGED]);
} else {
setBackgroundColor(QColor(0, 0, 0));
}
}
mat4 AnnotatedCameraWidget::calcFrameMatrix() {
@@ -118,7 +128,13 @@ void AnnotatedCameraWidget::paintGL() {
} else if (v_ego > 15) {
wide_cam_requested = false;
}
wide_cam_requested = wide_cam_requested && sm["selfdriveState"].getSelfdriveState().getExperimentalMode();
if (s->scene.visual_wide_cam == 1) {
wide_cam_requested = true;
} else if (s->scene.visual_wide_cam == 2) {
wide_cam_requested = false;
} else {
wide_cam_requested = wide_cam_requested && sm["selfdriveState"].getSelfdriveState().getExperimentalMode();
}
}
CameraWidget::setStreamType(wide_cam_requested ? VISION_STREAM_WIDE_ROAD : VISION_STREAM_ROAD);
CameraWidget::setFrameId(sm["modelV2"].getModelV2().getFrameId());

View File

@@ -1,4 +1,5 @@
#include "selfdrive/ui/qt/onroad/model.h"
#include <algorithm>
void ModelRenderer::draw(QPainter &painter, const QRect &surface_rect) {
auto *s = uiState();
@@ -49,8 +50,14 @@ void ModelRenderer::update_leads(const cereal::RadarState::Reader &radar_state,
}
void ModelRenderer::update_model(const cereal::ModelDataV2::Reader &model, const cereal::RadarState::LeadData::Reader &lead) {
auto *s = uiState();
const auto &model_position = model.getPosition();
float max_distance = std::clamp(*(model_position.getX().end() - 1), MIN_DRAW_DISTANCE, MAX_DRAW_DISTANCE);
float max_distance;
if (s->scene.visual_style == 0) {
max_distance = std::clamp(*(model_position.getX().end() - 1), MIN_DRAW_DISTANCE, MAX_DRAW_DISTANCE);
} else {
max_distance = std::clamp(*(model_position.getX().end() - 1), MIN_DRAW_DISTANCE, MAX_DRAW_DISTANCE);
}
// update lane lines
const auto &lane_lines = model.getLaneLines();
@@ -58,7 +65,11 @@ void ModelRenderer::update_model(const cereal::ModelDataV2::Reader &model, const
int max_idx = get_path_length_idx(lane_lines[0], max_distance);
for (int i = 0; i < std::size(lane_line_vertices); i++) {
lane_line_probs[i] = line_probs[i];
mapLineToPolygon(lane_lines[i], 0.025 * lane_line_probs[i], 0, &lane_line_vertices[i], max_idx);
if (s->scene.visual_style == 2) {
mapLineToPolygon(lane_lines[i], 0.075 * lane_line_probs[i], 0, &lane_line_vertices[i], max_idx);
} else {
mapLineToPolygon(lane_lines[i], 0.025 * lane_line_probs[i], 0, &lane_line_vertices[i], max_idx);
}
}
// update road edges
@@ -66,7 +77,11 @@ void ModelRenderer::update_model(const cereal::ModelDataV2::Reader &model, const
const auto &edge_stds = model.getRoadEdgeStds();
for (int i = 0; i < std::size(road_edge_vertices); i++) {
road_edge_stds[i] = edge_stds[i];
mapLineToPolygon(road_edges[i], 0.025, 0, &road_edge_vertices[i], max_idx);
if (s->scene.visual_style == 2) {
mapLineToPolygon(road_edges[i], 0.1, 0, &road_edge_vertices[i], max_idx);
} else {
mapLineToPolygon(road_edges[i], 0.025, 0, &road_edge_vertices[i], max_idx);
}
}
// update path
@@ -79,16 +94,112 @@ void ModelRenderer::update_model(const cereal::ModelDataV2::Reader &model, const
}
void ModelRenderer::drawLaneLines(QPainter &painter) {
// lanelines
for (int i = 0; i < std::size(lane_line_vertices); ++i) {
painter.setBrush(QColor::fromRgbF(1.0, 1.0, 1.0, std::clamp<float>(lane_line_probs[i], 0.0, 0.7)));
painter.drawPolygon(lane_line_vertices[i]);
}
auto *s = uiState();
if (s->scene.visual_style == 2) {
QRectF r = clip_region;
// road edges
for (int i = 0; i < std::size(road_edge_vertices); ++i) {
painter.setBrush(QColor::fromRgbF(1.0, 0, 0, std::clamp<float>(1.0 - road_edge_stds[i], 0.0, 1.0)));
painter.drawPolygon(road_edge_vertices[i]);
qreal horizonY = r.bottom();
if (!road_edge_vertices[0].isEmpty() || !road_edge_vertices[1].isEmpty()) {
qreal leftH = r.top();
qreal rightH = r.top();
if (!road_edge_vertices[0].isEmpty()) {
leftH = std::numeric_limits<qreal>::max();
for (const QPointF &pt : road_edge_vertices[0]) {
if (pt.y() < leftH) leftH = pt.y();
}
}
if (!road_edge_vertices[1].isEmpty()) {
rightH = std::numeric_limits<qreal>::max();
for (const QPointF &pt : road_edge_vertices[1]) {
if (pt.y() < rightH) rightH = pt.y();
}
}
horizonY = std::max(leftH, rightH);
}
painter.fillRect(QRectF(r.left(), horizonY + 0, r.width(), r.bottom() - (horizonY + 0)), QColor("#111111"));
auto buildFill = [&](const QPolygonF &edgeRibbon, bool isLeftSide) -> QPolygonF {
if (edgeRibbon.isEmpty()) return {};
QMap<int, QPointF> byY;
for (const QPointF &pt : edgeRibbon) {
int yi = int(std::round(pt.y()));
if (!byY.contains(yi)) {
byY[yi] = pt;
} else {
if (isLeftSide) {
if (pt.x() > byY[yi].x()) byY[yi] = pt;
} else {
if (pt.x() < byY[yi].x()) byY[yi] = pt;
}
}
}
if (byY.isEmpty()) return {};
QPolygonF curve;
for (auto it = byY.cbegin(); it != byY.cend(); ++it) {
curve << it.value();
}
if (curve.size() < 2) return {};
const qreal topY = curve.first().y();
QPolygonF fill;
if (isLeftSide) {
fill << QPointF(r.left(), topY);
for (const QPointF &pt : curve) fill << pt;
fill << QPointF(r.left(), r.bottom());
} else {
fill << QPointF(r.right(), topY);
for (const QPointF &pt : curve) fill << pt;
fill << QPointF(r.right(), r.bottom());
}
return fill;
};
QPolygonF leftFill = buildFill(road_edge_vertices[0], true);
QPolygonF rightFill = buildFill(road_edge_vertices[1], false);
if (!leftFill.isEmpty()) {
painter.setBrush(QColor("#222222"));
painter.drawPolygon(leftFill);
}
if (!rightFill.isEmpty()) {
painter.setBrush(QColor("#222222"));
painter.drawPolygon(rightFill);
}
for (int i = 0; i < std::size(lane_line_vertices); ++i) {
painter.setBrush(QColor::fromRgbF(0.902, 0.902, 0.902, std::clamp<float>(lane_line_probs[i], 0.0, 0.7)));
painter.drawPolygon(lane_line_vertices[i]);
}
for (int i = 0; i < std::size(road_edge_vertices); ++i) {
painter.setBrush(QColor(0x55, 0x55, 0x55, 255));
painter.drawPolygon(road_edge_vertices[i]);
}
QLinearGradient bgGrad(r.left(), horizonY - 100, r.left(), horizonY + 100);
bgGrad.setColorAt(0.0, QColor("#000000"));
bgGrad.setColorAt(0.5, QColor("#111111"));
bgGrad.setColorAt(1.0, QColor("#111111"));
painter.fillRect(QRectF(r.left(), horizonY - 200, r.width(), 200), bgGrad);
} else {
// lanelines
for (int i = 0; i < std::size(lane_line_vertices); ++i) {
painter.setBrush(QColor::fromRgbF(1.0, 1.0, 1.0, std::clamp<float>(lane_line_probs[i], 0.0, 0.7)));
painter.drawPolygon(lane_line_vertices[i]);
}
// road edges
for (int i = 0; i < std::size(road_edge_vertices); ++i) {
painter.setBrush(QColor::fromRgbF(1.0, 0, 0, std::clamp<float>(1.0 - road_edge_stds[i], 0.0, 1.0)));
painter.drawPolygon(road_edge_vertices[i]);
}
}
}
@@ -175,6 +286,7 @@ QColor ModelRenderer::blendColors(const QColor &start, const QColor &end, float
void ModelRenderer::drawLead(QPainter &painter, const cereal::RadarState::LeadData::Reader &lead_data,
const QPointF &vd, const QRect &surface_rect) {
auto *s = uiState();
const float speedBuff = 10.;
const float leadBuff = 40.;
const float d_rel = lead_data.getDRel();
@@ -197,20 +309,133 @@ void ModelRenderer::drawLead(QPainter &painter, const cereal::RadarState::LeadDa
float g_yo = sz / 10;
QPointF glow[] = {{x + (sz * 1.35) + g_xo, y + sz + g_yo}, {x, y - g_yo}, {x - (sz * 1.35) - g_xo, y + sz + g_yo}};
painter.setBrush(QColor(218, 202, 37, 255));
if (s->scene.visual_style == 2) {
painter.setBrush(QColor(0xE6, 0xE6, 0xE6, 255));
} else {
painter.setBrush(QColor(218, 202, 37, 255));
}
painter.drawPolygon(glow, std::size(glow));
// chevron
QPointF chevron[] = {{x + (sz * 1.25), y + sz}, {x, y}, {x - (sz * 1.25), y + sz}};
painter.setBrush(QColor(201, 34, 49, fillAlpha));
if (s->scene.visual_style == 2) {
painter.setBrush(QColor(0, 0, 0, fillAlpha));
} else {
painter.setBrush(QColor(201, 34, 49, fillAlpha));
}
painter.drawPolygon(chevron, std::size(chevron));
}
// Projects a point in car to space to the corresponding point in full frame image space.
float mapRange(float x, float in_min, float in_max, float out_min, float out_max) {
if (in_min < in_max) {
x = std::clamp(x, in_min, in_max);
} else {
x = std::clamp(x, in_max, in_min);
}
return out_min + (x - in_min) * (out_max - out_min) / (in_max - in_min);
}
// Projects a point in car space to the corresponding point in full frame image space.
bool ModelRenderer::mapToScreen(float in_x, float in_y, float in_z, QPointF *out) {
auto *s = uiState();
auto &sm = *(s->sm);
float blend_speed_mph = fabsf(sm["carState"].getCarState().getVEgo() * 2.23694f);
Eigen::Vector3f input(in_x, in_y, in_z);
if ((s->scene.visual_style_zoom == 1 || s->scene.visual_style_zoom == 2) && s->scene.visual_style != 0) {
float zoom_start = 20.0f;
float zoom_end = 50.0f;
if (s->scene.visual_style_zoom == 2) {
std::swap(zoom_start, zoom_end);
}
float IN_X_OFFSET = mapRange(blend_speed_mph, zoom_start, zoom_end, 0.0f, 24.0f);
float IN_Y_OFFSET = mapRange(blend_speed_mph, zoom_start, zoom_end, 1.0f, 2.0f);
float IN_Z_OFFSET = mapRange(blend_speed_mph, zoom_start, zoom_end, 0.0f, 5.0f);
float PITCH_DEG = mapRange(blend_speed_mph, zoom_start, zoom_end, 0.0f, 5.0f);
input = Eigen::Vector3f(in_x + IN_X_OFFSET, in_y / IN_Y_OFFSET, in_z + IN_Z_OFFSET);
Eigen::AngleAxisf pitch_rot(PITCH_DEG * M_PI / 180.0f, Eigen::Vector3f::UnitY());
input = pitch_rot * input;
}
auto pt = car_space_transform * input;
*out = QPointF(pt.x() / pt.z(), pt.y() / pt.z());
bool normal_valid = (pt.z() > 1e-3f &&
std::isfinite(pt.x()) && std::isfinite(pt.y()));
QPointF normal_view;
if (normal_valid) {
normal_view = QPointF(pt.x() / pt.z(), pt.y() / pt.z());
}
const float base_scale_x = 20.0f;
const float base_scale_y = 15.0f;
const float y_offset = 450.0f;
float factor_scale_x = 0.0f;
if (blend_speed_mph > 0.0f) {
if (s->scene.visual_style_overhead_zoom == 1) {
factor_scale_x = mapRange(blend_speed_mph, 0.0f, 50.0f, 30.0f, 0.0f);
} else if (s->scene.visual_style_overhead_zoom == 2) {
factor_scale_x = mapRange(blend_speed_mph, 50.0f, 0.0f, 30.0f, 0.0f);
}
}
float scale_x = base_scale_x + factor_scale_x;
float scale_y = base_scale_y;
QPointF topdown_view(
clip_region.center().x() + in_y * scale_x,
(clip_region.bottom() - y_offset) - in_x * scale_y
);
if ((s->scene.visual_style_overhead == 1 || s->scene.visual_style_overhead == 2) && s->scene.visual_style != 0) {
static float blend = 0.0f;
static float target_blend = 0.0f;
static double last_t = millis_since_boot();
const bool inverted = (s->scene.visual_style_overhead == 2);
const float threshold = s->scene.visual_style_overhead_threshold;
const float hysteresis = 5.0f;
if (!inverted) {
if (target_blend < 0.5f && blend_speed_mph > threshold) {
target_blend = 1.0f;
} else if (target_blend > 0.5f && blend_speed_mph < threshold - hysteresis) {
target_blend = 0.0f;
}
} else {
if (target_blend < 0.5f && blend_speed_mph < threshold) {
target_blend = 1.0f;
} else if (target_blend > 0.5f && blend_speed_mph > threshold + hysteresis) {
target_blend = 0.0f;
}
}
double now = millis_since_boot();
double dt = (now - last_t) / 1000.0;
last_t = now;
const float transition_time = 1.50f;
float step = dt / transition_time;
if (blend < target_blend) {
blend = std::min(blend + step, target_blend);
} else if (blend > target_blend) {
blend = std::max(blend - step, target_blend);
}
if (!normal_valid) return false;
*out = QPointF(
(1 - blend) * normal_view.x() + blend * topdown_view.x(),
(1 - blend) * normal_view.y() + blend * topdown_view.y()
);
} else {
if (!normal_valid) return false;
*out = normal_view;
}
return clip_region.contains(*out);
}

View File

@@ -196,6 +196,7 @@ mat4 CameraWidget::calcFrameMatrix() {
}
void CameraWidget::paintGL() {
auto *s = uiState();
glClearColor(bg.redF(), bg.greenF(), bg.blueF(), bg.alphaF());
glClear(GL_STENCIL_BUFFER_BIT | GL_COLOR_BUFFER_BIT);
@@ -248,7 +249,9 @@ void CameraWidget::paintGL() {
glUniformMatrix4fv(program->uniformLocation("uTransform"), 1, GL_TRUE, frame_mat.v);
glEnableVertexAttribArray(0);
glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_BYTE, (const void *)0);
if (s->scene.visual_style == 0) {
glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_BYTE, (const void *)0);
}
glDisableVertexAttribArray(0);
glBindVertexArray(0);
glBindTexture(GL_TEXTURE_2D, 0);

View File

@@ -30,6 +30,7 @@ qt_src = [
"sunnypilot/qt/offroad/settings/max_time_offroad.cc",
"sunnypilot/qt/offroad/settings/brightness.cc",
"sunnypilot/qt/offroad/settings/models_panel.cc",
"sunnypilot/qt/offroad/settings/navigation_panel.cc",
"sunnypilot/qt/offroad/settings/osm_panel.cc",
"sunnypilot/qt/offroad/settings/settings.cc",
"sunnypilot/qt/offroad/settings/software_panel.cc",

View File

@@ -36,7 +36,7 @@ DisplayPanel::DisplayPanel(QWidget *parent) : QWidget(parent) {
interactivityTimeout = new OptionControlSP("InteractivityTimeout", tr("Interactivity Timeout"),
tr("Apply a custom timeout for settings UI."
"\nThis is the time after which settings UI closes automatically if user is not interacting with the screen."),
"", {0, 120}, 10, true, nullptr, false);
"", {999999, 1000000}, 1000000, true, nullptr, false);
connect(interactivityTimeout, &OptionControlSP::updateLabels, [=]() {
refresh();

View File

@@ -0,0 +1,79 @@
/**
* Copyright (c) 2021-, Haibin Wen, sunnypilot, and a number of other contributors.
*
* This file is part of sunnypilot and is licensed under the MIT License.
* See the LICENSE.md file in the root directory for more details.
*/
#include "selfdrive/ui/sunnypilot/qt/offroad/settings/navigation_panel.h"
NavigationPanel::NavigationPanel(QWidget* parent) : QWidget(parent) {
QVBoxLayout* main_layout = new QVBoxLayout(this);
main_layout->setContentsMargins(50, 25, 50, 25);
list = new ListWidget(this, false);
scroller = new ScrollViewSP(list, this);
main_layout->addWidget(scroller);
// Mapbox Token
mapbox_token = new ButtonControl(tr("Mapbox Token"), tr("Edit"), tr("Enter your Mapbox API token"));
QObject::connect(mapbox_token, &ButtonControl::clicked, [=]() {
QString current = QString::fromStdString(params.get("MapboxToken"));
QString token = InputDialog::getText(tr("Enter Mapbox Token"), this, "", false, -1, current);
if (!token.isEmpty()) {
params.put("MapboxToken", token.toStdString());
refresh();
}
});
list->addItem(mapbox_token);
// Mapbox Route
mapbox_route = new ButtonControl(tr("Mapbox Route"), tr("Edit"), tr("Enter Mapbox route data"));
QObject::connect(mapbox_route, &ButtonControl::clicked, [=]() {
QString current = QString::fromStdString(params.get("MapboxRoute"));
QString route = InputDialog::getText(tr("Enter Mapbox Route"), this, "", false, -1, current);
if (!route.isEmpty()) {
params.put("MapboxRoute", route.toStdString());
refresh();
}
});
list->addItem(mapbox_route);
// Allow Navigation
allow_navigation = new ParamControlSP("AllowNavigation", tr("Allow Navigation"), tr("Enable navigation features and start navigationd"), "", this);
QObject::connect(allow_navigation, &ParamControlSP::toggleFlipped, this, &NavigationPanel::updateNavigationVisibility);
list->addItem(allow_navigation);
// Mapbox Recompute
mapbox_recompute = new ParamControlSP("MapboxRecompute", tr("Mapbox Recompute"), tr("Enable automatic route recomputation"), "", this);
list->addItem(mapbox_recompute);
// Nav Allowed
nav_allowed = new ParamControlSP("NavDesiresAllowed", tr("Navigation Allowed"), tr("Allow navigation to automatically take turns"), "", this);
list->addItem(nav_allowed);
}
void NavigationPanel::updateNavigationVisibility(bool state) {
mapbox_recompute->setVisible(state);
nav_allowed->setVisible(state);
}
void NavigationPanel::showEvent(QShowEvent *event) {
refresh();
}
void NavigationPanel::refresh() {
allow_navigation->refresh();
bool nav_enabled = allow_navigation->isToggled();
updateNavigationVisibility(nav_enabled);
QString token = QString::fromStdString(params.get("MapboxToken"));
mapbox_token->setValue(token.isEmpty() ? tr("Not set") : token);
QString route = QString::fromStdString(params.get("MapboxRoute"));
mapbox_route->setValue(route.isEmpty() ? tr("Not set") : route);
mapbox_recompute->refresh();
nav_allowed->refresh();
}

View File

@@ -0,0 +1,38 @@
/**
* Copyright (c) 2021-, Haibin Wen, sunnypilot, and a number of other contributors.
*
* This file is part of sunnypilot and is licensed under the MIT License.
* See the LICENSE.md file in the root directory for more details.
*/
#pragma once
#include "selfdrive/ui/qt/offroad/settings.h"
#include "selfdrive/ui/qt/widgets/controls.h"
#include "selfdrive/ui/qt/widgets/input.h"
#include "selfdrive/ui/sunnypilot/qt/widgets/controls.h"
#include "selfdrive/ui/sunnypilot/qt/widgets/scrollview.h"
#include "selfdrive/ui/sunnypilot/qt/util.h"
class NavigationPanel : public QWidget {
Q_OBJECT
public:
explicit NavigationPanel(QWidget* parent = nullptr);
void showEvent(QShowEvent *event) override;
void refresh();
public slots:
void updateNavigationVisibility(bool state);
private:
Params params;
ListWidget* list;
ScrollViewSP* scroller;
ParamControlSP* allow_navigation;
ButtonControl* mapbox_token;
ButtonControl* mapbox_route;
ParamControlSP* mapbox_recompute;
ParamControlSP* nav_allowed;
};

View File

@@ -15,6 +15,7 @@
#include "selfdrive/ui/sunnypilot/qt/offroad/settings/device_panel.h"
#include "selfdrive/ui/sunnypilot/qt/offroad/settings/display_panel.h"
#include "selfdrive/ui/sunnypilot/qt/offroad/settings/models_panel.h"
#include "selfdrive/ui/sunnypilot/qt/offroad/settings/navigation_panel.h"
#include "selfdrive/ui/sunnypilot/qt/offroad/settings/software_panel.h"
#include "selfdrive/ui/sunnypilot/qt/offroad/settings/sunnylink_panel.h"
#include "selfdrive/ui/sunnypilot/qt/offroad/settings/lateral_panel.h"
@@ -85,6 +86,7 @@ SettingsWindowSP::SettingsWindowSP(QWidget *parent) : SettingsWindow(parent) {
PanelInfo(" " + tr("Toggles"), toggles, "../../sunnypilot/selfdrive/assets/offroad/icon_toggle.png"),
PanelInfo(" " + tr("Software"), new SoftwarePanelSP(this), "../../sunnypilot/selfdrive/assets/offroad/icon_software.png"),
PanelInfo(" " + tr("Models"), new ModelsPanel(this), "../../sunnypilot/selfdrive/assets/offroad/icon_models.png"),
PanelInfo(" " + tr("Navigation"), new NavigationPanel(this), "../../sunnypilot/selfdrive/assets/offroad/icon_map.png"),
PanelInfo(" " + tr("Steering"), new LateralPanel(this), "../../sunnypilot/selfdrive/assets/offroad/icon_lateral.png"),
PanelInfo(" " + tr("Cruise"), new LongitudinalPanel(this), "../assets/icons/speed_limit.png"),
PanelInfo(" " + tr("Visuals"), new VisualsPanel(this), "../../sunnypilot/selfdrive/assets/offroad/icon_visuals.png"),

View File

@@ -11,6 +11,18 @@ VisualsPanel::VisualsPanel(QWidget *parent) : QWidget(parent) {
param_watcher = new ParamWatcher(this);
connect(param_watcher, &ParamWatcher::paramChanged, [=](const QString &param_name, const QString &param_value) {
paramsRefresh();
if (param_name == "VisualStyle") {
visual_style_value = param_value.toInt();
} else if (param_name == "VisualStyleOverhead") {
visual_style_overhead_value = param_value.toInt();
} else if (param_name == "VisualRadarTracks") {
bool radar_tracks_enabled = param_value.toInt() != 0;
visual_radar_tracks_delay_settings->setVisible(radar_tracks_enabled);
}
visual_style_zoom_settings->setVisible(visual_style_value != 0);
visual_style_overhead_settings->setVisible(visual_style_value != 0);
visual_style_overhead_zoom_settings->setVisible(visual_style_value != 0 && visual_style_overhead_value != 0);
visual_style_overhead_threshold_settings->setVisible(visual_style_value != 0 && visual_style_overhead_value != 0);
});
main_layout = new QStackedLayout(this);
@@ -90,6 +102,13 @@ VisualsPanel::VisualsPanel(QWidget *parent) : QWidget(parent) {
"",
false,
},
{
"VisualRadarTracks",
tr("Show Radar Tracks"),
tr("Shows what the cars radar sees."),
"",
false,
},
};
// Add regular toggles first
@@ -116,6 +135,111 @@ VisualsPanel::VisualsPanel(QWidget *parent) : QWidget(parent) {
param_watcher->addParam(param);
}
// Visuals: Radar Tracks Delay
visual_radar_tracks_delay_settings = new OptionControlSP("VisualRadarTracksDelay", tr("Adjust Visual Radar Tracks Delay"),
tr("Delays radar tracks to better match what you see through the camera."),
"", {0, 100}, 10, false, nullptr, true);
connect(visual_radar_tracks_delay_settings, &OptionControlSP::updateLabels, [=]() {
float radar_tracks_delay_value = QString::fromStdString(params.get("VisualRadarTracksDelay")).toFloat();
visual_radar_tracks_delay_settings->setLabel(QString::number(radar_tracks_delay_value, 'f', 1) + " s");
});
float radar_tracks_delay_value = QString::fromStdString(params.get("VisualRadarTracksDelay")).toFloat();
visual_radar_tracks_delay_settings->setLabel(QString::number(radar_tracks_delay_value, 'f', 1) + " s");
list->addItem(visual_radar_tracks_delay_settings);
// Wide Cam
std::vector<QString> visual_wide_cam_settings_texts{tr("Auto"), tr("On"), tr("Off")};
visual_wide_cam_settings = new ButtonParamControlSP(
"VisualWideCam", tr("Wide Cam"), tr("Override the wide cam view regardless of experimental mode status."),
"",
visual_wide_cam_settings_texts,
250);
list->addItem(visual_wide_cam_settings);
// Visual Style
std::vector<QString> visual_style_settings_texts{tr("Default"), tr("Minimal"), tr("Vision")};
visual_style_settings = new ButtonParamControlSP(
"VisualStyle", tr("Visual Style"),
tr(
"Switch between different on-road visualization layouts."
"<ul style='margin-left: 10px; margin-top: 4px;'>"
"<li><b>Default:</b> Standard OpenPilot layout with camera and path view.</li>"
"<li><b>Minimal:</b> Clean interface without camera feed or extra elements.</li>"
"<li><b>Vision:</b> Experimental layout that focuses on model perception and environment.</li>"
"</ul>"
),
"",
visual_style_settings_texts,
380);
list->addItem(visual_style_settings);
// Visual Style Zoom
std::vector<QString> visual_style_zoom_settings_texts{tr("Disabled"), tr("Enabled"), tr("Inverted")};
visual_style_zoom_settings = new ButtonParamControlSP(
"VisualStyleZoom", tr("Visual Style Zoom"),
tr(
"Enables dynamic zooming based on driving speed in the selected visual style."
"<ul style='margin-left: 10px; margin-top: 4px;'>"
"<li><b>Disabled:</b> Keeps the zoom fixed.</li>"
"<li><b>Enabled:</b> Zooms in at low speed and out at high speed.</li>"
"<li><b>Inverted:</b> Reverses the zoom behavior.</li>"
"</ul>"
),
"",
visual_style_zoom_settings_texts,
380);
list->addItem(visual_style_zoom_settings);
// Visual Style Overhead
std::vector<QString> visual_style_overhead_settings_texts{tr("Disabled"), tr("Enabled"), tr("Inverted")};
visual_style_overhead_settings = new ButtonParamControlSP(
"VisualStyleOverhead", tr("Visual Style Overhead"),
tr(
"Toggles an overhead (top-down) camera view for a 2D-style perspective."
"<ul style='margin-left: 10px; margin-top: 4px;'>"
"<li><b>Disabled:</b> Keeps the standard forward 3D view.</li>"
"<li><b>Enabled:</b> Switches to overhead view when active.</li>"
"<li><b>Inverted:</b> Reverses when the transition happens.</li>"
"</ul>"
),
"",
visual_style_overhead_settings_texts,
380);
list->addItem(visual_style_overhead_settings);
// Visual Style Overhead Zoom
std::vector<QString> visual_style_overhead_zoom_settings_texts{tr("Disabled"), tr("Enabled"), tr("Inverted")};
visual_style_overhead_zoom_settings = new ButtonParamControlSP(
"VisualStyleOverheadZoom", tr("Visual Style Overhead Zoom"),
tr(
"Controls zooming behavior while in overhead mode."
"<ul style='margin-left: 10px; margin-top: 4px;'>"
"<li><b>Disabled:</b> Keeps a fixed zoom level in overhead mode.</li>"
"<li><b>Enabled:</b> Zooms dynamically based on speed while overhead.</li>"
"<li><b>Inverted:</b> Opposite zoom direction.</li>"
"</ul>"
),
"",
visual_style_overhead_zoom_settings_texts,
380);
list->addItem(visual_style_overhead_zoom_settings);
// Visual Style Overhead Threshold
visual_style_overhead_threshold_settings = new OptionControlSP(
"VisualStyleOverheadThreshold", tr("Visual Style Overhead Threshold"),
tr("Sets the speed (in mph) where the display transitions between normal and overhead view."),
"", {10, 80}, 5, false, nullptr, false);
auto updateThresholdLabel = [=]() {
int mph = QString::fromStdString(params.get("VisualStyleOverheadThreshold")).toInt();
visual_style_overhead_threshold_settings->setLabel(QString("%1 mph").arg(mph));
};
connect(visual_style_overhead_threshold_settings, &OptionControlSP::updateLabels, updateThresholdLabel);
updateThresholdLabel();
list->addItem(visual_style_overhead_threshold_settings);
// Visuals: Display Metrics below Chevron
std::vector<QString> chevron_info_settings_texts{tr("Off"), tr("Distance"), tr("Speed"), tr("Time"), tr("All")};
chevron_info_settings = new ButtonParamControlSP(
@@ -136,6 +260,19 @@ VisualsPanel::VisualsPanel(QWidget *parent) : QWidget(parent) {
380);
list->addItem(dev_ui_settings);
bool radar_tracks_enabled = QString::fromStdString(params.get("VisualRadarTracks")).toInt() != 0;
visual_radar_tracks_delay_settings->setVisible(radar_tracks_enabled);
param_watcher->addParam("VisualRadarTracks");
visual_style_value = QString::fromStdString(params.get("VisualStyle")).toInt();
visual_style_overhead_value = QString::fromStdString(params.get("VisualStyleOverhead")).toInt();
visual_style_zoom_settings->setVisible(visual_style_value != 0);
visual_style_overhead_settings->setVisible(visual_style_value != 0);
visual_style_overhead_zoom_settings->setVisible(visual_style_value != 0 && visual_style_overhead_value != 0);
visual_style_overhead_threshold_settings->setVisible(visual_style_value != 0 && visual_style_overhead_value != 0);
param_watcher->addParam("VisualStyle");
param_watcher->addParam("VisualStyleOverhead");
sunnypilotScroller = new ScrollViewSP(list, this);
vlayout->addWidget(sunnypilotScroller);
@@ -191,4 +328,19 @@ void VisualsPanel::paramsRefresh() {
if (dev_ui_settings) {
dev_ui_settings->refresh();
}
if (visual_wide_cam_settings) {
visual_wide_cam_settings->refresh();
}
if (visual_style_settings) {
visual_style_settings->refresh();
}
if (visual_style_zoom_settings) {
visual_style_zoom_settings->refresh();
}
if (visual_style_overhead_settings) {
visual_style_overhead_settings->refresh();
}
if (visual_style_overhead_zoom_settings) {
visual_style_overhead_zoom_settings->refresh();
}
}

View File

@@ -32,4 +32,14 @@ protected:
ButtonParamControlSP *dev_ui_settings;
bool has_longitudinal_control = false;
OptionControlSP *visual_radar_tracks_delay_settings;
ButtonParamControlSP *visual_wide_cam_settings;
int visual_style_value = 0;
int visual_style_overhead_value = 0;
ButtonParamControlSP *visual_style_settings;
ButtonParamControlSP *visual_style_zoom_settings;
ButtonParamControlSP *visual_style_overhead_settings;
ButtonParamControlSP *visual_style_overhead_zoom_settings;
OptionControlSP *visual_style_overhead_threshold_settings;
};

View File

@@ -5,6 +5,7 @@
* See the LICENSE.md file in the root directory for more details.
*/
#include <QPainterPath>
#include <cmath>
#include "selfdrive/ui/sunnypilot/qt/onroad/hud.h"
@@ -154,6 +155,63 @@ void HudRendererSP::updateState(const UIState &s) {
allow_e2e_alerts = sm["selfdriveState"].getSelfdriveState().getAlertSize() == cereal::SelfdriveState::AlertSize::NONE &&
sm.rcv_frame("driverStateV2") > s.scene.started_frame && !reversing;
// Navigationd
if (sm.updated("navigationd")) {
auto nav = sm["navigationd"].getNavigationd();
navigationValid = nav.getValid();
if (navigationValid && nav.getAllManeuvers().size() > 0) {
int currManeuverIdx = nav.getAllManeuvers().size() > 1 ? 1 : 0;
auto maneuver = nav.getAllManeuvers()[currManeuverIdx];
navigationModifier = QString::fromStdString(maneuver.getModifier());
navigationManeuverType = QString::fromStdString(maneuver.getType());
float dist = maneuver.getDistance();
if (is_metric) {
if (dist < 1000) {
if (dist < 500) {
navigationDistance = QString::number(std::round(dist / 25.0) * 25) + " m";
}
else {
navigationDistance = QString::number(std::round(dist / 50.0) * 50) + " m";
}
} else {
navigationDistance = QString::number(dist / 1000, 'f', 1) + " km";
}
} else {
float dist_ft = dist * 3.28084f;
if (dist_ft < 1000) {
if (dist_ft <= 100){
navigationDistance = QString::number((std::round(dist_ft / 10.0) * 10)) + " ft";
}
else {
navigationDistance = QString::number((std::round(dist_ft / 50.0) * 50)) + " ft";
}
} else {
navigationDistance = QString::number(dist_ft / 5280, 'f', 1) + " mi";
}
}
QString instruction = QString::fromStdString(maneuver.getInstruction());
QStringList parts = instruction.split(" onto ");
if (parts.size() > 1) {
navigationStreet = parts[1].trimmed();
} else {
navigationStreet = instruction;
}
navigationStreet = navigationStreet.replace(".", "");
// Get next maneuver if available
if (nav.getAllManeuvers().size() > 2) {
auto nextManeuver = nav.getAllManeuvers()[2];
navigationNextModifier = QString::fromStdString(nextManeuver.getModifier());
navigationNextManeuverType = QString::fromStdString(nextManeuver.getType());
navigationHasNext = true;
} else {
navigationHasNext = false;
}
}
}
}
void HudRendererSP::draw(QPainter &p, const QRect &surface_rect) {
@@ -287,6 +345,8 @@ void HudRendererSP::draw(QPainter &p, const QRect &surface_rect) {
}
}
drawNavigationHUD(p, surface_rect);
p.restore();
}
@@ -306,9 +366,14 @@ bool HudRendererSP::pulseElement(int frame) {
}
void HudRendererSP::drawSmartCruiseControlOnroadIcon(QPainter &p, const QRect &surface_rect, int x_offset, int y_offset, std::string name) {
int x = surface_rect.center().x();
int base_x = surface_rect.center().x();
int y = surface_rect.height() / 4;
if (navigationValid) {
base_x = 618;
y = 420;
}
QString text = QString::fromStdString(name);
QFont font = InterFont(36, QFont::Bold);
p.setFont(font);
@@ -319,7 +384,7 @@ void HudRendererSP::drawSmartCruiseControlOnroadIcon(QPainter &p, const QRect &s
int box_width = 160;
int box_height = fm.height() + padding_v * 2;
QRectF bg_rect(x - (box_width / 2) + x_offset,
QRectF bg_rect(base_x - (box_width / 2) + x_offset,
y - (box_height / 2) + y_offset,
box_width, box_height);
@@ -684,15 +749,16 @@ void HudRendererSP::drawSpeedLimitPreActiveArrow(QPainter &p, QRect &sign_rect)
}
void HudRendererSP::drawSetSpeedSP(QPainter &p, const QRect &surface_rect) {
// Draw outer box + border to contain set speed
const QSize default_size = {172, 204};
QSize set_speed_size = is_metric ? QSize(200, 204) : default_size;
QRect set_speed_rect(QPoint(60 + (default_size.width() - set_speed_size.width()) / 2, 45), set_speed_size);
// Draw set speed box
p.setPen(QPen(QColor(255, 255, 255, 75), 6));
p.setBrush(QColor(0, 0, 0, 166));
p.drawRoundedRect(set_speed_rect, 32, 32);
// ICBM counter logic
if (!pcmCruiseSpeed && carControlEnabled) {
if (std::nearbyint(set_speed) != std::nearbyint(speedCluster)) {
icbm_active_counter = 3 * UI_FREQ;
} else if (icbm_active_counter > 0) {
icbm_active_counter--;
}
} else {
icbm_active_counter = 0;
}
// Colors based on status
QColor max_color = QColor(0xa6, 0xa6, 0xa6, 0xff);
@@ -713,29 +779,62 @@ void HudRendererSP::drawSetSpeedSP(QPainter &p, const QRect &surface_rect) {
}
}
// Draw "MAX" or carState.cruiseState.speedCluster (when ICBM is active) text
if (!pcmCruiseSpeed && carControlEnabled) {
if (std::nearbyint(set_speed) != std::nearbyint(speedCluster)) {
icbm_active_counter = 3 * UI_FREQ;
} else if (icbm_active_counter > 0) {
icbm_active_counter--;
}
} else {
icbm_active_counter = 0;
}
int max_str_size = (icbm_active_counter != 0) ? 60 : 40;
int max_str_y = (icbm_active_counter != 0) ? 15 : 27;
QString max_str = (icbm_active_counter != 0) ? QString::number(std::nearbyint(speedCluster)) : tr("MAX");
p.setFont(InterFont(max_str_size, QFont::DemiBold));
p.setPen(max_color);
p.drawText(set_speed_rect.adjusted(0, max_str_y, 0, 0), Qt::AlignTop | Qt::AlignHCenter, max_str);
// Draw set speed
QString setSpeedStr = is_cruise_set ? QString::number(std::nearbyint(set_speed)) : "";
p.setFont(InterFont(90, QFont::Bold));
p.setPen(set_speed_color);
p.drawText(set_speed_rect.adjusted(0, 77, 0, 0), Qt::AlignTop | Qt::AlignHCenter, setSpeedStr);
if (!navigationValid) {
// Original positions when navigation is not valid
const QSize default_size = {172, 204};
QSize set_speed_size = is_metric ? QSize(200, 204) : default_size;
QRect set_speed_rect(QPoint(60 + (default_size.width() - set_speed_size.width()) / 2, 45), set_speed_size);
// Draw set speed box
p.setPen(QPen(QColor(255, 255, 255, 75), 6));
p.setBrush(QColor(0, 0, 0, 166));
p.drawRoundedRect(set_speed_rect, 32, 32);
// Draw "MAX" or carState.cruiseState.speedCluster (when ICBM is active) text
int max_str_size = (icbm_active_counter != 0) ? 60 : 40;
int max_str_y = (icbm_active_counter != 0) ? 15 : 27;
p.setFont(InterFont(max_str_size, QFont::DemiBold));
p.setPen(max_color);
p.drawText(set_speed_rect.adjusted(0, max_str_y, 0, 0), Qt::AlignTop | Qt::AlignHCenter, max_str);
// Draw set speed
p.setFont(InterFont(90, QFont::Bold));
p.setPen(set_speed_color);
p.drawText(set_speed_rect.adjusted(0, 77, 0, 0), Qt::AlignTop | Qt::AlignHCenter, setSpeedStr);
} else {
// Modified positions when navigation is valid
const int container_width = 200;
const int container_height = 320;
const int container_x = 40;
const int container_y = 45;
QRect speed_container(container_x, container_y, container_width, container_height);
// Draw outer rounded rectangle container
p.setPen(QPen(QColor(255, 255, 255, 75), 6));
p.setBrush(QColor(0, 0, 0, 166));
p.drawRoundedRect(speed_container, 24, 24);
int divider_y = container_y + 190;
p.setPen(QPen(QColor(255, 255, 255, 50), 2));
p.drawLine(container_x + 20, divider_y, container_x + container_width - 20, divider_y);
// max label
QRect max_label_rect(container_x, container_y + 200, container_width, 35);
p.setFont(InterFont(32, QFont::Normal));
p.setPen(max_color);
p.drawText(max_label_rect, Qt::AlignCenter, max_str);
// Set speed value
QRect set_speed_rect(container_x, container_y + 240, container_width, 70);
p.setFont(InterFont(68, QFont::Bold));
p.setPen(set_speed_color);
p.drawText(set_speed_rect, Qt::AlignCenter, setSpeedStr);
}
}
void HudRendererSP::drawE2eAlert(QPainter &p, const QRect &surface_rect, const QString &alert_alt_text) {
@@ -794,12 +893,40 @@ void HudRendererSP::drawE2eAlert(QPainter &p, const QRect &surface_rect, const Q
void HudRendererSP::drawCurrentSpeedSP(QPainter &p, const QRect &surface_rect) {
QString speedStr = QString::number(std::nearbyint(speed));
QString unit = is_metric ? tr("km/h") : tr("mph");
p.setFont(InterFont(176, QFont::Bold));
HudRenderer::drawText(p, surface_rect.center().x(), 210, speedStr);
int speed_x = surface_rect.center().x();
int speed_y = 210;
int unit_y = 290;
QFont speed_font = InterFont(176, QFont::Bold);
QFont unit_font = InterFont(66);
p.setFont(InterFont(66));
HudRenderer::drawText(p, surface_rect.center().x(), 290, is_metric ? tr("km/h") : tr("mph"), 200);
if (navigationValid) {
speed_y = 75;
unit_y = 175;
speed_font = InterFont(100, QFont::Bold);
unit_font = InterFont(35, QFont::Normal);
}
// Draw speed
p.setFont(speed_font);
if (!navigationValid) {
HudRenderer::drawText(p, speed_x, speed_y, speedStr);
} else {
QRect current_speed_rect(40, speed_y, 200, 100);
p.setPen(Qt::white);
p.drawText(current_speed_rect, Qt::AlignCenter, speedStr);
}
// Draw unit
p.setFont(unit_font);
if (!navigationValid) {
HudRenderer::drawText(p, speed_x, unit_y, unit, 200);
} else {
QRect unit_rect(40, unit_y, 200, 40);
p.setPen(QColor(180, 180, 180, 255));
p.drawText(unit_rect, Qt::AlignCenter, unit);
}
}
void HudRendererSP::drawBlinker(QPainter &p, const QRect &surface_rect) {
@@ -827,7 +954,7 @@ void HudRendererSP::drawBlinker(QPainter &p, const QRect &surface_rect) {
const int circleRadius = 60;
const int arrowLength = 60;
const int x_gap = 160;
const int y_offset = 272;
const int y_offset = navigationValid ? 352 : 300;
const int centerX = surface_rect.center().x();
@@ -885,3 +1012,181 @@ void HudRendererSP::drawBlinker(QPainter &p, const QRect &surface_rect) {
p.restore();
}
QString HudRendererSP::getNavigationIconName(const QString &type, const QString &mod) {
static QMap<QString, QString> icon_map;
if (icon_map.isEmpty()) {
icon_map["turn|uturn"] = "direction_uturn.png";
icon_map["turn|sharp right"] = "direction_turn_sharp_right.png";
icon_map["turn|right"] = "direction_turn_right.png";
icon_map["turn|slight right"] = "direction_turn_slight_right.png";
icon_map["turn|straight"] = "direction_turn_straight.png";
icon_map["turn|slight left"] = "direction_turn_slight_left.png";
icon_map["turn|left"] = "direction_turn_left.png";
icon_map["turn|sharp left"] = "direction_turn_sharp_left.png";
icon_map["arrive|right"] = "direction_arrive_right.png";
icon_map["arrive|straight"] = "direction_arrive_straight.png";
icon_map["arrive|left"] = "direction_arrive_left.png";
icon_map["arrive|"] = "direction_arrive.png";
icon_map["merge|slight right"] = "direction_merge_slight_right.png";
icon_map["merge|right"] = "direction_merge_right.png";
icon_map["merge|straight"] = "direction_merge_straight.png";
icon_map["merge|slight left"] = "direction_merge_slight_left.png";
icon_map["merge|left"] = "direction_merge_left.png";
icon_map["on ramp|sharp right"] = "direction_on_ramp_sharp_right.png";
icon_map["on ramp|right"] = "direction_on_ramp_right.png";
icon_map["on ramp|slight right"] = "direction_on_ramp_slight_right.png";
icon_map["on ramp|straight"] = "direction_on_ramp_straight.png";
icon_map["on ramp|slight left"] = "direction_on_ramp_slight_left.png";
icon_map["on ramp|left"] = "direction_on_ramp_left.png";
icon_map["on ramp|sharp left"] = "direction_on_ramp_sharp_left.png";
icon_map["off ramp|slight right"] = "direction_off_ramp_slight_right.png";
icon_map["off ramp|right"] = "direction_off_ramp_right.png";
icon_map["off ramp|slight left"] = "direction_off_ramp_slight_left.png";
icon_map["off ramp|left"] = "direction_off_ramp_left.png";
icon_map["roundabout|sharp right"] = "direction_roundabout_sharp_right.png";
icon_map["roundabout|right"] = "direction_roundabout_right.png";
icon_map["roundabout|slight right"] = "direction_roundabout_slight_right.png";
icon_map["roundabout|straight"] = "direction_roundabout_straight.png";
icon_map["roundabout|slight left"] = "direction_roundabout_slight_left.png";
icon_map["roundabout|left"] = "direction_roundabout_left.png";
icon_map["roundabout|sharp left"] = "direction_roundabout_sharp_left.png";
icon_map["roundabout|"] = "direction_roundabout.png";
}
QString normalized_type = type;
if (normalized_type == "rotary") {
normalized_type = "roundabout";
} else if (normalized_type == "new name") {
normalized_type = "turn";
} else if (normalized_type == "continue") {
normalized_type = "turn";
}
QString icon_name;
QStringList keys = {normalized_type + "|" + mod, normalized_type + "|", "turn|" + mod};
for (const QString &key : keys) {
icon_name = icon_map.value(key);
if (!icon_name.isEmpty()) break;
}
if (icon_name.isEmpty()) {
icon_name = "direction_turn_straight.png";
}
return icon_name;
}
void HudRendererSP::drawNavigationHUD(QPainter &p, const QRect &surface_rect) {
if (!navigationValid) return;
p.save();
const int container_width = 1080;
const int container_height = 225;
const int container_x = (surface_rect.width() - container_width) / 2;
const int container_y = 62;
const int border_radius = 42;
QRect container_rect(container_x, container_y, container_width, container_height);
p.setPen(Qt::NoPen);
p.setBrush(QColor(0, 0, 0, 180));
p.drawRoundedRect(container_rect, border_radius, border_radius);
// Navigation icon
const int icon_size = 150;
const int icon_padding = 30;
const int icon_x = container_x + icon_padding;
const int icon_y = container_y;
QString icon_name = getNavigationIconName(navigationManeuverType, navigationModifier);
QPixmap nav_icon = loadPixmap("../../sunnypilot/selfdrive/assets/navigation/" + icon_name, {icon_size, icon_size});
if (!nav_icon.isNull()) {
p.drawPixmap(icon_x, icon_y, nav_icon);
}
// Distance
p.setFont(InterFont(48, QFont::Bold));
p.setPen(Qt::white);
QRect distance_rect(icon_x, icon_y + icon_size, icon_size, 38);
p.drawText(distance_rect, Qt::AlignCenter, navigationDistance);
const int then_section_width = 180;
const int text_x = icon_x + icon_size + 53;
const int text_area_width = container_width - (text_x - container_x) - icon_padding - then_section_width;
// Street name
p.setFont(InterFont(75, QFont::Bold));
p.setPen(Qt::white);
QFontMetrics fm(p.font());
QString street_line1, street_line2;
QStringList words = navigationStreet.split(' ');
QString currentLine;
for (int i = 0; i < words.size(); ++i) {
QString testLine = currentLine.isEmpty() ? words[i] : currentLine + " " + words[i];
if (fm.horizontalAdvance(testLine) <= text_area_width) {
currentLine = testLine;
} else {
if (street_line1.isEmpty()) {
street_line1 = currentLine;
currentLine = words[i];
} else {
break;
}
}
}
if (street_line1.isEmpty()) {
street_line1 = currentLine;
} else if (!currentLine.isEmpty()) {
street_line2 = currentLine;
if (words.size() > words.indexOf(currentLine.split(' ').last()) + 1) {
street_line2 = fm.elidedText(street_line2, Qt::ElideRight, text_area_width);
}
}
if (street_line2.isEmpty()) {
QRect street_rect(text_x, container_y + (container_height - fm.height()) / 2, text_area_width, fm.height());
p.drawText(street_rect, Qt::AlignLeft | Qt::AlignVCenter, street_line1);
} else {
QRect street_rect1(text_x, container_y + 23, text_area_width, fm.height());
p.drawText(street_rect1, Qt::AlignLeft | Qt::AlignVCenter, street_line1);
QRect street_rect2(text_x, container_y + 23 + fm.height(), text_area_width, fm.height());
p.drawText(street_rect2, Qt::AlignLeft | Qt::AlignVCenter, street_line2);
}
// Next Maneuver
if (navigationHasNext) {
const int divider_x = container_x + container_width - then_section_width - 8;
p.setPen(QPen(QColor(255, 255, 255, 50), 2));
p.drawLine(divider_x, container_y + 23, divider_x, container_y + container_height - 23);
const int then_x = divider_x + 15;
const int then_icon_size = 105;
QRect then_label_rect(then_x, container_y + 30, then_section_width - 23, 38);
p.setFont(InterFont(53, QFont::Medium));
p.setPen(Qt::white);
p.drawText(then_label_rect, Qt::AlignCenter, tr("Then"));
// Next maneuver icon
const int then_icon_x = then_x + (then_section_width - 23 - then_icon_size) / 2;
const int then_icon_y = container_y + 75;
QString next_icon_name = getNavigationIconName(navigationNextManeuverType, navigationNextModifier);
QPixmap next_nav_icon = loadPixmap("../../sunnypilot/selfdrive/assets/navigation/" + next_icon_name, {then_icon_size, then_icon_size});
if (!next_nav_icon.isNull()) {
p.drawPixmap(then_icon_x, then_icon_y, next_nav_icon);
}
}
p.restore();
}

View File

@@ -39,6 +39,8 @@ private:
void drawE2eAlert(QPainter &p, const QRect &surface_rect, const QString &alert_alt_text = "");
void drawCurrentSpeedSP(QPainter &p, const QRect &surface_rect);
void drawBlinker(QPainter &p, const QRect &surface_rect);
void drawNavigationHUD(QPainter &p, const QRect &surface_rect);
QString getNavigationIconName(const QString &type, const QString &mod);
bool lead_status;
float lead_d_rel;
@@ -120,4 +122,13 @@ private:
float speedCluster = 0;
int icbm_active_counter = 0;
bool pcmCruiseSpeed = true;
bool navigationValid;
QString navigationStreet;
QString navigationDistance;
QString navigationModifier;
QString navigationManeuverType;
QString navigationNextModifier;
QString navigationNextManeuverType;
bool navigationHasNext;
};

View File

@@ -8,6 +8,12 @@
#include "selfdrive/ui/sunnypilot/qt/onroad/model.h"
void ModelRendererSP::drawRadarPoint(QPainter &painter, const QPointF &pos, float v_rel, float radius) {
painter.setBrush(QColor(255, 255, 255, 200));
painter.setPen(Qt::NoPen);
painter.drawEllipse(pos, radius, radius);
}
void ModelRendererSP::update_model(const cereal::ModelDataV2::Reader &model, const cereal::RadarState::LeadData::Reader &lead) {
ModelRenderer::update_model(model, lead);
const auto &model_position = model.getPosition();
@@ -67,6 +73,26 @@ void ModelRendererSP::draw(QPainter &painter, const QRect &surface_rect) {
const bool right_blindspot = car_state.getRightBlindspot();
drawBlindspot(painter, surface_rect, left_blindspot, right_blindspot);
}
if (s->scene.visual_radar_tracks) {
if (sm.alive("liveTracks") && sm.rcv_frame("liveTracks") >= s->scene.started_frame) {
const auto &tracks = sm["liveTracks"].getLiveTracks().getPoints();
for (const auto &track : tracks) {
if (!std::isfinite(track.getDRel()) || !std::isfinite(track.getYRel())) continue;
float t_lag = s->scene.visual_radar_tracks_delay;
float d_pred = track.getDRel();
float y_pred = track.getYRel();
if (t_lag > 0.0f) {
d_pred += track.getVRel() * t_lag + 0.5f * track.getARel() * t_lag * t_lag;
}
QPointF screen_pt;
if (mapToScreen(d_pred, -y_pred, path_offset_z, &screen_pt)) {
drawRadarPoint(painter, screen_pt, track.getVRel(), 10.0f);
}
}
}
}
drawLeadStatus(painter, surface_rect.height(), surface_rect.width());
painter.restore();

View File

@@ -28,4 +28,6 @@ private:
// Lead status animation
float lead_status_alpha = 0.0f;
void drawRadarPoint(QPainter &painter, const QPointF &pos, float v_rel, float radius = 10.0f);
};

View File

@@ -29,7 +29,7 @@ UIStateSP::UIStateSP(QObject *parent) : UIState(parent) {
"wideRoadCameraState", "managerState", "selfdriveState", "longitudinalPlan",
"modelManagerSP", "selfdriveStateSP", "longitudinalPlanSP", "backupManagerSP",
"carControl", "gpsLocationExternal", "gpsLocation", "liveTorqueParameters",
"carStateSP", "liveParameters", "liveMapDataSP", "carParamsSP"
"carStateSP", "liveParameters", "liveMapDataSP", "carParamsSP", "navigationd", "liveTracks"
});
// update timer
@@ -44,6 +44,14 @@ UIStateSP::UIStateSP(QObject *parent) : UIState(parent) {
});
param_watcher->addParam("DevUIInfo");
param_watcher->addParam("StandstillTimer");
param_watcher->addParam("VisualRadarTracks");
param_watcher->addParam("VisualRadarTracksDelay");
param_watcher->addParam("VisualWideCam");
param_watcher->addParam("VisualStyle");
param_watcher->addParam("VisualStyleZoom");
param_watcher->addParam("VisualStyleOverhead");
param_watcher->addParam("VisualStyleOverheadZoom");
param_watcher->addParam("VisualStyleOverheadThreshold");
}
// This method overrides completely the update method from the parent class intentionally.
@@ -76,6 +84,17 @@ void ui_update_params_sp(UIStateSP *s) {
s->scene.chevron_info = std::atoi(params.get("ChevronInfo").c_str());
s->scene.blindspot_ui = params.getBool("BlindSpot");
s->scene.rainbow_mode = params.getBool("RainbowMode");
s->scene.visual_radar_tracks = QString::fromStdString(params.get("VisualRadarTracks")).toInt();
s->scene.visual_radar_tracks_delay = QString::fromStdString(params.get("VisualRadarTracksDelay")).toFloat();
s->scene.visual_wide_cam = QString::fromStdString(params.get("VisualWideCam")).toInt();
s->scene.visual_style = QString::fromStdString(params.get("VisualStyle")).toInt();
s->scene.visual_style_zoom = QString::fromStdString(params.get("VisualStyleZoom")).toInt();
s->scene.visual_style_overhead = QString::fromStdString(params.get("VisualStyleOverhead")).toInt();
s->scene.visual_style_overhead_zoom = QString::fromStdString(params.get("VisualStyleOverheadZoom")).toInt();
s->scene.visual_style_overhead_threshold = QString::fromStdString(params.get("VisualStyleOverheadThreshold")).toInt();
}
void UIStateSP::reset_onroad_sleep_timer(OnroadTimerStatusToggle toggleTimerStatus) {

View File

@@ -21,4 +21,12 @@ typedef struct UISceneSP : UIScene {
int chevron_info;
bool blindspot_ui;
bool rainbow_mode;
int visual_radar_tracks = 0;
float visual_radar_tracks_delay = 0;
int visual_wide_cam = 0;
int visual_style = 0;
int visual_style_zoom = 0;
int visual_style_overhead = 0;
int visual_style_overhead_zoom = 0;
int visual_style_overhead_threshold = 20.0;
} UISceneSP;

View File

@@ -0,0 +1,6 @@
# Navigation
Navigation daemon with Mapbox integration for semi-offline navigation. This module handles route planning, geocoding, and turn-by-turn instructions to support autonomous driving features.
- `navigation_helpers/`: Mapbox API integration and navigation instructions processing.
- `navigationd`: Navigation service which uses mapbox integration to generate a route and keep it up to date. This service runs at three hz, using keep time to ensure the while loop only updates three times a second rather than every time sm updates, which in this case would be twenty hz (LLK).

View File

View File

@@ -0,0 +1,16 @@
"""
Copyright (c) 2021-, James Vecellio, Haibin Wen, sunnypilot, and a number of other contributors.
This file is part of sunnypilot and is licensed under the MIT License.
See the LICENSE.md file in the root directory for more details.
"""
class NAV_CV:
""" These distances are expected in meters format and convert to desired format """
SHORT_DISTANCE_METERS = 200.0
QUARTER_MILE = 402.336
POINT_ONE_MILE = 160.9344
METERS_TO_KILO = 1000 # divide n by this
METERS_TO_MILE = 1609.344 # divide n by this
METERS_TO_FEET = 3.280839895 # multiply n by this

View File

@@ -126,6 +126,8 @@ def string_to_direction(direction: str) -> str:
if d in direction:
if 'slight' in direction and d in MODIFIABLE_DIRECTIONS:
return 'slight' + d.capitalize()
elif 'sharp' in direction and d in MODIFIABLE_DIRECTIONS:
return 'sharp' + d.capitalize()
return d
return 'none'

View File

@@ -0,0 +1,44 @@
"""
Copyright (c) 2021-, James Vecellio, Haibin Wen, sunnypilot, and a number of other contributors.
This file is part of sunnypilot and is licensed under the MIT License.
See the LICENSE.md file in the root directory for more details.
"""
import cereal.messaging as messaging
from cereal import car, log
from openpilot.common.constants import CV
from openpilot.common.params import Params
class NavigationDesires:
def __init__(self):
self.sm = messaging.SubMaster(['navigationd'])
self.desire = log.Desire.none
self._turn_speed_limit = 20 * CV.MPH_TO_MS
self._params = Params()
self.param_counter = -1
self.nav_allowed: bool = False
def update_params(self):
self.param_counter += 1
if self.param_counter % 60 == 0: # every 3 seconds at 20hz
self.nav_allowed = self._params.get("NavDesiresAllowed", return_default=True)
def update(self, CS: car.CarState, lateral_active: bool) -> log.Desire:
self.update_params()
self.sm.update(0)
nav_msg = self.sm['navigationd']
self.desire = log.Desire.none
if self.nav_allowed and nav_msg.valid and lateral_active:
upcoming = nav_msg.upcomingTurn
if upcoming == 'slightLeft' and (not CS.leftBlindspot or CS.vEgo < self._turn_speed_limit):
self.desire = log.Desire.keepLeft
elif upcoming == 'slightRight' and (not CS.rightBlindspot or CS.vEgo < self._turn_speed_limit):
self.desire = log.Desire.keepRight
elif (upcoming == 'left' and CS.steeringPressed and CS.steeringTorque > 0 and not CS.rightBlinker
and not CS.leftBlindspot and CS.vEgo < self._turn_speed_limit):
self.desire = log.Desire.turnLeft
elif (upcoming == 'right' and CS.steeringPressed and CS.steeringTorque < 0 and not CS.leftBlinker
and not CS.rightBlindspot and CS.vEgo < self._turn_speed_limit):
self.desire = log.Desire.turnRight
return self.desire

View File

@@ -0,0 +1,96 @@
"""
Copyright (c) 2021-, James Vecellio, Haibin Wen, sunnypilot, and a number of other contributors.
This file is part of sunnypilot and is licensed under the MIT License.
See the LICENSE.md file in the root directory for more details.
"""
import pytest
import types
from cereal import log
from openpilot.common.params import Params
from openpilot.selfdrive.controls.lib.desire_helper import DesireHelper
from openpilot.sunnypilot.navd.navigation_desires.navigation_desires import NavigationDesires
def make_car(vEgo=0, leftBlinker=False, rightBlinker=False, leftBlindspot=False, rightBlindspot=False, steeringPressed=False, steeringTorque=0):
return types.SimpleNamespace(
vEgo=vEgo, leftBlinker=leftBlinker, rightBlinker=rightBlinker,
leftBlindspot=leftBlindspot, rightBlindspot=rightBlindspot,
steeringPressed=steeringPressed, steeringTorque=steeringTorque
)
NAVIGATION_PARAMS: list[tuple] = [
('slightLeft', make_car(), log.Desire.keepLeft),
('slightRight', make_car(), log.Desire.keepRight),
('slightLeft', make_car(vEgo=9, leftBlindspot=True), log.Desire.none),
('slightRight', make_car(vEgo=9, rightBlindspot=True), log.Desire.none),
('left', make_car(vEgo=5, leftBlinker=True, rightBlinker=False, leftBlindspot=False, steeringPressed=True, steeringTorque=1), log.Desire.turnLeft),
('left', make_car(vEgo=5, leftBlinker=False, rightBlinker=True), log.Desire.none),
('right', make_car(vEgo=6, rightBlinker=True, leftBlindspot=False, steeringPressed=True, steeringTorque=-1), log.Desire.turnRight),
('right', make_car(vEgo=6, rightBlinker=True, rightBlindspot=True), log.Desire.none),
('left', make_car(vEgo=9, leftBlinker=True), log.Desire.none),
]
INTEGRATION_PARAMS: list[tuple] = [(carstate, upcoming, log.Desire.none, expected) for upcoming, carstate, expected in NAVIGATION_PARAMS] + [
(make_car(vEgo=9, leftBlinker=True, steeringPressed=True, steeringTorque=1), 'slightLeft', log.Desire.turnLeft, log.Desire.keepLeft),
(make_car(vEgo=9, rightBlinker=True, steeringPressed=True, steeringTorque=-1), 'slightRight', log.Desire.turnRight, log.Desire.keepRight),
(make_car(vEgo=9, leftBlinker=True), 'slightLeft', log.Desire.laneChangeLeft, log.Desire.laneChangeLeft),
(make_car(vEgo=9, rightBlinker=True), 'slightRight', log.Desire.laneChangeRight, log.Desire.laneChangeRight),
(make_car(vEgo=9), 'none', log.Desire.none, log.Desire.none),
]
def make_nav_msg(valid=False, upcoming='none'):
return types.SimpleNamespace(valid=valid, upcomingTurn=upcoming)
def params_setter(allowed: bool):
params = Params()
params.put("NavDesiresAllowed", allowed)
@pytest.fixture
def mock_submaster(mocker):
mock_sm = mocker.patch('cereal.messaging.SubMaster')
mock_sm_instance = mocker.Mock()
mock_sm.return_value = mock_sm_instance
mock_sm_instance.__getitem__ = mocker.Mock(return_value=make_nav_msg(valid=False))
params_setter(True)
return mock_sm_instance
@pytest.mark.parametrize("upcoming, carstate, expected", NAVIGATION_PARAMS)
def test_navigation_desires_update(mock_submaster, mocker, upcoming, carstate, expected):
nav_desires = NavigationDesires()
nav_desires.sm.__getitem__ = mocker.Mock(return_value=make_nav_msg(valid=True, upcoming=upcoming))
nav_desires.update(carstate, True)
assert nav_desires.desire == expected
@pytest.mark.parametrize("msg_valid,lateral_active", [(False, True), (True, False)])
def test_invalid_or_inactive(mock_submaster, mocker, msg_valid, lateral_active):
nav_desires = NavigationDesires()
nav_desires.sm.__getitem__ = mocker.Mock(return_value=make_nav_msg(valid=msg_valid, upcoming='slightLeft'))
nav_desires.update(make_car(), lateral_active)
assert nav_desires.desire == log.Desire.none
def test_update(mock_submaster, mocker):
mock_submaster.__getitem__ = mocker.Mock(return_value=make_nav_msg(valid=True, upcoming='left'))
nav_desires = NavigationDesires()
assert nav_desires.update(make_car(leftBlinker=True, steeringPressed=True, steeringTorque=1), True) == log.Desire.turnLeft
params_setter(False)
nav_desires.param_counter = 59
nav_desires.update(make_car(leftBlinker=True), True)
assert nav_desires.desire == log.Desire.none
@pytest.mark.parametrize("carstate, upcoming, current_desire, expected", INTEGRATION_PARAMS)
def test_desire_helper(mock_submaster, mocker, carstate, upcoming, current_desire, expected):
mock_submaster.__getitem__ = mocker.Mock(return_value=make_nav_msg(valid=True, upcoming=upcoming))
dh = DesireHelper()
dh.desire = current_desire
if current_desire in (log.Desire.laneChangeLeft, log.Desire.laneChangeRight):
dh.lane_change_state = log.LaneChangeState.laneChangeStarting
dh.lane_change_direction = log.LaneChangeDirection.left if current_desire == log.Desire.laneChangeLeft else log.LaneChangeDirection.right
dh.update(carstate, True, 1.0)
assert dh.desire == expected

View File

@@ -0,0 +1,113 @@
"""
Copyright (c) 2021-, James Vecellio, Haibin Wen, sunnypilot, and a number of other contributors.
This file is part of sunnypilot and is licensed under the MIT License.
See the LICENSE.md file in the root directory for more details.
"""
import requests
from urllib.parse import quote
from openpilot.common.params import Params
class MapboxIntegration:
def __init__(self):
self.params = Params()
def get_public_token(self) -> str:
token: str = self.params.get('MapboxToken', return_default=True)
return token
def set_destination(self, postvars, current_lon, current_lat, bearing=None) -> tuple[dict, bool]:
if 'latitude' in postvars and 'longitude' in postvars:
self.nav_confirmed(postvars, current_lon, current_lat, bearing)
return postvars, True
addr = postvars['place_name']
if not addr:
return postvars, False
token = self.get_public_token()
url = f'https://api.mapbox.com/geocoding/v5/mapbox.places/{quote(addr)}.json?access_token={token}&limit=1&proximity={current_lon},{current_lat}'
try:
response = requests.get(url, timeout=5)
if response.status_code == 200:
features = response.json()['features']
if features:
longitude, latitude = features[0]['geometry']['coordinates']
postvars.update({'latitude': latitude, 'longitude': longitude, 'name': addr})
self.nav_confirmed(postvars, current_lon, current_lat, bearing)
return postvars, True
except requests.RequestException:
pass # Broad exception to handle network errors like no internet without crashing navd process.
return postvars, False
def nav_confirmed(self, postvars, start_lon, start_lat, bearing=None) -> None:
if not postvars:
return
latitude = float(postvars['latitude'])
longitude = float(postvars['longitude'])
data: dict = {'navData': {'current': {'latitude': latitude, 'longitude': longitude}, 'route': {}}}
token = self.get_public_token()
route_data = self.generate_route(start_lon, start_lat, longitude, latitude, token, bearing)
if route_data:
data['navData']['route'] = route_data
self.params.put('MapboxSettings', data)
@staticmethod
def generate_route(start_lon, start_lat, end_lon, end_lat, token, bearing=None) -> dict | None:
if not token:
return None
params = {
'access_token': token,
'geometries': 'geojson',
'steps': 'true',
'overview': 'full',
'annotations': 'maxspeed',
'alternatives': 'false',
'banner_instructions': 'true',
}
if bearing is not None:
params['bearings'] = f'{int((bearing + 360) % 360):.0f},90;'
try:
response = requests.get(f'https://api.mapbox.com/directions/v5/mapbox/driving/{start_lon},{start_lat};{end_lon},{end_lat}', params=params, timeout=5)
data = response.json() if response.status_code == 200 else {}
except requests.RequestException:
return None
routes = data['routes'] if data else None
legs = routes[0]['legs'] if routes else None
if data.get('code') != 'Ok' or not routes or not legs:
return None
route = routes[0]
leg = legs[0]
steps = [
{
'maneuver': step['maneuver']['type'],
'instruction': step['maneuver']['instruction'],
'distance': step['distance'],
'duration': step['duration'],
'location': {'longitude': step['maneuver']['location'][0], 'latitude': step['maneuver']['location'][1]},
'modifier': step['maneuver'].get('modifier', 'none'),
'bannerInstructions': step['bannerInstructions'],
}
for step in leg['steps']
]
maxspeed = [{'speed': item['speed'], 'unit': item['unit']} for item in leg['annotation']['maxspeed'] if 'speed' in item]
return {
'steps': steps,
'totalDistance': route['distance'],
'totalDuration': route['duration'],
'geometry': [{'longitude': coord[0], 'latitude': coord[1]} for coord in route['geometry']['coordinates']],
'maxspeed': maxspeed,
}

View File

@@ -0,0 +1,138 @@
"""
Copyright (c) 2021-, James Vecellio, Haibin Wen, sunnypilot, and a number of other contributors.
This file is part of sunnypilot and is licensed under the MIT License.
See the LICENSE.md file in the root directory for more details.
"""
from openpilot.common.constants import CV
from openpilot.common.params import Params
from openpilot.sunnypilot.navd.helpers import Coordinate, string_to_direction
class NavigationInstructions:
def __init__(self):
self.coord = Coordinate(0, 0)
self.params = Params()
self._cached_route = None
self._route_loaded = False
self._no_route = False
def get_route_progress(self, current_lat, current_lon) -> dict | None:
route = self.get_current_route()
if not route or not route['geometry'] or not route['steps']:
return None
self.coord.latitude = current_lat
self.coord.longitude = current_lon
# Find the closest point on the route relative to self
closest_idx, min_distance = min(((idx, self.coord.distance_to(coord)) for idx, coord in enumerate(route['geometry'])), key=lambda x: x[1])
closest_cumulative = route['cumulative_distances'][closest_idx]
# Find the current step index, which is the HIGHEST idx where the step location cumulative less/equal closest cumulative
current_step_idx = max((idx for idx, step in enumerate(route['steps']) if step['cumulative_distance'] <= closest_cumulative), default=-1)
current_step = route['steps'][current_step_idx if current_step_idx >= 0 else 0]
# The next turn is the next step relative to our cumulative index
next_turn_idx = current_step_idx + 1
next_turn = route['steps'][next_turn_idx] if 0 <= next_turn_idx < len(route['steps']) else None
current_maxspeed = current_step['maxspeed']
distance_to_end_of_step = max(0, current_step['distance'] - (closest_cumulative - current_step['cumulative_distance']))
all_maneuvers: list = []
max_maneuvers = 3
for idx in range(current_step_idx, min(current_step_idx + max_maneuvers, len(route['steps']))):
step = route['steps'][idx]
if idx == current_step_idx:
distance = distance_to_end_of_step
else:
distance = step['cumulative_distance'] - closest_cumulative
all_maneuvers.append({'distance': distance, 'type': step['maneuver'], 'modifier': step['modifier'], 'instruction': step['instruction']})
return {
'distance_from_route': min_distance,
'current_step': current_step,
'next_turn': next_turn,
'current_maxspeed': current_maxspeed,
'all_maneuvers': all_maneuvers,
'current_step_idx': current_step_idx,
'distance_to_end_of_step': distance_to_end_of_step,
}
def get_current_route(self):
if self._route_loaded and self._cached_route is not None:
return self._cached_route
if self._no_route:
return None
param_value = self.params.get('MapboxSettings')
route = param_value['navData']['route'] if param_value else None
if not route:
self._no_route = True
return None
geometry = [Coordinate(coord['latitude'], coord['longitude']) for coord in route['geometry']]
cumulative_distances = [0.0]
cumulative_distances.extend(cumulative_distances[-1] + geometry[step - 1].distance_to(geometry[step]) for step in range(1, len(geometry)))
maxspeed = [(speed['speed'], speed['unit']) for speed in route['maxspeed']]
steps = []
for step in route['steps']:
location = Coordinate(step['location']['latitude'], step['location']['longitude'])
closest_idx = min(range(len(geometry)), key=lambda i: location.distance_to(geometry[i]))
steps.append({
'bannerInstructions': step['bannerInstructions'],
'distance': step['distance'],
'duration': step['duration'],
'maneuver': step['maneuver'],
'location': location,
'cumulative_distance': cumulative_distances[closest_idx],
'maxspeed': maxspeed[min(closest_idx, len(maxspeed) - 1)] if len(maxspeed) > 0 else (0, 'kmh'),
'modifier': string_to_direction(step['modifier']),
'instruction': step['instruction'],
})
self._cached_route = {
'steps': steps,
'total_distance': route['totalDistance'],
'total_duration': route['totalDuration'],
'geometry': geometry,
'cumulative_distances': cumulative_distances,
'maxspeed': maxspeed,
}
self._route_loaded = True
return self._cached_route
def clear_route_cache(self):
self._cached_route = None
self._route_loaded = False
self._no_route = False
def get_upcoming_turn_from_progress(self, progress, current_lat, current_lon) -> str:
if progress and progress['next_turn']:
self.coord.latitude = current_lat
self.coord.longitude = current_lon
distance = self.coord.distance_to(progress['next_turn']['location'])
if distance <= 30.0:
modifier = progress['next_turn']['modifier']
return str(modifier)
return 'none'
@staticmethod
def get_current_speed_limit_from_progress(progress, is_metric: bool) -> int:
if progress and progress['current_maxspeed']:
speed, _ = progress['current_maxspeed']
if is_metric:
return int(speed)
else:
return int(round(speed * CV.KPH_TO_MPH))
return 0
@staticmethod
def arrived_at_destination(progress) -> bool:
if progress['all_maneuvers'][0]['type'] == 'arrive':
return True
elif progress['all_maneuvers'][0]['instruction'].startswith('Your destination'):
return True
return False

View File

@@ -0,0 +1,94 @@
"""
Copyright (c) 2021-, James Vecellio, Haibin Wen, sunnypilot, and a number of other contributors.
This file is part of sunnypilot and is licensed under the MIT License.
See the LICENSE.md file in the root directory for more details.
"""
import os
from openpilot.common.constants import CV
from openpilot.sunnypilot.navd.navigation_helpers.mapbox_integration import MapboxIntegration
from openpilot.sunnypilot.navd.navigation_helpers.nav_instructions import NavigationInstructions
class TestMapbox:
@classmethod
def setup_class(cls):
cls.mapbox = MapboxIntegration()
cls.nav = NavigationInstructions()
token = os.environ.get('MAPBOX_TOKEN_CI')
if token:
cls.mapbox.params.put('MapboxToken', token)
# route setup
cls.current_lon, cls.current_lat = -119.17557, 34.23305
cls.mapbox.params.put('MapboxRoute', '740 E Ventura Blvd. Camarillo, CA')
cls.postvars = {"place_name": cls.mapbox.params.get('MapboxRoute')}
cls.postvars, cls.valid_addr = cls.mapbox.set_destination(cls.postvars, cls.current_lon, cls.current_lat)
cls.route = cls.nav.get_current_route()
cls.progress = cls.nav.get_route_progress(cls.current_lat, cls.current_lon)
def test_set_destination(self):
assert self.valid_addr
settings = self.mapbox.params.get('MapboxSettings')
assert settings is not None
dest_lat = settings['navData']['current']['latitude']
dest_lon = settings['navData']['current']['longitude']
assert dest_lat == self.postvars["latitude"] and dest_lon == self.postvars["longitude"]
def test_get_route(self):
assert self.route is not None
assert 'steps' in self.route
assert 'geometry' in self.route
assert 'maxspeed' in self.route
assert 'total_distance' in self.route
assert 'total_duration' in self.route
assert len(self.route['steps']) > 0
assert len(self.route['geometry']) > 0
assert len(self.route['maxspeed']) > 0
if self.route and 'steps' in self.route:
for step in self.route['steps']:
assert 'modifier' in step
def test_upcoming_turn_detection(self):
upcoming = self.nav.get_upcoming_turn_from_progress(self.progress, self.current_lat, self.current_lon)
assert isinstance(upcoming, str)
assert upcoming == 'none'
if self.route['steps']:
turn_lat = self.route['steps'][1]['location'].latitude
turn_lon = self.route['steps'][1]['location'].longitude
close_lat = turn_lat - 0.00025 # slightly before the turn
if self.progress and self.progress.get('next_turn'):
expected_turn = self.progress['next_turn']['modifier']
upcoming_close = self.nav.get_upcoming_turn_from_progress(self.progress, close_lat, turn_lon)
if expected_turn:
assert upcoming_close == expected_turn == 'right', "Should be a right turn upcoming"
def test_route_progress_tracking(self):
assert self.progress is not None
assert 'distance_from_route' in self.progress
assert 'next_turn' in self.progress
assert 'current_maxspeed' in self.progress
assert 'all_maneuvers' in self.progress
assert 'distance_to_end_of_step' in self.progress
assert self.progress['distance_from_route'] >= 0
assert isinstance(self.progress['all_maneuvers'], list)
def test_speed_limit_handling(self):
speed_limit_metric = self.nav.get_current_speed_limit_from_progress(self.progress, True)
speed_limit_imperial = self.nav.get_current_speed_limit_from_progress(self.progress, False)
assert isinstance(speed_limit_metric, int)
assert isinstance(speed_limit_imperial, int)
expected_metric = int(self.progress['current_maxspeed'][0])
expected_imperial = int(round(self.progress['current_maxspeed'][0] * CV.KPH_TO_MPH))
assert speed_limit_metric == expected_metric
assert speed_limit_imperial == expected_imperial
def test_arrival_detection(self):
is_arrived = self.nav.arrived_at_destination(self.progress)
assert isinstance(is_arrived, bool)
assert not is_arrived

160
sunnypilot/navd/navigationd.py Executable file
View File

@@ -0,0 +1,160 @@
"""
Copyright (c) 2021-, James Vecellio, Haibin Wen, sunnypilot, and a number of other contributors.
This file is part of sunnypilot and is licensed under the MIT License.
See the LICENSE.md file in the root directory for more details.
"""
import math
import cereal.messaging as messaging
from cereal import custom
from openpilot.common.params import Params
from openpilot.common.realtime import Ratekeeper
from openpilot.common.swaglog import cloudlog
from openpilot.sunnypilot.navd.constants import NAV_CV
from openpilot.sunnypilot.navd.helpers import Coordinate, parse_banner_instructions
from openpilot.sunnypilot.navd.navigation_helpers.mapbox_integration import MapboxIntegration
from openpilot.sunnypilot.navd.navigation_helpers.nav_instructions import NavigationInstructions
class Navigationd:
def __init__(self):
self.params = Params()
self.mapbox = MapboxIntegration()
self.nav_instructions = NavigationInstructions()
self.sm = messaging.SubMaster(['liveLocationKalman'])
self.pm = messaging.PubMaster(['navigationd'])
self.rk = Ratekeeper(3) # 3 Hz
self.route = None
self.destination: str | None = None
self.new_destination: str = ''
self.allow_navigation: bool = False
self.recompute_allowed: bool = False
self.allow_recompute: bool = False
self.reroute_counter: int = 0
self.cancel_route_counter: int = 0
self.frame: int = -1
self.last_position: Coordinate | None = None
self.last_bearing: float | None = None
self.is_metric: bool = False
self.valid: bool = False
def _update_params(self):
if self.last_position is not None:
self.frame += 1
if self.frame % 9 == 0:
self.allow_navigation = self.params.get('AllowNavigation', return_default=True)
self.is_metric = self.params.get('IsMetric', return_default=True)
self.new_destination = self.params.get('MapboxRoute')
self.recompute_allowed = self.params.get('MapboxRecompute', return_default=True)
self.allow_recompute: bool = (self.new_destination != self.destination and self.new_destination != '') or (
self.recompute_allowed and self.reroute_counter > 9 and self.route
)
if self.allow_recompute:
postvars = {'place_name': self.new_destination}
postvars, valid_addr = self.mapbox.set_destination(postvars, self.last_position.longitude, self.last_position.latitude, self.last_bearing)
cloudlog.debug(f'Set new destination to: {self.new_destination}, valid: {valid_addr}')
if valid_addr:
self.destination = self.new_destination
self.nav_instructions.clear_route_cache()
self.route = self.nav_instructions.get_current_route()
self.cancel_route_counter = 0
self.reroute_counter = 0
if self.cancel_route_counter == 30:
self.cancel_route_counter = 0
self.destination = None
self.nav_instructions.clear_route_cache()
self.route = None
self.valid = self.route is not None
def _update_navigation(self) -> tuple[str, dict | None, dict]:
banner_instructions: str = ''
nav_data: dict = {}
if self.allow_navigation and self.last_position is not None:
if progress := self.nav_instructions.get_route_progress(self.last_position.latitude, self.last_position.longitude):
nav_data['upcoming_turn'] = self.nav_instructions.get_upcoming_turn_from_progress(progress, self.last_position.latitude, self.last_position.longitude)
nav_data['current_speed_limit'] = self.nav_instructions.get_current_speed_limit_from_progress(progress, self.is_metric)
arrived = self.nav_instructions.arrived_at_destination(progress)
if progress['current_step']:
parsed = parse_banner_instructions(progress['current_step']['bannerInstructions'], progress['distance_to_end_of_step'])
if parsed:
banner_instructions = parsed['maneuverPrimaryText']
nav_data['distance_from_route'] = progress['distance_from_route']
large_distance = progress['distance_from_route'] > 100
if large_distance:
self.cancel_route_counter = self.cancel_route_counter + 1 if progress['distance_from_route'] > NAV_CV.QUARTER_MILE else 0
if self.recompute_allowed:
self.reroute_counter += 1
elif arrived:
self.cancel_route_counter += 1
else:
self.cancel_route_counter = 0
self.reroute_counter = 0
# Don't recompute in last segment to prevent reroute loops
if self.route:
if progress['current_step_idx'] == len(self.route['steps']) - 1:
self.allow_recompute = False
else:
banner_instructions = ''
progress = None
nav_data = {}
self.valid = False
return banner_instructions, progress, nav_data
def _build_navigation_message(self, banner_instructions: str, progress: dict | None, nav_data: dict, valid: bool):
msg = messaging.new_message('navigationd')
msg.valid = valid
msg.navigationd.upcomingTurn = nav_data.get('upcoming_turn', 'none')
msg.navigationd.currentSpeedLimit = nav_data.get('current_speed_limit', 0)
msg.navigationd.bannerInstructions = banner_instructions
msg.navigationd.distanceFromRoute = nav_data.get('distance_from_route', 0.0)
msg.navigationd.valid = self.valid
all_maneuvers = (
[custom.Navigationd.Maneuver.new_message(distance=m['distance'], type=m['type'], modifier=m['modifier'],
instruction=m['instruction']) for m in progress['all_maneuvers']]
if progress
else []
)
msg.navigationd.allManeuvers = all_maneuvers
return msg
def run(self):
cloudlog.warning('navigationd init')
while True:
self.sm.update()
location = self.sm['liveLocationKalman']
localizer_valid = location.positionGeodetic.valid if location else False
if localizer_valid:
self.last_bearing = math.degrees(location.calibratedOrientationNED.value[2])
self.last_position = Coordinate(location.positionGeodetic.value[0], location.positionGeodetic.value[1])
self._update_params()
banner_instructions, progress, nav_data = self._update_navigation()
msg = self._build_navigation_message(banner_instructions, progress, nav_data, valid=localizer_valid)
self.pm.send('navigationd', msg)
self.rk.keep_time()
def main():
nav = Navigationd()
nav.run()

View File

View File

@@ -0,0 +1,76 @@
"""
Copyright (c) 2021-, James Vecellio, Haibin Wen, sunnypilot, and a number of other contributors.
This file is part of sunnypilot and is licensed under the MIT License.
See the LICENSE.md file in the root directory for more details.
"""
import platform
import pytest
import cereal.messaging as messaging
from openpilot.sunnypilot.navd.navigationd import Navigationd
from openpilot.sunnypilot.navd.helpers import Coordinate
class TestNavigationd:
is_darwin = platform.system() == "Darwin"
@pytest.fixture(autouse=True)
def setup_method(self, mocker):
if self.is_darwin:
mocker.patch('cereal.messaging.SubMaster')
mocker.patch('cereal.messaging.PubMaster')
def test_update_params(self):
nav = Navigationd()
nav.last_position = None
nav._update_params()
assert nav.frame == -1
nav.last_position = Coordinate(latitude=37.0, longitude=128.0)
nav._update_params()
assert nav.frame == 0 # frame only updates when last position is set
def test_update_navigation_no_position(self):
nav = Navigationd()
nav.last_position = None
banner, progress, nav_data = nav._update_navigation()
assert banner == ''
assert progress is None
assert nav_data == {}
def test_update_navigation(self):
nav = Navigationd()
nav.last_position = Coordinate(latitude=37.0, longitude=128.0)
nav.route = {'580 Winchester dr, oxnard, CA': True}
banner, progress, nav_data = nav._update_navigation()
assert isinstance(banner, str)
assert not progress # no route was actually set
assert isinstance(nav_data, dict)
def test_build_navigation_message(self):
if self.is_darwin:
nav = Navigationd()
msg = nav._build_navigation_message('', None, {}, True)
assert msg.navigationd.bannerInstructions == ''
assert msg.navigationd.valid is False
else:
sm = messaging.SubMaster(['navigationd'])
nav = Navigationd()
msg = nav._build_navigation_message('', None, {}, True)
nav.pm.send('navigationd', msg)
sm.update()
received_msg = sm['navigationd']
assert received_msg.bannerInstructions == msg.navigationd.bannerInstructions
assert received_msg.valid == msg.navigationd.valid
def test_cancel_route(self):
nav = Navigationd()
nav.last_position = Coordinate(latitude=37.0, longitude=128.0)
nav.route = {'580 Winchester dr, oxnard, CA': True}
nav.cancel_route_counter = 30
nav._update_params()
assert nav.route is None
assert nav.destination is None

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Some files were not shown because too many files have changed in this diff Show More