StarPilot
@@ -0,0 +1,125 @@
|
||||
info face="Inter-Black" size=-200 bold=0 italic=0 charset="" unicode=1 stretchH=100 smooth=0 aa=1 padding=0,0,0,0 spacing=0,0 outline=0
|
||||
common lineHeight=200 base=200 scaleW=2048 scaleH=2048 pages=1 packed=0 alphaChnl=0 redChnl=4 greenChnl=4 blueChnl=4
|
||||
page id=0 file="Inter-Black.png"
|
||||
chars count=121
|
||||
char id=32 x=6 y=6 width=32 height=200 xoffset=0 yoffset=0 xadvance=32 page=0 chnl=15
|
||||
char id=33 x=50 y=6 width=34 height=123 xoffset=12 yoffset=39 xadvance=58 page=0 chnl=15
|
||||
char id=34 x=96 y=6 width=53 height=50 xoffset=5 yoffset=39 xadvance=63 page=0 chnl=15
|
||||
char id=35 x=161 y=6 width=109 height=121 xoffset=0 yoffset=39 xadvance=109 page=0 chnl=15
|
||||
char id=36 x=282 y=6 width=102 height=152 xoffset=4 yoffset=24 xadvance=110 page=0 chnl=15
|
||||
char id=37 x=396 y=6 width=121 height=133 xoffset=13 yoffset=33 xadvance=147 page=0 chnl=15
|
||||
char id=38 x=529 y=6 width=112 height=125 xoffset=5 yoffset=37 xadvance=115 page=0 chnl=15
|
||||
char id=39 x=653 y=6 width=22 height=50 xoffset=5 yoffset=39 xadvance=33 page=0 chnl=15
|
||||
char id=40 x=687 y=6 width=57 height=149 xoffset=14 yoffset=39 xadvance=72 page=0 chnl=15
|
||||
char id=41 x=756 y=6 width=57 height=149 xoffset=2 yoffset=39 xadvance=72 page=0 chnl=15
|
||||
char id=42 x=825 y=6 width=69 height=69 xoffset=16 yoffset=39 xadvance=100 page=0 chnl=15
|
||||
char id=43 x=906 y=6 width=88 height=88 xoffset=13 yoffset=67 xadvance=114 page=0 chnl=15
|
||||
char id=44 x=1006 y=6 width=33 height=47 xoffset=7 yoffset=143 xadvance=52 page=0 chnl=15
|
||||
char id=45 x=1051 y=6 width=57 height=26 xoffset=11 yoffset=99 xadvance=78 page=0 chnl=15
|
||||
char id=46 x=1120 y=6 width=34 height=33 xoffset=9 yoffset=129 xadvance=51 page=0 chnl=15
|
||||
char id=47 x=1166 y=6 width=68 height=145 xoffset=0 yoffset=34 xadvance=68 page=0 chnl=15
|
||||
char id=48 x=1246 y=6 width=107 height=126 xoffset=7 yoffset=38 xadvance=120 page=0 chnl=15
|
||||
char id=49 x=1365 y=6 width=60 height=121 xoffset=7 yoffset=39 xadvance=83 page=0 chnl=15
|
||||
char id=50 x=1437 y=6 width=96 height=122 xoffset=5 yoffset=38 xadvance=106 page=0 chnl=15
|
||||
char id=51 x=1545 y=6 width=100 height=124 xoffset=6 yoffset=38 xadvance=111 page=0 chnl=15
|
||||
char id=52 x=1657 y=6 width=105 height=121 xoffset=6 yoffset=39 xadvance=115 page=0 chnl=15
|
||||
char id=53 x=1774 y=6 width=97 height=123 xoffset=8 yoffset=39 xadvance=110 page=0 chnl=15
|
||||
char id=54 x=1883 y=6 width=101 height=125 xoffset=6 yoffset=37 xadvance=113 page=0 chnl=15
|
||||
char id=55 x=6 y=218 width=90 height=121 xoffset=5 yoffset=39 xadvance=100 page=0 chnl=15
|
||||
char id=56 x=108 y=218 width=102 height=124 xoffset=6 yoffset=38 xadvance=114 page=0 chnl=15
|
||||
char id=57 x=222 y=218 width=102 height=125 xoffset=6 yoffset=37 xadvance=113 page=0 chnl=15
|
||||
char id=58 x=336 y=218 width=34 height=94 xoffset=9 yoffset=68 xadvance=51 page=0 chnl=15
|
||||
char id=59 x=382 y=218 width=38 height=122 xoffset=7 yoffset=68 xadvance=52 page=0 chnl=15
|
||||
char id=60 x=432 y=218 width=88 height=99 xoffset=13 yoffset=62 xadvance=114 page=0 chnl=15
|
||||
char id=61 x=532 y=218 width=88 height=69 xoffset=13 yoffset=77 xadvance=114 page=0 chnl=15
|
||||
char id=62 x=632 y=218 width=88 height=99 xoffset=13 yoffset=62 xadvance=114 page=0 chnl=15
|
||||
char id=63 x=732 y=218 width=86 height=124 xoffset=6 yoffset=38 xadvance=99 page=0 chnl=15
|
||||
char id=64 x=830 y=218 width=164 height=160 xoffset=11 yoffset=37 xadvance=180 page=0 chnl=15
|
||||
char id=65 x=1006 y=218 width=125 height=121 xoffset=3 yoffset=39 xadvance=131 page=0 chnl=15
|
||||
char id=66 x=1143 y=218 width=100 height=121 xoffset=7 yoffset=39 xadvance=110 page=0 chnl=15
|
||||
char id=67 x=1255 y=218 width=113 height=124 xoffset=7 yoffset=38 xadvance=126 page=0 chnl=15
|
||||
char id=68 x=1380 y=218 width=107 height=121 xoffset=7 yoffset=39 xadvance=121 page=0 chnl=15
|
||||
char id=69 x=1499 y=218 width=88 height=121 xoffset=7 yoffset=39 xadvance=102 page=0 chnl=15
|
||||
char id=70 x=1599 y=218 width=86 height=121 xoffset=7 yoffset=39 xadvance=96 page=0 chnl=15
|
||||
char id=71 x=1697 y=218 width=114 height=124 xoffset=7 yoffset=38 xadvance=127 page=0 chnl=15
|
||||
char id=72 x=1823 y=218 width=110 height=121 xoffset=7 yoffset=39 xadvance=123 page=0 chnl=15
|
||||
char id=73 x=1945 y=218 width=34 height=121 xoffset=7 yoffset=39 xadvance=48 page=0 chnl=15
|
||||
char id=74 x=6 y=430 width=89 height=123 xoffset=1 yoffset=39 xadvance=97 page=0 chnl=15
|
||||
char id=75 x=107 y=430 width=111 height=121 xoffset=7 yoffset=39 xadvance=118 page=0 chnl=15
|
||||
char id=76 x=230 y=430 width=82 height=121 xoffset=7 yoffset=39 xadvance=94 page=0 chnl=15
|
||||
char id=77 x=324 y=430 width=140 height=121 xoffset=7 yoffset=39 xadvance=153 page=0 chnl=15
|
||||
char id=78 x=476 y=430 width=105 height=121 xoffset=7 yoffset=39 xadvance=119 page=0 chnl=15
|
||||
char id=79 x=593 y=430 width=118 height=124 xoffset=7 yoffset=38 xadvance=131 page=0 chnl=15
|
||||
char id=80 x=723 y=430 width=98 height=121 xoffset=7 yoffset=39 xadvance=108 page=0 chnl=15
|
||||
char id=81 x=833 y=430 width=118 height=131 xoffset=7 yoffset=38 xadvance=131 page=0 chnl=15
|
||||
char id=82 x=963 y=430 width=102 height=121 xoffset=7 yoffset=39 xadvance=110 page=0 chnl=15
|
||||
char id=83 x=1077 y=430 width=102 height=124 xoffset=4 yoffset=38 xadvance=110 page=0 chnl=15
|
||||
char id=84 x=1191 y=430 width=105 height=121 xoffset=4 yoffset=39 xadvance=113 page=0 chnl=15
|
||||
char id=85 x=1308 y=430 width=105 height=123 xoffset=7 yoffset=39 xadvance=119 page=0 chnl=15
|
||||
char id=86 x=1425 y=430 width=125 height=121 xoffset=3 yoffset=39 xadvance=131 page=0 chnl=15
|
||||
char id=87 x=1562 y=430 width=176 height=121 xoffset=2 yoffset=39 xadvance=180 page=0 chnl=15
|
||||
char id=88 x=1750 y=430 width=118 height=121 xoffset=4 yoffset=39 xadvance=126 page=0 chnl=15
|
||||
char id=89 x=1880 y=430 width=122 height=121 xoffset=2 yoffset=39 xadvance=126 page=0 chnl=15
|
||||
char id=90 x=6 y=642 width=99 height=121 xoffset=8 yoffset=39 xadvance=114 page=0 chnl=15
|
||||
char id=91 x=117 y=642 width=55 height=149 xoffset=16 yoffset=39 xadvance=72 page=0 chnl=15
|
||||
char id=92 x=184 y=642 width=68 height=145 xoffset=0 yoffset=34 xadvance=68 page=0 chnl=15
|
||||
char id=93 x=264 y=642 width=55 height=149 xoffset=2 yoffset=39 xadvance=72 page=0 chnl=15
|
||||
char id=94 x=331 y=642 width=74 height=54 xoffset=4 yoffset=41 xadvance=82 page=0 chnl=15
|
||||
char id=95 x=417 y=642 width=82 height=24 xoffset=0 yoffset=160 xadvance=81 page=0 chnl=15
|
||||
char id=96 x=511 y=642 width=40 height=27 xoffset=21 yoffset=32 xadvance=82 page=0 chnl=15
|
||||
char id=97 x=563 y=642 width=88 height=94 xoffset=3 yoffset=68 xadvance=97 page=0 chnl=15
|
||||
char id=98 x=663 y=642 width=95 height=123 xoffset=7 yoffset=39 xadvance=106 page=0 chnl=15
|
||||
char id=99 x=770 y=642 width=92 height=94 xoffset=4 yoffset=68 xadvance=100 page=0 chnl=15
|
||||
char id=100 x=874 y=642 width=94 height=123 xoffset=5 yoffset=39 xadvance=106 page=0 chnl=15
|
||||
char id=101 x=980 y=642 width=92 height=94 xoffset=4 yoffset=68 xadvance=100 page=0 chnl=15
|
||||
char id=102 x=1084 y=642 width=66 height=126 xoffset=1 yoffset=34 xadvance=66 page=0 chnl=15
|
||||
char id=103 x=1162 y=642 width=95 height=127 xoffset=5 yoffset=68 xadvance=107 page=0 chnl=15
|
||||
char id=104 x=1269 y=642 width=92 height=121 xoffset=8 yoffset=39 xadvance=106 page=0 chnl=15
|
||||
char id=105 x=1373 y=642 width=33 height=130 xoffset=8 yoffset=30 xadvance=48 page=0 chnl=15
|
||||
char id=106 x=1418 y=642 width=47 height=164 xoffset=-6 yoffset=30 xadvance=48 page=0 chnl=15
|
||||
char id=107 x=1477 y=642 width=93 height=121 xoffset=8 yoffset=39 xadvance=100 page=0 chnl=15
|
||||
char id=108 x=1582 y=642 width=33 height=121 xoffset=8 yoffset=39 xadvance=48 page=0 chnl=15
|
||||
char id=109 x=1627 y=642 width=140 height=92 xoffset=8 yoffset=68 xadvance=155 page=0 chnl=15
|
||||
char id=110 x=1779 y=642 width=92 height=92 xoffset=8 yoffset=68 xadvance=106 page=0 chnl=15
|
||||
char id=111 x=1883 y=642 width=95 height=94 xoffset=4 yoffset=68 xadvance=103 page=0 chnl=15
|
||||
char id=112 x=6 y=854 width=94 height=126 xoffset=8 yoffset=68 xadvance=107 page=0 chnl=15
|
||||
char id=113 x=112 y=854 width=94 height=126 xoffset=5 yoffset=68 xadvance=107 page=0 chnl=15
|
||||
char id=114 x=218 y=854 width=62 height=92 xoffset=8 yoffset=68 xadvance=71 page=0 chnl=15
|
||||
char id=115 x=292 y=854 width=89 height=94 xoffset=4 yoffset=68 xadvance=97 page=0 chnl=15
|
||||
char id=116 x=393 y=854 width=62 height=114 xoffset=2 yoffset=48 xadvance=66 page=0 chnl=15
|
||||
char id=117 x=467 y=854 width=92 height=93 xoffset=8 yoffset=69 xadvance=107 page=0 chnl=15
|
||||
char id=118 x=571 y=854 width=100 height=91 xoffset=0 yoffset=69 xadvance=100 page=0 chnl=15
|
||||
char id=119 x=683 y=854 width=142 height=91 xoffset=1 yoffset=69 xadvance=144 page=0 chnl=15
|
||||
char id=120 x=837 y=854 width=95 height=91 xoffset=2 yoffset=69 xadvance=98 page=0 chnl=15
|
||||
char id=121 x=944 y=854 width=100 height=125 xoffset=0 yoffset=69 xadvance=100 page=0 chnl=15
|
||||
char id=122 x=1056 y=854 width=80 height=91 xoffset=9 yoffset=69 xadvance=97 page=0 chnl=15
|
||||
char id=123 x=1148 y=854 width=67 height=149 xoffset=4 yoffset=39 xadvance=72 page=0 chnl=15
|
||||
char id=124 x=1227 y=854 width=33 height=198 xoffset=17 yoffset=1 xadvance=66 page=0 chnl=15
|
||||
char id=125 x=1272 y=854 width=67 height=149 xoffset=2 yoffset=39 xadvance=72 page=0 chnl=15
|
||||
char id=126 x=1351 y=854 width=93 height=43 xoffset=11 yoffset=90 xadvance=114 page=0 chnl=15
|
||||
char id=163 x=1456 y=854 width=94 height=122 xoffset=11 yoffset=38 xadvance=115 page=0 chnl=15
|
||||
char id=165 x=1562 y=854 width=93 height=121 xoffset=2 yoffset=39 xadvance=97 page=0 chnl=15
|
||||
char id=167 x=1667 y=854 width=85 height=147 xoffset=9 yoffset=38 xadvance=102 page=0 chnl=15
|
||||
char id=176 x=1764 y=854 width=61 height=61 xoffset=8 yoffset=39 xadvance=76 page=0 chnl=15
|
||||
char id=178 x=1837 y=854 width=62 height=74 xoffset=4 yoffset=26 xadvance=70 page=0 chnl=15
|
||||
char id=215 x=1911 y=854 width=93 height=94 xoffset=11 yoffset=64 xadvance=114 page=0 chnl=15
|
||||
char id=231 x=6 y=1066 width=92 height=132 xoffset=4 yoffset=68 xadvance=100 page=0 chnl=15
|
||||
char id=234 x=110 y=1066 width=92 height=131 xoffset=4 yoffset=31 xadvance=100 page=0 chnl=15
|
||||
char id=241 x=214 y=1066 width=92 height=127 xoffset=8 yoffset=33 xadvance=106 page=0 chnl=15
|
||||
char id=252 x=318 y=1066 width=92 height=130 xoffset=8 yoffset=32 xadvance=107 page=0 chnl=15
|
||||
char id=8209 x=422 y=1066 width=57 height=26 xoffset=18 yoffset=99 xadvance=88 page=0 chnl=15
|
||||
char id=8211 x=491 y=1066 width=83 height=26 xoffset=0 yoffset=99 xadvance=82 page=0 chnl=15
|
||||
char id=8226 x=586 y=1066 width=52 height=52 xoffset=7 yoffset=86 xadvance=65 page=0 chnl=15
|
||||
char id=8364 x=650 y=1066 width=103 height=124 xoffset=5 yoffset=38 xadvance=115 page=0 chnl=15
|
||||
char id=8627 x=765 y=1066 width=139 height=112 xoffset=16 yoffset=39 xadvance=170 page=0 chnl=15
|
||||
char id=8679 x=916 y=1066 width=170 height=130 xoffset=11 yoffset=30 xadvance=191 page=0 chnl=15
|
||||
char id=9003 x=1098 y=1066 width=173 height=121 xoffset=13 yoffset=39 xadvance=200 page=0 chnl=15
|
||||
char id=9251 x=1283 y=1066 width=110 height=41 xoffset=10 yoffset=148 xadvance=130 page=0 chnl=15
|
||||
char id=9654 x=1405 y=1066 width=135 height=152 xoffset=18 yoffset=24 xadvance=162 page=0 chnl=15
|
||||
char id=9664 x=1552 y=1066 width=135 height=152 xoffset=9 yoffset=24 xadvance=162 page=0 chnl=15
|
||||
char id=9675 x=1699 y=1066 width=125 height=125 xoffset=13 yoffset=37 xadvance=151 page=0 chnl=15
|
||||
char id=9679 x=1836 y=1066 width=125 height=125 xoffset=13 yoffset=37 xadvance=151 page=0 chnl=15
|
||||
char id=9881 x=1973 y=1066 width=0 height=0 xoffset=0 yoffset=0 xadvance=0 page=0 chnl=15
|
||||
char id=10003 x=6 y=1278 width=127 height=101 xoffset=14 yoffset=57 xadvance=145 page=0 chnl=15
|
||||
char id=10004 x=145 y=1278 width=0 height=0 xoffset=0 yoffset=0 xadvance=0 page=0 chnl=15
|
||||
char id=10005 x=157 y=1278 width=0 height=0 xoffset=0 yoffset=0 xadvance=0 page=0 chnl=15
|
||||
|
After Width: | Height: | Size: 211 KiB |
@@ -0,0 +1,125 @@
|
||||
info face="Inter-Bold" size=-200 bold=0 italic=0 charset="" unicode=1 stretchH=100 smooth=0 aa=1 padding=0,0,0,0 spacing=0,0 outline=0
|
||||
common lineHeight=200 base=200 scaleW=2048 scaleH=2048 pages=1 packed=0 alphaChnl=0 redChnl=4 greenChnl=4 blueChnl=4
|
||||
page id=0 file="Inter-Bold.png"
|
||||
chars count=121
|
||||
char id=32 x=6 y=6 width=38 height=200 xoffset=0 yoffset=0 xadvance=38 page=0 chnl=15
|
||||
char id=33 x=56 y=6 width=29 height=123 xoffset=12 yoffset=39 xadvance=53 page=0 chnl=15
|
||||
char id=34 x=97 y=6 width=49 height=47 xoffset=8 yoffset=39 xadvance=64 page=0 chnl=15
|
||||
char id=35 x=158 y=6 width=105 height=121 xoffset=1 yoffset=39 xadvance=107 page=0 chnl=15
|
||||
char id=36 x=275 y=6 width=96 height=152 xoffset=6 yoffset=24 xadvance=108 page=0 chnl=15
|
||||
char id=37 x=383 y=6 width=116 height=130 xoffset=13 yoffset=35 xadvance=142 page=0 chnl=15
|
||||
char id=38 x=511 y=6 width=105 height=124 xoffset=6 yoffset=38 xadvance=111 page=0 chnl=15
|
||||
char id=39 x=628 y=6 width=19 height=47 xoffset=8 yoffset=39 xadvance=34 page=0 chnl=15
|
||||
char id=40 x=659 y=6 width=49 height=150 xoffset=15 yoffset=37 xadvance=67 page=0 chnl=15
|
||||
char id=41 x=720 y=6 width=49 height=150 xoffset=4 yoffset=37 xadvance=67 page=0 chnl=15
|
||||
char id=42 x=781 y=6 width=67 height=69 xoffset=13 yoffset=39 xadvance=93 page=0 chnl=15
|
||||
char id=43 x=860 y=6 width=84 height=84 xoffset=14 yoffset=70 xadvance=112 page=0 chnl=15
|
||||
char id=44 x=956 y=6 width=29 height=47 xoffset=9 yoffset=143 xadvance=50 page=0 chnl=15
|
||||
char id=45 x=997 y=6 width=56 height=21 xoffset=11 yoffset=100 xadvance=77 page=0 chnl=15
|
||||
char id=46 x=1065 y=6 width=29 height=29 xoffset=10 yoffset=133 xadvance=49 page=0 chnl=15
|
||||
char id=47 x=1106 y=6 width=61 height=145 xoffset=2 yoffset=34 xadvance=64 page=0 chnl=15
|
||||
char id=48 x=1179 y=6 width=98 height=125 xoffset=8 yoffset=38 xadvance=113 page=0 chnl=15
|
||||
char id=49 x=1289 y=6 width=55 height=121 xoffset=8 yoffset=39 xadvance=80 page=0 chnl=15
|
||||
char id=50 x=1356 y=6 width=88 height=122 xoffset=8 yoffset=38 xadvance=104 page=0 chnl=15
|
||||
char id=51 x=1456 y=6 width=93 height=124 xoffset=8 yoffset=38 xadvance=108 page=0 chnl=15
|
||||
char id=52 x=1561 y=6 width=98 height=121 xoffset=7 yoffset=39 xadvance=112 page=0 chnl=15
|
||||
char id=53 x=1671 y=6 width=89 height=123 xoffset=10 yoffset=39 xadvance=106 page=0 chnl=15
|
||||
char id=54 x=1772 y=6 width=94 height=125 xoffset=7 yoffset=37 xadvance=109 page=0 chnl=15
|
||||
char id=55 x=1878 y=6 width=85 height=121 xoffset=6 yoffset=39 xadvance=98 page=0 chnl=15
|
||||
char id=56 x=6 y=218 width=95 height=124 xoffset=7 yoffset=38 xadvance=109 page=0 chnl=15
|
||||
char id=57 x=113 y=218 width=94 height=125 xoffset=8 yoffset=37 xadvance=109 page=0 chnl=15
|
||||
char id=58 x=219 y=218 width=29 height=92 xoffset=10 yoffset=70 xadvance=49 page=0 chnl=15
|
||||
char id=59 x=260 y=218 width=32 height=120 xoffset=9 yoffset=70 xadvance=49 page=0 chnl=15
|
||||
char id=60 x=304 y=218 width=84 height=94 xoffset=14 yoffset=65 xadvance=112 page=0 chnl=15
|
||||
char id=61 x=400 y=218 width=82 height=60 xoffset=15 yoffset=82 xadvance=112 page=0 chnl=15
|
||||
char id=62 x=494 y=218 width=84 height=94 xoffset=14 yoffset=65 xadvance=112 page=0 chnl=15
|
||||
char id=63 x=590 y=218 width=80 height=124 xoffset=6 yoffset=38 xadvance=92 page=0 chnl=15
|
||||
char id=64 x=682 y=218 width=155 height=155 xoffset=9 yoffset=40 xadvance=170 page=0 chnl=15
|
||||
char id=65 x=849 y=218 width=117 height=121 xoffset=3 yoffset=39 xadvance=123 page=0 chnl=15
|
||||
char id=66 x=978 y=218 width=94 height=121 xoffset=10 yoffset=39 xadvance=109 page=0 chnl=15
|
||||
char id=67 x=1084 y=218 width=109 height=124 xoffset=8 yoffset=38 xadvance=124 page=0 chnl=15
|
||||
char id=68 x=1205 y=218 width=102 height=121 xoffset=10 yoffset=39 xadvance=120 page=0 chnl=15
|
||||
char id=69 x=1319 y=218 width=82 height=121 xoffset=10 yoffset=39 xadvance=101 page=0 chnl=15
|
||||
char id=70 x=1413 y=218 width=81 height=121 xoffset=10 yoffset=39 xadvance=96 page=0 chnl=15
|
||||
char id=71 x=1506 y=218 width=110 height=124 xoffset=8 yoffset=38 xadvance=125 page=0 chnl=15
|
||||
char id=72 x=1628 y=218 width=103 height=121 xoffset=10 yoffset=39 xadvance=123 page=0 chnl=15
|
||||
char id=73 x=1743 y=218 width=26 height=121 xoffset=10 yoffset=39 xadvance=46 page=0 chnl=15
|
||||
char id=74 x=1781 y=218 width=81 height=123 xoffset=3 yoffset=39 xadvance=94 page=0 chnl=15
|
||||
char id=75 x=1874 y=218 width=102 height=121 xoffset=10 yoffset=39 xadvance=113 page=0 chnl=15
|
||||
char id=76 x=6 y=430 width=78 height=121 xoffset=10 yoffset=39 xadvance=93 page=0 chnl=15
|
||||
char id=77 x=96 y=430 width=131 height=121 xoffset=10 yoffset=39 xadvance=151 page=0 chnl=15
|
||||
char id=78 x=239 y=430 width=101 height=121 xoffset=10 yoffset=39 xadvance=121 page=0 chnl=15
|
||||
char id=79 x=352 y=430 width=113 height=124 xoffset=8 yoffset=38 xadvance=129 page=0 chnl=15
|
||||
char id=80 x=477 y=430 width=91 height=121 xoffset=10 yoffset=39 xadvance=107 page=0 chnl=15
|
||||
char id=81 x=580 y=430 width=113 height=132 xoffset=8 yoffset=38 xadvance=129 page=0 chnl=15
|
||||
char id=82 x=705 y=430 width=96 height=121 xoffset=10 yoffset=39 xadvance=108 page=0 chnl=15
|
||||
char id=83 x=813 y=430 width=96 height=124 xoffset=6 yoffset=38 xadvance=108 page=0 chnl=15
|
||||
char id=84 x=921 y=430 width=100 height=121 xoffset=5 yoffset=39 xadvance=110 page=0 chnl=15
|
||||
char id=85 x=1033 y=430 width=101 height=123 xoffset=10 yoffset=39 xadvance=120 page=0 chnl=15
|
||||
char id=86 x=1146 y=430 width=117 height=121 xoffset=3 yoffset=39 xadvance=123 page=0 chnl=15
|
||||
char id=87 x=1275 y=430 width=166 height=121 xoffset=3 yoffset=39 xadvance=171 page=0 chnl=15
|
||||
char id=88 x=1453 y=430 width=110 height=121 xoffset=4 yoffset=39 xadvance=118 page=0 chnl=15
|
||||
char id=89 x=1575 y=430 width=114 height=121 xoffset=3 yoffset=39 xadvance=119 page=0 chnl=15
|
||||
char id=90 x=1701 y=430 width=93 height=121 xoffset=9 yoffset=39 xadvance=110 page=0 chnl=15
|
||||
char id=91 x=1806 y=430 width=47 height=150 xoffset=17 yoffset=37 xadvance=67 page=0 chnl=15
|
||||
char id=92 x=1865 y=430 width=61 height=145 xoffset=2 yoffset=34 xadvance=64 page=0 chnl=15
|
||||
char id=93 x=1938 y=430 width=48 height=150 xoffset=3 yoffset=37 xadvance=67 page=0 chnl=15
|
||||
char id=94 x=6 y=642 width=71 height=53 xoffset=5 yoffset=42 xadvance=80 page=0 chnl=15
|
||||
char id=95 x=89 y=642 width=80 height=19 xoffset=-1 yoffset=160 xadvance=78 page=0 chnl=15
|
||||
char id=96 x=181 y=642 width=36 height=28 xoffset=23 yoffset=31 xadvance=82 page=0 chnl=15
|
||||
char id=97 x=229 y=642 width=82 height=94 xoffset=5 yoffset=68 xadvance=95 page=0 chnl=15
|
||||
char id=98 x=323 y=642 width=89 height=123 xoffset=10 yoffset=39 xadvance=104 page=0 chnl=15
|
||||
char id=99 x=424 y=642 width=85 height=94 xoffset=6 yoffset=68 xadvance=97 page=0 chnl=15
|
||||
char id=100 x=521 y=642 width=89 height=123 xoffset=6 yoffset=39 xadvance=104 page=0 chnl=15
|
||||
char id=101 x=622 y=642 width=87 height=94 xoffset=6 yoffset=68 xadvance=98 page=0 chnl=15
|
||||
char id=102 x=721 y=642 width=61 height=126 xoffset=2 yoffset=34 xadvance=63 page=0 chnl=15
|
||||
char id=103 x=794 y=642 width=89 height=128 xoffset=6 yoffset=68 xadvance=104 page=0 chnl=15
|
||||
char id=104 x=895 y=642 width=85 height=121 xoffset=9 yoffset=39 xadvance=103 page=0 chnl=15
|
||||
char id=105 x=992 y=642 width=28 height=128 xoffset=9 yoffset=32 xadvance=44 page=0 chnl=15
|
||||
char id=106 x=1032 y=642 width=41 height=162 xoffset=-5 yoffset=32 xadvance=44 page=0 chnl=15
|
||||
char id=107 x=1085 y=642 width=87 height=121 xoffset=9 yoffset=39 xadvance=96 page=0 chnl=15
|
||||
char id=108 x=1184 y=642 width=26 height=121 xoffset=9 yoffset=39 xadvance=44 page=0 chnl=15
|
||||
char id=109 x=1222 y=642 width=132 height=92 xoffset=9 yoffset=68 xadvance=150 page=0 chnl=15
|
||||
char id=110 x=1366 y=642 width=85 height=92 xoffset=9 yoffset=68 xadvance=102 page=0 chnl=15
|
||||
char id=111 x=1463 y=642 width=90 height=94 xoffset=6 yoffset=68 xadvance=101 page=0 chnl=15
|
||||
char id=112 x=1565 y=642 width=89 height=126 xoffset=9 yoffset=68 xadvance=104 page=0 chnl=15
|
||||
char id=113 x=1666 y=642 width=89 height=126 xoffset=6 yoffset=68 xadvance=104 page=0 chnl=15
|
||||
char id=114 x=1767 y=642 width=56 height=92 xoffset=9 yoffset=68 xadvance=67 page=0 chnl=15
|
||||
char id=115 x=1835 y=642 width=83 height=94 xoffset=5 yoffset=68 xadvance=92 page=0 chnl=15
|
||||
char id=116 x=1930 y=642 width=57 height=114 xoffset=3 yoffset=48 xadvance=64 page=0 chnl=15
|
||||
char id=117 x=6 y=854 width=84 height=93 xoffset=9 yoffset=69 xadvance=102 page=0 chnl=15
|
||||
char id=118 x=102 y=854 width=93 height=91 xoffset=2 yoffset=69 xadvance=96 page=0 chnl=15
|
||||
char id=119 x=207 y=854 width=135 height=91 xoffset=3 yoffset=69 xadvance=140 page=0 chnl=15
|
||||
char id=120 x=354 y=854 width=87 height=91 xoffset=4 yoffset=69 xadvance=94 page=0 chnl=15
|
||||
char id=121 x=453 y=854 width=93 height=125 xoffset=2 yoffset=69 xadvance=96 page=0 chnl=15
|
||||
char id=122 x=558 y=854 width=77 height=91 xoffset=9 yoffset=69 xadvance=94 page=0 chnl=15
|
||||
char id=123 x=647 y=854 width=59 height=150 xoffset=5 yoffset=37 xadvance=67 page=0 chnl=15
|
||||
char id=124 x=718 y=854 width=24 height=198 xoffset=19 yoffset=1 xadvance=61 page=0 chnl=15
|
||||
char id=125 x=754 y=854 width=60 height=150 xoffset=3 yoffset=37 xadvance=67 page=0 chnl=15
|
||||
char id=126 x=826 y=854 width=89 height=38 xoffset=12 yoffset=93 xadvance=112 page=0 chnl=15
|
||||
char id=163 x=927 y=854 width=89 height=122 xoffset=11 yoffset=38 xadvance=110 page=0 chnl=15
|
||||
char id=165 x=1028 y=854 width=91 height=121 xoffset=2 yoffset=39 xadvance=94 page=0 chnl=15
|
||||
char id=167 x=1131 y=854 width=82 height=147 xoffset=8 yoffset=38 xadvance=98 page=0 chnl=15
|
||||
char id=176 x=1225 y=854 width=58 height=59 xoffset=9 yoffset=38 xadvance=75 page=0 chnl=15
|
||||
char id=178 x=1295 y=854 width=60 height=74 xoffset=5 yoffset=26 xadvance=70 page=0 chnl=15
|
||||
char id=215 x=1367 y=854 width=86 height=86 xoffset=13 yoffset=69 xadvance=112 page=0 chnl=15
|
||||
char id=231 x=1465 y=854 width=85 height=131 xoffset=6 yoffset=68 xadvance=97 page=0 chnl=15
|
||||
char id=234 x=1562 y=854 width=87 height=130 xoffset=6 yoffset=32 xadvance=98 page=0 chnl=15
|
||||
char id=241 x=1661 y=854 width=85 height=125 xoffset=9 yoffset=35 xadvance=102 page=0 chnl=15
|
||||
char id=252 x=1758 y=854 width=84 height=129 xoffset=9 yoffset=33 xadvance=102 page=0 chnl=15
|
||||
char id=8209 x=1854 y=854 width=56 height=21 xoffset=12 yoffset=100 xadvance=76 page=0 chnl=15
|
||||
char id=8211 x=1922 y=854 width=83 height=21 xoffset=0 yoffset=100 xadvance=82 page=0 chnl=15
|
||||
char id=8226 x=6 y=1066 width=53 height=52 xoffset=12 yoffset=86 xadvance=76 page=0 chnl=15
|
||||
char id=8364 x=71 y=1066 width=101 height=124 xoffset=4 yoffset=38 xadvance=113 page=0 chnl=15
|
||||
char id=8627 x=184 y=1066 width=136 height=109 xoffset=16 yoffset=39 xadvance=167 page=0 chnl=15
|
||||
char id=8679 x=332 y=1066 width=158 height=130 xoffset=12 yoffset=30 xadvance=182 page=0 chnl=15
|
||||
char id=9003 x=502 y=1066 width=173 height=121 xoffset=13 yoffset=39 xadvance=200 page=0 chnl=15
|
||||
char id=9251 x=687 y=1066 width=104 height=40 xoffset=11 yoffset=148 xadvance=126 page=0 chnl=15
|
||||
char id=9654 x=803 y=1066 width=132 height=152 xoffset=17 yoffset=24 xadvance=157 page=0 chnl=15
|
||||
char id=9664 x=947 y=1066 width=132 height=152 xoffset=9 yoffset=24 xadvance=157 page=0 chnl=15
|
||||
char id=9675 x=1091 y=1066 width=125 height=125 xoffset=13 yoffset=37 xadvance=151 page=0 chnl=15
|
||||
char id=9679 x=1228 y=1066 width=125 height=125 xoffset=13 yoffset=37 xadvance=151 page=0 chnl=15
|
||||
char id=9881 x=1365 y=1066 width=0 height=0 xoffset=0 yoffset=0 xadvance=0 page=0 chnl=15
|
||||
char id=10003 x=1377 y=1066 width=122 height=94 xoffset=15 yoffset=59 xadvance=145 page=0 chnl=15
|
||||
char id=10004 x=1511 y=1066 width=0 height=0 xoffset=0 yoffset=0 xadvance=0 page=0 chnl=15
|
||||
char id=10005 x=1523 y=1066 width=0 height=0 xoffset=0 yoffset=0 xadvance=0 page=0 chnl=15
|
||||
|
After Width: | Height: | Size: 216 KiB |
@@ -0,0 +1,125 @@
|
||||
info face="Inter-ExtraBold" size=-200 bold=0 italic=0 charset="" unicode=1 stretchH=100 smooth=0 aa=1 padding=0,0,0,0 spacing=0,0 outline=0
|
||||
common lineHeight=200 base=200 scaleW=2048 scaleH=2048 pages=1 packed=0 alphaChnl=0 redChnl=4 greenChnl=4 blueChnl=4
|
||||
page id=0 file="Inter-ExtraBold.png"
|
||||
chars count=121
|
||||
char id=32 x=6 y=6 width=35 height=200 xoffset=0 yoffset=0 xadvance=35 page=0 chnl=15
|
||||
char id=33 x=53 y=6 width=32 height=123 xoffset=12 yoffset=39 xadvance=56 page=0 chnl=15
|
||||
char id=34 x=97 y=6 width=50 height=48 xoffset=7 yoffset=39 xadvance=64 page=0 chnl=15
|
||||
char id=35 x=159 y=6 width=107 height=121 xoffset=1 yoffset=39 xadvance=108 page=0 chnl=15
|
||||
char id=36 x=278 y=6 width=99 height=152 xoffset=5 yoffset=24 xadvance=109 page=0 chnl=15
|
||||
char id=37 x=389 y=6 width=119 height=132 xoffset=13 yoffset=34 xadvance=144 page=0 chnl=15
|
||||
char id=38 x=520 y=6 width=109 height=124 xoffset=5 yoffset=38 xadvance=113 page=0 chnl=15
|
||||
char id=39 x=641 y=6 width=20 height=48 xoffset=7 yoffset=39 xadvance=33 page=0 chnl=15
|
||||
char id=40 x=673 y=6 width=53 height=149 xoffset=14 yoffset=38 xadvance=70 page=0 chnl=15
|
||||
char id=41 x=738 y=6 width=53 height=149 xoffset=3 yoffset=38 xadvance=70 page=0 chnl=15
|
||||
char id=42 x=803 y=6 width=69 height=69 xoffset=14 yoffset=39 xadvance=96 page=0 chnl=15
|
||||
char id=43 x=884 y=6 width=86 height=86 xoffset=14 yoffset=69 xadvance=113 page=0 chnl=15
|
||||
char id=44 x=982 y=6 width=31 height=47 xoffset=8 yoffset=143 xadvance=51 page=0 chnl=15
|
||||
char id=45 x=1025 y=6 width=56 height=23 xoffset=11 yoffset=100 xadvance=77 page=0 chnl=15
|
||||
char id=46 x=1093 y=6 width=32 height=31 xoffset=9 yoffset=131 xadvance=50 page=0 chnl=15
|
||||
char id=47 x=1137 y=6 width=64 height=145 xoffset=1 yoffset=34 xadvance=66 page=0 chnl=15
|
||||
char id=48 x=1213 y=6 width=103 height=125 xoffset=7 yoffset=38 xadvance=117 page=0 chnl=15
|
||||
char id=49 x=1328 y=6 width=58 height=121 xoffset=7 yoffset=39 xadvance=82 page=0 chnl=15
|
||||
char id=50 x=1398 y=6 width=91 height=122 xoffset=7 yoffset=38 xadvance=105 page=0 chnl=15
|
||||
char id=51 x=1501 y=6 width=96 height=124 xoffset=7 yoffset=38 xadvance=110 page=0 chnl=15
|
||||
char id=52 x=1609 y=6 width=101 height=121 xoffset=7 yoffset=39 xadvance=113 page=0 chnl=15
|
||||
char id=53 x=1722 y=6 width=93 height=123 xoffset=9 yoffset=39 xadvance=108 page=0 chnl=15
|
||||
char id=54 x=1827 y=6 width=98 height=125 xoffset=6 yoffset=37 xadvance=111 page=0 chnl=15
|
||||
char id=55 x=1937 y=6 width=88 height=121 xoffset=5 yoffset=39 xadvance=99 page=0 chnl=15
|
||||
char id=56 x=6 y=218 width=98 height=124 xoffset=7 yoffset=38 xadvance=111 page=0 chnl=15
|
||||
char id=57 x=116 y=218 width=98 height=125 xoffset=7 yoffset=37 xadvance=111 page=0 chnl=15
|
||||
char id=58 x=226 y=218 width=32 height=93 xoffset=9 yoffset=69 xadvance=50 page=0 chnl=15
|
||||
char id=59 x=270 y=218 width=35 height=121 xoffset=8 yoffset=69 xadvance=50 page=0 chnl=15
|
||||
char id=60 x=317 y=218 width=86 height=97 xoffset=14 yoffset=63 xadvance=113 page=0 chnl=15
|
||||
char id=61 x=415 y=218 width=85 height=65 xoffset=14 yoffset=79 xadvance=113 page=0 chnl=15
|
||||
char id=62 x=512 y=218 width=86 height=97 xoffset=14 yoffset=63 xadvance=113 page=0 chnl=15
|
||||
char id=63 x=610 y=218 width=83 height=124 xoffset=6 yoffset=38 xadvance=96 page=0 chnl=15
|
||||
char id=64 x=705 y=218 width=160 height=158 xoffset=10 yoffset=38 xadvance=175 page=0 chnl=15
|
||||
char id=65 x=877 y=218 width=121 height=121 xoffset=3 yoffset=39 xadvance=127 page=0 chnl=15
|
||||
char id=66 x=1010 y=218 width=96 height=121 xoffset=9 yoffset=39 xadvance=109 page=0 chnl=15
|
||||
char id=67 x=1118 y=218 width=112 height=124 xoffset=7 yoffset=38 xadvance=125 page=0 chnl=15
|
||||
char id=68 x=1242 y=218 width=104 height=121 xoffset=9 yoffset=39 xadvance=120 page=0 chnl=15
|
||||
char id=69 x=1358 y=218 width=84 height=121 xoffset=9 yoffset=39 xadvance=101 page=0 chnl=15
|
||||
char id=70 x=1454 y=218 width=83 height=121 xoffset=9 yoffset=39 xadvance=96 page=0 chnl=15
|
||||
char id=71 x=1549 y=218 width=112 height=124 xoffset=7 yoffset=38 xadvance=126 page=0 chnl=15
|
||||
char id=72 x=1673 y=218 width=106 height=121 xoffset=9 yoffset=39 xadvance=123 page=0 chnl=15
|
||||
char id=73 x=1791 y=218 width=30 height=121 xoffset=9 yoffset=39 xadvance=47 page=0 chnl=15
|
||||
char id=74 x=1833 y=218 width=85 height=123 xoffset=2 yoffset=39 xadvance=95 page=0 chnl=15
|
||||
char id=75 x=6 y=430 width=106 height=121 xoffset=9 yoffset=39 xadvance=116 page=0 chnl=15
|
||||
char id=76 x=124 y=430 width=80 height=121 xoffset=9 yoffset=39 xadvance=94 page=0 chnl=15
|
||||
char id=77 x=216 y=430 width=135 height=121 xoffset=9 yoffset=39 xadvance=152 page=0 chnl=15
|
||||
char id=78 x=363 y=430 width=103 height=121 xoffset=9 yoffset=39 xadvance=120 page=0 chnl=15
|
||||
char id=79 x=478 y=430 width=116 height=124 xoffset=7 yoffset=38 xadvance=130 page=0 chnl=15
|
||||
char id=80 x=606 y=430 width=94 height=121 xoffset=9 yoffset=39 xadvance=107 page=0 chnl=15
|
||||
char id=81 x=712 y=430 width=116 height=132 xoffset=7 yoffset=38 xadvance=130 page=0 chnl=15
|
||||
char id=82 x=840 y=430 width=98 height=121 xoffset=9 yoffset=39 xadvance=109 page=0 chnl=15
|
||||
char id=83 x=950 y=430 width=99 height=124 xoffset=5 yoffset=38 xadvance=109 page=0 chnl=15
|
||||
char id=84 x=1061 y=430 width=102 height=121 xoffset=5 yoffset=39 xadvance=111 page=0 chnl=15
|
||||
char id=85 x=1175 y=430 width=102 height=123 xoffset=9 yoffset=39 xadvance=119 page=0 chnl=15
|
||||
char id=86 x=1289 y=430 width=121 height=121 xoffset=3 yoffset=39 xadvance=127 page=0 chnl=15
|
||||
char id=87 x=1422 y=430 width=172 height=121 xoffset=2 yoffset=39 xadvance=175 page=0 chnl=15
|
||||
char id=88 x=1606 y=430 width=114 height=121 xoffset=4 yoffset=39 xadvance=122 page=0 chnl=15
|
||||
char id=89 x=1732 y=430 width=117 height=121 xoffset=3 yoffset=39 xadvance=123 page=0 chnl=15
|
||||
char id=90 x=1861 y=430 width=96 height=121 xoffset=8 yoffset=39 xadvance=112 page=0 chnl=15
|
||||
char id=91 x=1969 y=430 width=52 height=149 xoffset=16 yoffset=38 xadvance=70 page=0 chnl=15
|
||||
char id=92 x=6 y=642 width=64 height=145 xoffset=1 yoffset=34 xadvance=66 page=0 chnl=15
|
||||
char id=93 x=82 y=642 width=51 height=149 xoffset=3 yoffset=38 xadvance=70 page=0 chnl=15
|
||||
char id=94 x=145 y=642 width=72 height=53 xoffset=5 yoffset=42 xadvance=81 page=0 chnl=15
|
||||
char id=95 x=229 y=642 width=82 height=22 xoffset=-1 yoffset=160 xadvance=80 page=0 chnl=15
|
||||
char id=96 x=323 y=642 width=38 height=28 xoffset=22 yoffset=31 xadvance=82 page=0 chnl=15
|
||||
char id=97 x=373 y=642 width=85 height=94 xoffset=4 yoffset=68 xadvance=96 page=0 chnl=15
|
||||
char id=98 x=470 y=642 width=91 height=123 xoffset=9 yoffset=39 xadvance=105 page=0 chnl=15
|
||||
char id=99 x=573 y=642 width=89 height=94 xoffset=5 yoffset=68 xadvance=98 page=0 chnl=15
|
||||
char id=100 x=674 y=642 width=92 height=123 xoffset=5 yoffset=39 xadvance=105 page=0 chnl=15
|
||||
char id=101 x=778 y=642 width=90 height=94 xoffset=5 yoffset=68 xadvance=99 page=0 chnl=15
|
||||
char id=102 x=880 y=642 width=63 height=126 xoffset=2 yoffset=34 xadvance=65 page=0 chnl=15
|
||||
char id=103 x=955 y=642 width=92 height=127 xoffset=5 yoffset=68 xadvance=105 page=0 chnl=15
|
||||
char id=104 x=1059 y=642 width=88 height=121 xoffset=9 yoffset=39 xadvance=104 page=0 chnl=15
|
||||
char id=105 x=1159 y=642 width=31 height=129 xoffset=8 yoffset=31 xadvance=46 page=0 chnl=15
|
||||
char id=106 x=1202 y=642 width=44 height=163 xoffset=-5 yoffset=31 xadvance=46 page=0 chnl=15
|
||||
char id=107 x=1258 y=642 width=90 height=121 xoffset=9 yoffset=39 xadvance=98 page=0 chnl=15
|
||||
char id=108 x=1360 y=642 width=29 height=121 xoffset=9 yoffset=39 xadvance=46 page=0 chnl=15
|
||||
char id=109 x=1401 y=642 width=136 height=92 xoffset=9 yoffset=68 xadvance=153 page=0 chnl=15
|
||||
char id=110 x=1549 y=642 width=88 height=92 xoffset=9 yoffset=68 xadvance=104 page=0 chnl=15
|
||||
char id=111 x=1649 y=642 width=92 height=94 xoffset=5 yoffset=68 xadvance=102 page=0 chnl=15
|
||||
char id=112 x=1753 y=642 width=91 height=126 xoffset=9 yoffset=68 xadvance=105 page=0 chnl=15
|
||||
char id=113 x=1856 y=642 width=92 height=126 xoffset=5 yoffset=68 xadvance=105 page=0 chnl=15
|
||||
char id=114 x=1960 y=642 width=58 height=92 xoffset=9 yoffset=68 xadvance=69 page=0 chnl=15
|
||||
char id=115 x=6 y=854 width=86 height=94 xoffset=5 yoffset=68 xadvance=95 page=0 chnl=15
|
||||
char id=116 x=104 y=854 width=59 height=114 xoffset=3 yoffset=48 xadvance=65 page=0 chnl=15
|
||||
char id=117 x=175 y=854 width=87 height=93 xoffset=9 yoffset=69 xadvance=105 page=0 chnl=15
|
||||
char id=118 x=274 y=854 width=96 height=91 xoffset=1 yoffset=69 xadvance=98 page=0 chnl=15
|
||||
char id=119 x=382 y=854 width=139 height=91 xoffset=2 yoffset=69 xadvance=142 page=0 chnl=15
|
||||
char id=120 x=533 y=854 width=91 height=91 xoffset=3 yoffset=69 xadvance=96 page=0 chnl=15
|
||||
char id=121 x=636 y=854 width=96 height=125 xoffset=1 yoffset=69 xadvance=98 page=0 chnl=15
|
||||
char id=122 x=744 y=854 width=78 height=91 xoffset=9 yoffset=69 xadvance=96 page=0 chnl=15
|
||||
char id=123 x=834 y=854 width=64 height=149 xoffset=4 yoffset=38 xadvance=70 page=0 chnl=15
|
||||
char id=124 x=910 y=854 width=29 height=198 xoffset=18 yoffset=1 xadvance=64 page=0 chnl=15
|
||||
char id=125 x=951 y=854 width=63 height=149 xoffset=3 yoffset=38 xadvance=70 page=0 chnl=15
|
||||
char id=126 x=1026 y=854 width=91 height=41 xoffset=11 yoffset=91 xadvance=113 page=0 chnl=15
|
||||
char id=163 x=1129 y=854 width=91 height=122 xoffset=11 yoffset=38 xadvance=113 page=0 chnl=15
|
||||
char id=165 x=1232 y=854 width=92 height=121 xoffset=2 yoffset=39 xadvance=95 page=0 chnl=15
|
||||
char id=167 x=1336 y=854 width=83 height=147 xoffset=9 yoffset=38 xadvance=100 page=0 chnl=15
|
||||
char id=176 x=1431 y=854 width=60 height=59 xoffset=8 yoffset=39 xadvance=76 page=0 chnl=15
|
||||
char id=178 x=1503 y=854 width=61 height=74 xoffset=5 yoffset=26 xadvance=70 page=0 chnl=15
|
||||
char id=215 x=1576 y=854 width=90 height=90 xoffset=12 yoffset=67 xadvance=113 page=0 chnl=15
|
||||
char id=231 x=1678 y=854 width=89 height=131 xoffset=5 yoffset=68 xadvance=98 page=0 chnl=15
|
||||
char id=234 x=1779 y=854 width=90 height=130 xoffset=5 yoffset=32 xadvance=99 page=0 chnl=15
|
||||
char id=241 x=1881 y=854 width=88 height=126 xoffset=9 yoffset=34 xadvance=104 page=0 chnl=15
|
||||
char id=252 x=6 y=1066 width=87 height=130 xoffset=9 yoffset=32 xadvance=105 page=0 chnl=15
|
||||
char id=8209 x=105 y=1066 width=57 height=23 xoffset=15 yoffset=100 xadvance=82 page=0 chnl=15
|
||||
char id=8211 x=174 y=1066 width=83 height=23 xoffset=0 yoffset=100 xadvance=82 page=0 chnl=15
|
||||
char id=8226 x=269 y=1066 width=53 height=52 xoffset=9 yoffset=86 xadvance=71 page=0 chnl=15
|
||||
char id=8364 x=334 y=1066 width=102 height=124 xoffset=4 yoffset=38 xadvance=114 page=0 chnl=15
|
||||
char id=8627 x=448 y=1066 width=137 height=110 xoffset=16 yoffset=39 xadvance=169 page=0 chnl=15
|
||||
char id=8679 x=597 y=1066 width=163 height=130 xoffset=12 yoffset=30 xadvance=187 page=0 chnl=15
|
||||
char id=9003 x=772 y=1066 width=173 height=121 xoffset=13 yoffset=39 xadvance=200 page=0 chnl=15
|
||||
char id=9251 x=957 y=1066 width=107 height=41 xoffset=11 yoffset=148 xadvance=128 page=0 chnl=15
|
||||
char id=9654 x=1076 y=1066 width=134 height=152 xoffset=17 yoffset=24 xadvance=160 page=0 chnl=15
|
||||
char id=9664 x=1222 y=1066 width=134 height=152 xoffset=9 yoffset=24 xadvance=160 page=0 chnl=15
|
||||
char id=9675 x=1368 y=1066 width=125 height=125 xoffset=13 yoffset=37 xadvance=151 page=0 chnl=15
|
||||
char id=9679 x=1505 y=1066 width=125 height=125 xoffset=13 yoffset=37 xadvance=151 page=0 chnl=15
|
||||
char id=9881 x=1642 y=1066 width=0 height=0 xoffset=0 yoffset=0 xadvance=0 page=0 chnl=15
|
||||
char id=10003 x=1654 y=1066 width=124 height=98 xoffset=15 yoffset=58 xadvance=145 page=0 chnl=15
|
||||
char id=10004 x=1790 y=1066 width=0 height=0 xoffset=0 yoffset=0 xadvance=0 page=0 chnl=15
|
||||
char id=10005 x=1802 y=1066 width=0 height=0 xoffset=0 yoffset=0 xadvance=0 page=0 chnl=15
|
||||
|
After Width: | Height: | Size: 214 KiB |
@@ -0,0 +1,125 @@
|
||||
info face="Inter-ExtraLight" size=-200 bold=0 italic=0 charset="" unicode=1 stretchH=100 smooth=0 aa=1 padding=0,0,0,0 spacing=0,0 outline=0
|
||||
common lineHeight=200 base=200 scaleW=2048 scaleH=2048 pages=1 packed=0 alphaChnl=0 redChnl=4 greenChnl=4 blueChnl=4
|
||||
page id=0 file="Inter-ExtraLight.png"
|
||||
chars count=121
|
||||
char id=32 x=6 y=6 width=46 height=200 xoffset=0 yoffset=0 xadvance=46 page=0 chnl=15
|
||||
char id=33 x=64 y=6 width=14 height=122 xoffset=15 yoffset=39 xadvance=44 page=0 chnl=15
|
||||
char id=34 x=90 y=6 width=33 height=40 xoffset=13 yoffset=39 xadvance=59 page=0 chnl=15
|
||||
char id=35 x=135 y=6 width=99 height=121 xoffset=3 yoffset=39 xadvance=104 page=0 chnl=15
|
||||
char id=36 x=246 y=6 width=81 height=152 xoffset=11 yoffset=24 xadvance=102 page=0 chnl=15
|
||||
char id=37 x=339 y=6 width=104 height=124 xoffset=13 yoffset=38 xadvance=129 page=0 chnl=15
|
||||
char id=38 x=455 y=6 width=88 height=124 xoffset=9 yoffset=38 xadvance=99 page=0 chnl=15
|
||||
char id=39 x=555 y=6 width=8 height=40 xoffset=13 yoffset=39 xadvance=34 page=0 chnl=15
|
||||
char id=40 x=575 y=6 width=27 height=151 xoffset=18 yoffset=34 xadvance=54 page=0 chnl=15
|
||||
char id=41 x=614 y=6 width=26 height=151 xoffset=10 yoffset=34 xadvance=54 page=0 chnl=15
|
||||
char id=42 x=652 y=6 width=63 height=69 xoffset=10 yoffset=39 xadvance=82 page=0 chnl=15
|
||||
char id=43 x=727 y=6 width=72 height=72 xoffset=17 yoffset=77 xadvance=106 page=0 chnl=15
|
||||
char id=44 x=811 y=6 width=16 height=47 xoffset=13 yoffset=143 xadvance=43 page=0 chnl=15
|
||||
char id=45 x=839 y=6 width=48 height=7 xoffset=13 yoffset=105 xadvance=73 page=0 chnl=15
|
||||
char id=46 x=899 y=6 width=13 height=13 xoffset=15 yoffset=148 xadvance=43 page=0 chnl=15
|
||||
char id=47 x=924 y=6 width=47 height=145 xoffset=4 yoffset=34 xadvance=55 page=0 chnl=15
|
||||
char id=48 x=983 y=6 width=78 height=124 xoffset=11 yoffset=38 xadvance=99 page=0 chnl=15
|
||||
char id=49 x=1073 y=6 width=36 height=121 xoffset=11 yoffset=39 xadvance=70 page=0 chnl=15
|
||||
char id=50 x=1121 y=6 width=74 height=122 xoffset=14 yoffset=38 xadvance=101 page=0 chnl=15
|
||||
char id=51 x=1207 y=6 width=76 height=124 xoffset=14 yoffset=38 xadvance=102 page=0 chnl=15
|
||||
char id=52 x=1295 y=6 width=79 height=121 xoffset=11 yoffset=39 xadvance=100 page=0 chnl=15
|
||||
char id=53 x=1386 y=6 width=72 height=123 xoffset=13 yoffset=39 xadvance=96 page=0 chnl=15
|
||||
char id=54 x=1470 y=6 width=75 height=124 xoffset=12 yoffset=38 xadvance=98 page=0 chnl=15
|
||||
char id=55 x=1557 y=6 width=73 height=121 xoffset=9 yoffset=39 xadvance=91 page=0 chnl=15
|
||||
char id=56 x=1642 y=6 width=78 height=124 xoffset=11 yoffset=38 xadvance=100 page=0 chnl=15
|
||||
char id=57 x=1732 y=6 width=75 height=125 xoffset=12 yoffset=37 xadvance=98 page=0 chnl=15
|
||||
char id=58 x=1819 y=6 width=13 height=77 xoffset=15 yoffset=78 xadvance=43 page=0 chnl=15
|
||||
char id=59 x=1844 y=6 width=17 height=112 xoffset=13 yoffset=78 xadvance=43 page=0 chnl=15
|
||||
char id=60 x=1873 y=6 width=69 height=88 xoffset=19 yoffset=69 xadvance=106 page=0 chnl=15
|
||||
char id=61 x=1954 y=6 width=62 height=40 xoffset=22 yoffset=93 xadvance=106 page=0 chnl=15
|
||||
char id=62 x=6 y=218 width=69 height=88 xoffset=19 yoffset=69 xadvance=106 page=0 chnl=15
|
||||
char id=63 x=87 y=218 width=65 height=123 xoffset=7 yoffset=38 xadvance=80 page=0 chnl=15
|
||||
char id=64 x=164 y=218 width=139 height=148 xoffset=8 yoffset=44 xadvance=154 page=0 chnl=15
|
||||
char id=65 x=315 y=218 width=97 height=121 xoffset=4 yoffset=39 xadvance=105 page=0 chnl=15
|
||||
char id=66 x=424 y=218 width=78 height=121 xoffset=17 yoffset=39 xadvance=106 page=0 chnl=15
|
||||
char id=67 x=514 y=218 width=98 height=124 xoffset=11 yoffset=38 xadvance=119 page=0 chnl=15
|
||||
char id=68 x=624 y=218 width=91 height=121 xoffset=17 yoffset=39 xadvance=118 page=0 chnl=15
|
||||
char id=69 x=727 y=218 width=69 height=121 xoffset=17 yoffset=39 xadvance=97 page=0 chnl=15
|
||||
char id=70 x=808 y=218 width=68 height=121 xoffset=17 yoffset=39 xadvance=96 page=0 chnl=15
|
||||
char id=71 x=888 y=218 width=99 height=124 xoffset=11 yoffset=38 xadvance=122 page=0 chnl=15
|
||||
char id=72 x=999 y=218 width=88 height=121 xoffset=17 yoffset=39 xadvance=121 page=0 chnl=15
|
||||
char id=73 x=1099 y=218 width=8 height=121 xoffset=17 yoffset=39 xadvance=42 page=0 chnl=15
|
||||
char id=74 x=1119 y=218 width=62 height=123 xoffset=8 yoffset=39 xadvance=87 page=0 chnl=15
|
||||
char id=75 x=1193 y=218 width=82 height=121 xoffset=17 yoffset=39 xadvance=103 page=0 chnl=15
|
||||
char id=76 x=1287 y=218 width=67 height=121 xoffset=17 yoffset=39 xadvance=92 page=0 chnl=15
|
||||
char id=77 x=1366 y=218 width=110 height=121 xoffset=17 yoffset=39 xadvance=144 page=0 chnl=15
|
||||
char id=78 x=1488 y=218 width=93 height=121 xoffset=17 yoffset=39 xadvance=126 page=0 chnl=15
|
||||
char id=79 x=1593 y=218 width=102 height=124 xoffset=11 yoffset=38 xadvance=124 page=0 chnl=15
|
||||
char id=80 x=1707 y=218 width=75 height=121 xoffset=17 yoffset=39 xadvance=101 page=0 chnl=15
|
||||
char id=81 x=1794 y=218 width=102 height=134 xoffset=11 yoffset=38 xadvance=124 page=0 chnl=15
|
||||
char id=82 x=1908 y=218 width=80 height=121 xoffset=17 yoffset=39 xadvance=102 page=0 chnl=15
|
||||
char id=83 x=6 y=430 width=81 height=125 xoffset=11 yoffset=37 xadvance=102 page=0 chnl=15
|
||||
char id=84 x=99 y=430 width=85 height=121 xoffset=9 yoffset=39 xadvance=102 page=0 chnl=15
|
||||
char id=85 x=196 y=430 width=90 height=123 xoffset=17 yoffset=39 xadvance=123 page=0 chnl=15
|
||||
char id=86 x=298 y=430 width=97 height=121 xoffset=4 yoffset=39 xadvance=105 page=0 chnl=15
|
||||
char id=87 x=407 y=430 width=143 height=121 xoffset=4 yoffset=39 xadvance=150 page=0 chnl=15
|
||||
char id=88 x=562 y=430 width=90 height=121 xoffset=5 yoffset=39 xadvance=99 page=0 chnl=15
|
||||
char id=89 x=664 y=430 width=95 height=121 xoffset=5 yoffset=39 xadvance=104 page=0 chnl=15
|
||||
char id=90 x=771 y=430 width=79 height=121 xoffset=11 yoffset=39 xadvance=101 page=0 chnl=15
|
||||
char id=91 x=862 y=430 width=26 height=151 xoffset=20 yoffset=34 xadvance=54 page=0 chnl=15
|
||||
char id=92 x=900 y=430 width=47 height=145 xoffset=4 yoffset=34 xadvance=55 page=0 chnl=15
|
||||
char id=93 x=959 y=430 width=26 height=151 xoffset=9 yoffset=34 xadvance=54 page=0 chnl=15
|
||||
char id=94 x=997 y=430 width=56 height=52 xoffset=7 yoffset=43 xadvance=69 page=0 chnl=15
|
||||
char id=95 x=1065 y=430 width=74 height=7 xoffset=-1 yoffset=160 xadvance=71 page=0 chnl=15
|
||||
char id=96 x=1151 y=430 width=25 height=27 xoffset=28 yoffset=31 xadvance=82 page=0 chnl=15
|
||||
char id=97 x=1188 y=430 width=66 height=94 xoffset=10 yoffset=68 xadvance=90 page=0 chnl=15
|
||||
char id=98 x=1266 y=430 width=74 height=123 xoffset=17 yoffset=39 xadvance=100 page=0 chnl=15
|
||||
char id=99 x=1352 y=430 width=74 height=94 xoffset=9 yoffset=68 xadvance=91 page=0 chnl=15
|
||||
char id=100 x=1438 y=430 width=74 height=123 xoffset=9 yoffset=39 xadvance=100 page=0 chnl=15
|
||||
char id=101 x=1524 y=430 width=77 height=94 xoffset=9 yoffset=68 xadvance=95 page=0 chnl=15
|
||||
char id=102 x=1613 y=430 width=46 height=125 xoffset=8 yoffset=35 xadvance=56 page=0 chnl=15
|
||||
char id=103 x=1671 y=430 width=74 height=128 xoffset=9 yoffset=68 xadvance=97 page=0 chnl=15
|
||||
char id=104 x=1757 y=430 width=67 height=121 xoffset=14 yoffset=39 xadvance=95 page=0 chnl=15
|
||||
char id=105 x=1836 y=430 width=13 height=118 xoffset=12 yoffset=42 xadvance=37 page=0 chnl=15
|
||||
char id=106 x=1861 y=430 width=28 height=152 xoffset=-3 yoffset=42 xadvance=37 page=0 chnl=15
|
||||
char id=107 x=1901 y=430 width=66 height=121 xoffset=14 yoffset=39 xadvance=83 page=0 chnl=15
|
||||
char id=108 x=1979 y=430 width=9 height=121 xoffset=14 yoffset=39 xadvance=36 page=0 chnl=15
|
||||
char id=109 x=6 y=642 width=112 height=92 xoffset=14 yoffset=68 xadvance=139 page=0 chnl=15
|
||||
char id=110 x=130 y=642 width=66 height=92 xoffset=14 yoffset=68 xadvance=94 page=0 chnl=15
|
||||
char id=111 x=208 y=642 width=78 height=94 xoffset=9 yoffset=68 xadvance=96 page=0 chnl=15
|
||||
char id=112 x=298 y=642 width=74 height=126 xoffset=14 yoffset=68 xadvance=97 page=0 chnl=15
|
||||
char id=113 x=384 y=642 width=74 height=126 xoffset=9 yoffset=68 xadvance=97 page=0 chnl=15
|
||||
char id=114 x=470 y=642 width=38 height=92 xoffset=14 yoffset=68 xadvance=56 page=0 chnl=15
|
||||
char id=115 x=520 y=642 width=64 height=94 xoffset=10 yoffset=68 xadvance=83 page=0 chnl=15
|
||||
char id=116 x=596 y=642 width=44 height=114 xoffset=7 yoffset=48 xadvance=58 page=0 chnl=15
|
||||
char id=117 x=652 y=642 width=66 height=93 xoffset=14 yoffset=69 xadvance=94 page=0 chnl=15
|
||||
char id=118 x=730 y=642 width=74 height=91 xoffset=7 yoffset=69 xadvance=88 page=0 chnl=15
|
||||
char id=119 x=816 y=642 width=118 height=91 xoffset=7 yoffset=69 xadvance=131 page=0 chnl=15
|
||||
char id=120 x=946 y=642 width=67 height=91 xoffset=9 yoffset=69 xadvance=84 page=0 chnl=15
|
||||
char id=121 x=1025 y=642 width=74 height=125 xoffset=7 yoffset=69 xadvance=88 page=0 chnl=15
|
||||
char id=122 x=1111 y=642 width=66 height=91 xoffset=11 yoffset=69 xadvance=85 page=0 chnl=15
|
||||
char id=123 x=1189 y=642 width=43 height=151 xoffset=9 yoffset=34 xadvance=54 page=0 chnl=15
|
||||
char id=124 x=1244 y=642 width=8 height=198 xoffset=20 yoffset=1 xadvance=48 page=0 chnl=15
|
||||
char id=125 x=1264 y=642 width=43 height=151 xoffset=3 yoffset=34 xadvance=54 page=0 chnl=15
|
||||
char id=126 x=1319 y=642 width=78 height=23 xoffset=14 yoffset=101 xadvance=106 page=0 chnl=15
|
||||
char id=163 x=1409 y=642 width=79 height=122 xoffset=12 yoffset=38 xadvance=104 page=0 chnl=15
|
||||
char id=165 x=1500 y=642 width=82 height=121 xoffset=3 yoffset=39 xadvance=87 page=0 chnl=15
|
||||
char id=167 x=1594 y=642 width=69 height=147 xoffset=10 yoffset=38 xadvance=88 page=0 chnl=15
|
||||
char id=176 x=1675 y=642 width=51 height=51 xoffset=12 yoffset=38 xadvance=75 page=0 chnl=15
|
||||
char id=178 x=1738 y=642 width=51 height=74 xoffset=9 yoffset=26 xadvance=68 page=0 chnl=15
|
||||
char id=215 x=1801 y=642 width=72 height=72 xoffset=17 yoffset=77 xadvance=106 page=0 chnl=15
|
||||
char id=231 x=1885 y=642 width=74 height=124 xoffset=9 yoffset=68 xadvance=91 page=0 chnl=15
|
||||
char id=234 x=6 y=854 width=77 height=126 xoffset=9 yoffset=36 xadvance=95 page=0 chnl=15
|
||||
char id=241 x=95 y=854 width=66 height=120 xoffset=14 yoffset=40 xadvance=94 page=0 chnl=15
|
||||
char id=252 x=173 y=854 width=66 height=120 xoffset=14 yoffset=42 xadvance=94 page=0 chnl=15
|
||||
char id=8209 x=251 y=854 width=48 height=7 xoffset=-2 yoffset=105 xadvance=47 page=0 chnl=15
|
||||
char id=8211 x=311 y=854 width=83 height=7 xoffset=0 yoffset=105 xadvance=82 page=0 chnl=15
|
||||
char id=8226 x=406 y=854 width=39 height=38 xoffset=27 yoffset=93 xadvance=92 page=0 chnl=15
|
||||
char id=8364 x=457 y=854 width=95 height=124 xoffset=5 yoffset=38 xadvance=111 page=0 chnl=15
|
||||
char id=8627 x=564 y=854 width=125 height=100 xoffset=16 yoffset=39 xadvance=156 page=0 chnl=15
|
||||
char id=8679 x=701 y=854 width=122 height=130 xoffset=15 yoffset=30 xadvance=151 page=0 chnl=15
|
||||
char id=9003 x=835 y=854 width=173 height=121 xoffset=13 yoffset=39 xadvance=200 page=0 chnl=15
|
||||
char id=9251 x=1020 y=854 width=36 height=34 xoffset=39 yoffset=148 xadvance=114 page=0 chnl=15
|
||||
char id=9654 x=1068 y=854 width=126 height=152 xoffset=14 yoffset=24 xadvance=146 page=0 chnl=15
|
||||
char id=9664 x=1206 y=854 width=126 height=152 xoffset=7 yoffset=24 xadvance=146 page=0 chnl=15
|
||||
char id=9675 x=1344 y=854 width=125 height=125 xoffset=13 yoffset=37 xadvance=151 page=0 chnl=15
|
||||
char id=9679 x=1481 y=854 width=125 height=125 xoffset=13 yoffset=37 xadvance=151 page=0 chnl=15
|
||||
char id=9881 x=1618 y=854 width=0 height=0 xoffset=0 yoffset=0 xadvance=0 page=0 chnl=15
|
||||
char id=10003 x=1630 y=854 width=110 height=77 xoffset=18 yoffset=65 xadvance=145 page=0 chnl=15
|
||||
char id=10004 x=1752 y=854 width=0 height=0 xoffset=0 yoffset=0 xadvance=0 page=0 chnl=15
|
||||
char id=10005 x=1764 y=854 width=0 height=0 xoffset=0 yoffset=0 xadvance=0 page=0 chnl=15
|
||||
|
After Width: | Height: | Size: 219 KiB |
@@ -0,0 +1,125 @@
|
||||
info face="Inter-Light" size=-200 bold=0 italic=0 charset="" unicode=1 stretchH=100 smooth=0 aa=1 padding=0,0,0,0 spacing=0,0 outline=0
|
||||
common lineHeight=200 base=200 scaleW=2048 scaleH=2048 pages=1 packed=0 alphaChnl=0 redChnl=4 greenChnl=4 blueChnl=4
|
||||
page id=0 file="Inter-Light.png"
|
||||
chars count=121
|
||||
char id=32 x=6 y=6 width=46 height=200 xoffset=0 yoffset=0 xadvance=46 page=0 chnl=15
|
||||
char id=33 x=64 y=6 width=17 height=122 xoffset=14 yoffset=39 xadvance=45 page=0 chnl=15
|
||||
char id=34 x=93 y=6 width=39 height=41 xoffset=12 yoffset=39 xadvance=63 page=0 chnl=15
|
||||
char id=35 x=144 y=6 width=99 height=121 xoffset=3 yoffset=39 xadvance=104 page=0 chnl=15
|
||||
char id=36 x=255 y=6 width=84 height=152 xoffset=10 yoffset=24 xadvance=103 page=0 chnl=15
|
||||
char id=37 x=351 y=6 width=106 height=124 xoffset=13 yoffset=38 xadvance=132 page=0 chnl=15
|
||||
char id=38 x=469 y=6 width=91 height=124 xoffset=9 yoffset=38 xadvance=102 page=0 chnl=15
|
||||
char id=39 x=572 y=6 width=11 height=41 xoffset=12 yoffset=39 xadvance=35 page=0 chnl=15
|
||||
char id=40 x=595 y=6 width=31 height=151 xoffset=18 yoffset=34 xadvance=57 page=0 chnl=15
|
||||
char id=41 x=638 y=6 width=32 height=151 xoffset=8 yoffset=34 xadvance=57 page=0 chnl=15
|
||||
char id=42 x=682 y=6 width=64 height=69 xoffset=9 yoffset=39 xadvance=82 page=0 chnl=15
|
||||
char id=43 x=758 y=6 width=76 height=76 xoffset=16 yoffset=75 xadvance=107 page=0 chnl=15
|
||||
char id=44 x=846 y=6 width=20 height=47 xoffset=12 yoffset=143 xadvance=44 page=0 chnl=15
|
||||
char id=45 x=878 y=6 width=51 height=11 xoffset=12 yoffset=103 xadvance=74 page=0 chnl=15
|
||||
char id=46 x=941 y=6 width=18 height=18 xoffset=13 yoffset=143 xadvance=44 page=0 chnl=15
|
||||
char id=47 x=971 y=6 width=49 height=145 xoffset=4 yoffset=34 xadvance=57 page=0 chnl=15
|
||||
char id=48 x=1032 y=6 width=81 height=124 xoffset=10 yoffset=38 xadvance=101 page=0 chnl=15
|
||||
char id=49 x=1125 y=6 width=41 height=121 xoffset=10 yoffset=39 xadvance=73 page=0 chnl=15
|
||||
char id=50 x=1178 y=6 width=76 height=122 xoffset=13 yoffset=38 xadvance=100 page=0 chnl=15
|
||||
char id=51 x=1266 y=6 width=79 height=124 xoffset=13 yoffset=38 xadvance=103 page=0 chnl=15
|
||||
char id=52 x=1357 y=6 width=84 height=121 xoffset=10 yoffset=39 xadvance=103 page=0 chnl=15
|
||||
char id=53 x=1453 y=6 width=74 height=123 xoffset=13 yoffset=39 xadvance=98 page=0 chnl=15
|
||||
char id=54 x=1539 y=6 width=79 height=125 xoffset=11 yoffset=37 xadvance=100 page=0 chnl=15
|
||||
char id=55 x=1630 y=6 width=76 height=121 xoffset=8 yoffset=39 xadvance=93 page=0 chnl=15
|
||||
char id=56 x=1718 y=6 width=81 height=124 xoffset=10 yoffset=38 xadvance=101 page=0 chnl=15
|
||||
char id=57 x=1811 y=6 width=79 height=125 xoffset=11 yoffset=37 xadvance=100 page=0 chnl=15
|
||||
char id=58 x=1902 y=6 width=18 height=83 xoffset=13 yoffset=75 xadvance=44 page=0 chnl=15
|
||||
char id=59 x=1932 y=6 width=21 height=115 xoffset=12 yoffset=75 xadvance=45 page=0 chnl=15
|
||||
char id=60 x=6 y=218 width=73 height=88 xoffset=17 yoffset=69 xadvance=107 page=0 chnl=15
|
||||
char id=61 x=91 y=218 width=68 height=44 xoffset=20 yoffset=91 xadvance=107 page=0 chnl=15
|
||||
char id=62 x=171 y=218 width=73 height=88 xoffset=17 yoffset=69 xadvance=107 page=0 chnl=15
|
||||
char id=63 x=256 y=218 width=68 height=123 xoffset=6 yoffset=38 xadvance=82 page=0 chnl=15
|
||||
char id=64 x=336 y=218 width=139 height=148 xoffset=8 yoffset=44 xadvance=154 page=0 chnl=15
|
||||
char id=65 x=487 y=218 width=100 height=121 xoffset=4 yoffset=39 xadvance=108 page=0 chnl=15
|
||||
char id=66 x=599 y=218 width=82 height=121 xoffset=15 yoffset=39 xadvance=106 page=0 chnl=15
|
||||
char id=67 x=693 y=218 width=101 height=124 xoffset=10 yoffset=38 xadvance=119 page=0 chnl=15
|
||||
char id=68 x=806 y=218 width=93 height=121 xoffset=15 yoffset=39 xadvance=118 page=0 chnl=15
|
||||
char id=69 x=911 y=218 width=72 height=121 xoffset=15 yoffset=39 xadvance=98 page=0 chnl=15
|
||||
char id=70 x=995 y=218 width=71 height=121 xoffset=15 yoffset=39 xadvance=96 page=0 chnl=15
|
||||
char id=71 x=1078 y=218 width=102 height=124 xoffset=10 yoffset=38 xadvance=122 page=0 chnl=15
|
||||
char id=72 x=1192 y=218 width=92 height=121 xoffset=15 yoffset=39 xadvance=122 page=0 chnl=15
|
||||
char id=73 x=1296 y=218 width=12 height=121 xoffset=15 yoffset=39 xadvance=42 page=0 chnl=15
|
||||
char id=74 x=1320 y=218 width=66 height=123 xoffset=7 yoffset=39 xadvance=88 page=0 chnl=15
|
||||
char id=75 x=1398 y=218 width=87 height=121 xoffset=15 yoffset=39 xadvance=105 page=0 chnl=15
|
||||
char id=76 x=1497 y=218 width=70 height=121 xoffset=15 yoffset=39 xadvance=92 page=0 chnl=15
|
||||
char id=77 x=1579 y=218 width=115 height=121 xoffset=15 yoffset=39 xadvance=145 page=0 chnl=15
|
||||
char id=78 x=1706 y=218 width=95 height=121 xoffset=15 yoffset=39 xadvance=125 page=0 chnl=15
|
||||
char id=79 x=1813 y=218 width=105 height=124 xoffset=10 yoffset=38 xadvance=125 page=0 chnl=15
|
||||
char id=80 x=1930 y=218 width=79 height=121 xoffset=15 yoffset=39 xadvance=103 page=0 chnl=15
|
||||
char id=81 x=6 y=430 width=105 height=134 xoffset=10 yoffset=38 xadvance=125 page=0 chnl=15
|
||||
char id=82 x=123 y=430 width=84 height=121 xoffset=15 yoffset=39 xadvance=104 page=0 chnl=15
|
||||
char id=83 x=219 y=430 width=84 height=125 xoffset=10 yoffset=38 xadvance=103 page=0 chnl=15
|
||||
char id=84 x=315 y=430 width=88 height=121 xoffset=8 yoffset=39 xadvance=104 page=0 chnl=15
|
||||
char id=85 x=415 y=430 width=93 height=124 xoffset=15 yoffset=39 xadvance=123 page=0 chnl=15
|
||||
char id=86 x=520 y=430 width=100 height=121 xoffset=4 yoffset=39 xadvance=108 page=0 chnl=15
|
||||
char id=87 x=632 y=430 width=147 height=121 xoffset=3 yoffset=39 xadvance=153 page=0 chnl=15
|
||||
char id=88 x=791 y=430 width=95 height=121 xoffset=4 yoffset=39 xadvance=102 page=0 chnl=15
|
||||
char id=89 x=898 y=430 width=99 height=121 xoffset=4 yoffset=39 xadvance=107 page=0 chnl=15
|
||||
char id=90 x=1009 y=430 width=82 height=121 xoffset=10 yoffset=39 xadvance=102 page=0 chnl=15
|
||||
char id=91 x=1103 y=430 width=31 height=151 xoffset=19 yoffset=34 xadvance=57 page=0 chnl=15
|
||||
char id=92 x=1146 y=430 width=49 height=145 xoffset=4 yoffset=34 xadvance=57 page=0 chnl=15
|
||||
char id=93 x=1207 y=430 width=31 height=151 xoffset=7 yoffset=34 xadvance=57 page=0 chnl=15
|
||||
char id=94 x=1250 y=430 width=61 height=52 xoffset=6 yoffset=43 xadvance=73 page=0 chnl=15
|
||||
char id=95 x=1323 y=430 width=75 height=10 xoffset=-1 yoffset=160 xadvance=73 page=0 chnl=15
|
||||
char id=96 x=1410 y=430 width=28 height=28 xoffset=27 yoffset=30 xadvance=82 page=0 chnl=15
|
||||
char id=97 x=1450 y=430 width=69 height=95 xoffset=9 yoffset=68 xadvance=91 page=0 chnl=15
|
||||
char id=98 x=1531 y=430 width=78 height=123 xoffset=15 yoffset=39 xadvance=101 page=0 chnl=15
|
||||
char id=99 x=1621 y=430 width=75 height=94 xoffset=9 yoffset=68 xadvance=91 page=0 chnl=15
|
||||
char id=100 x=1708 y=430 width=77 height=123 xoffset=9 yoffset=39 xadvance=101 page=0 chnl=15
|
||||
char id=101 x=1797 y=430 width=78 height=94 xoffset=9 yoffset=68 xadvance=95 page=0 chnl=15
|
||||
char id=102 x=1887 y=430 width=50 height=126 xoffset=6 yoffset=34 xadvance=57 page=0 chnl=15
|
||||
char id=103 x=1949 y=430 width=77 height=128 xoffset=9 yoffset=68 xadvance=99 page=0 chnl=15
|
||||
char id=104 x=6 y=642 width=70 height=121 xoffset=13 yoffset=39 xadvance=96 page=0 chnl=15
|
||||
char id=105 x=88 y=642 width=17 height=121 xoffset=11 yoffset=39 xadvance=38 page=0 chnl=15
|
||||
char id=106 x=117 y=642 width=30 height=155 xoffset=-3 yoffset=39 xadvance=38 page=0 chnl=15
|
||||
char id=107 x=159 y=642 width=71 height=121 xoffset=13 yoffset=39 xadvance=86 page=0 chnl=15
|
||||
char id=108 x=242 y=642 width=12 height=121 xoffset=13 yoffset=39 xadvance=38 page=0 chnl=15
|
||||
char id=109 x=266 y=642 width=115 height=92 xoffset=13 yoffset=68 xadvance=141 page=0 chnl=15
|
||||
char id=110 x=393 y=642 width=69 height=92 xoffset=13 yoffset=68 xadvance=95 page=0 chnl=15
|
||||
char id=111 x=474 y=642 width=80 height=94 xoffset=9 yoffset=68 xadvance=97 page=0 chnl=15
|
||||
char id=112 x=566 y=642 width=77 height=126 xoffset=13 yoffset=68 xadvance=99 page=0 chnl=15
|
||||
char id=113 x=655 y=642 width=77 height=126 xoffset=9 yoffset=68 xadvance=99 page=0 chnl=15
|
||||
char id=114 x=744 y=642 width=42 height=92 xoffset=13 yoffset=68 xadvance=58 page=0 chnl=15
|
||||
char id=115 x=798 y=642 width=67 height=94 xoffset=9 yoffset=68 xadvance=84 page=0 chnl=15
|
||||
char id=116 x=877 y=642 width=47 height=114 xoffset=6 yoffset=48 xadvance=59 page=0 chnl=15
|
||||
char id=117 x=936 y=642 width=69 height=93 xoffset=13 yoffset=69 xadvance=95 page=0 chnl=15
|
||||
char id=118 x=1017 y=642 width=78 height=91 xoffset=6 yoffset=69 xadvance=90 page=0 chnl=15
|
||||
char id=119 x=1107 y=642 width=121 height=91 xoffset=6 yoffset=69 xadvance=133 page=0 chnl=15
|
||||
char id=120 x=1240 y=642 width=71 height=91 xoffset=8 yoffset=69 xadvance=87 page=0 chnl=15
|
||||
char id=121 x=1323 y=642 width=78 height=125 xoffset=6 yoffset=69 xadvance=90 page=0 chnl=15
|
||||
char id=122 x=1413 y=642 width=69 height=91 xoffset=10 yoffset=69 xadvance=87 page=0 chnl=15
|
||||
char id=123 x=1494 y=642 width=46 height=151 xoffset=7 yoffset=34 xadvance=57 page=0 chnl=15
|
||||
char id=124 x=1552 y=642 width=11 height=198 xoffset=20 yoffset=1 xadvance=50 page=0 chnl=15
|
||||
char id=125 x=1575 y=642 width=46 height=151 xoffset=4 yoffset=34 xadvance=57 page=0 chnl=15
|
||||
char id=126 x=1633 y=642 width=80 height=27 xoffset=14 yoffset=99 xadvance=107 page=0 chnl=15
|
||||
char id=163 x=1725 y=642 width=82 height=122 xoffset=11 yoffset=38 xadvance=104 page=0 chnl=15
|
||||
char id=165 x=1819 y=642 width=85 height=121 xoffset=2 yoffset=39 xadvance=89 page=0 chnl=15
|
||||
char id=167 x=1916 y=642 width=72 height=147 xoffset=9 yoffset=38 xadvance=89 page=0 chnl=15
|
||||
char id=176 x=6 y=854 width=53 height=53 xoffset=11 yoffset=38 xadvance=75 page=0 chnl=15
|
||||
char id=178 x=71 y=854 width=54 height=74 xoffset=8 yoffset=26 xadvance=69 page=0 chnl=15
|
||||
char id=215 x=137 y=854 width=74 height=74 xoffset=17 yoffset=76 xadvance=107 page=0 chnl=15
|
||||
char id=231 x=223 y=854 width=75 height=126 xoffset=9 yoffset=68 xadvance=91 page=0 chnl=15
|
||||
char id=234 x=310 y=854 width=78 height=127 xoffset=9 yoffset=35 xadvance=95 page=0 chnl=15
|
||||
char id=241 x=400 y=854 width=69 height=122 xoffset=13 yoffset=38 xadvance=95 page=0 chnl=15
|
||||
char id=252 x=481 y=854 width=69 height=123 xoffset=13 yoffset=39 xadvance=95 page=0 chnl=15
|
||||
char id=8209 x=562 y=854 width=51 height=11 xoffset=0 yoffset=103 xadvance=53 page=0 chnl=15
|
||||
char id=8211 x=625 y=854 width=83 height=11 xoffset=0 yoffset=103 xadvance=82 page=0 chnl=15
|
||||
char id=8226 x=720 y=854 width=45 height=46 xoffset=24 yoffset=89 xadvance=92 page=0 chnl=15
|
||||
char id=8364 x=777 y=854 width=96 height=124 xoffset=4 yoffset=38 xadvance=110 page=0 chnl=15
|
||||
char id=8627 x=885 y=854 width=128 height=102 xoffset=16 yoffset=39 xadvance=159 page=0 chnl=15
|
||||
char id=8679 x=1025 y=854 width=131 height=130 xoffset=15 yoffset=30 xadvance=160 page=0 chnl=15
|
||||
char id=9003 x=1168 y=854 width=173 height=121 xoffset=13 yoffset=39 xadvance=200 page=0 chnl=15
|
||||
char id=9251 x=1353 y=854 width=38 height=36 xoffset=39 yoffset=148 xadvance=116 page=0 chnl=15
|
||||
char id=9654 x=1403 y=854 width=128 height=152 xoffset=14 yoffset=24 xadvance=148 page=0 chnl=15
|
||||
char id=9664 x=1543 y=854 width=128 height=152 xoffset=7 yoffset=24 xadvance=148 page=0 chnl=15
|
||||
char id=9675 x=1683 y=854 width=125 height=125 xoffset=13 yoffset=37 xadvance=151 page=0 chnl=15
|
||||
char id=9679 x=1820 y=854 width=125 height=125 xoffset=13 yoffset=37 xadvance=151 page=0 chnl=15
|
||||
char id=9881 x=1957 y=854 width=0 height=0 xoffset=0 yoffset=0 xadvance=0 page=0 chnl=15
|
||||
char id=10003 x=6 y=1066 width=112 height=80 xoffset=18 yoffset=64 xadvance=145 page=0 chnl=15
|
||||
char id=10004 x=130 y=1066 width=0 height=0 xoffset=0 yoffset=0 xadvance=0 page=0 chnl=15
|
||||
char id=10005 x=142 y=1066 width=0 height=0 xoffset=0 yoffset=0 xadvance=0 page=0 chnl=15
|
||||
|
After Width: | Height: | Size: 222 KiB |
@@ -0,0 +1,125 @@
|
||||
info face="Inter-Medium" size=-200 bold=0 italic=0 charset="" unicode=1 stretchH=100 smooth=0 aa=1 padding=0,0,0,0 spacing=0,0 outline=0
|
||||
common lineHeight=200 base=200 scaleW=2048 scaleH=2048 pages=1 packed=0 alphaChnl=0 redChnl=4 greenChnl=4 blueChnl=4
|
||||
page id=0 file="Inter-Medium.png"
|
||||
chars count=121
|
||||
char id=32 x=6 y=6 width=43 height=200 xoffset=0 yoffset=0 xadvance=43 page=0 chnl=15
|
||||
char id=33 x=61 y=6 width=25 height=123 xoffset=12 yoffset=39 xadvance=48 page=0 chnl=15
|
||||
char id=34 x=98 y=6 width=46 height=44 xoffset=10 yoffset=39 xadvance=66 page=0 chnl=15
|
||||
char id=35 x=156 y=6 width=101 height=121 xoffset=2 yoffset=39 xadvance=105 page=0 chnl=15
|
||||
char id=36 x=269 y=6 width=90 height=152 xoffset=8 yoffset=24 xadvance=106 page=0 chnl=15
|
||||
char id=37 x=371 y=6 width=111 height=126 xoffset=13 yoffset=37 xadvance=136 page=0 chnl=15
|
||||
char id=38 x=494 y=6 width=99 height=124 xoffset=7 yoffset=38 xadvance=107 page=0 chnl=15
|
||||
char id=39 x=605 y=6 width=16 height=44 xoffset=10 yoffset=39 xadvance=35 page=0 chnl=15
|
||||
char id=40 x=633 y=6 width=40 height=150 xoffset=16 yoffset=35 xadvance=62 page=0 chnl=15
|
||||
char id=41 x=685 y=6 width=40 height=150 xoffset=6 yoffset=35 xadvance=62 page=0 chnl=15
|
||||
char id=42 x=737 y=6 width=66 height=69 xoffset=10 yoffset=39 xadvance=86 page=0 chnl=15
|
||||
char id=43 x=815 y=6 width=80 height=80 xoffset=15 yoffset=73 xadvance=110 page=0 chnl=15
|
||||
char id=44 x=907 y=6 width=25 height=47 xoffset=10 yoffset=143 xadvance=47 page=0 chnl=15
|
||||
char id=45 x=944 y=6 width=54 height=16 xoffset=11 yoffset=101 xadvance=76 page=0 chnl=15
|
||||
char id=46 x=1010 y=6 width=25 height=25 xoffset=11 yoffset=137 xadvance=46 page=0 chnl=15
|
||||
char id=47 x=1047 y=6 width=55 height=145 xoffset=3 yoffset=34 xadvance=60 page=0 chnl=15
|
||||
char id=48 x=1114 y=6 width=89 height=124 xoffset=9 yoffset=38 xadvance=106 page=0 chnl=15
|
||||
char id=49 x=1215 y=6 width=49 height=121 xoffset=9 yoffset=39 xadvance=78 page=0 chnl=15
|
||||
char id=50 x=1276 y=6 width=80 height=122 xoffset=11 yoffset=38 xadvance=101 page=0 chnl=15
|
||||
char id=51 x=1368 y=6 width=86 height=124 xoffset=10 yoffset=38 xadvance=106 page=0 chnl=15
|
||||
char id=52 x=1466 y=6 width=91 height=121 xoffset=9 yoffset=39 xadvance=108 page=0 chnl=15
|
||||
char id=53 x=1569 y=6 width=82 height=123 xoffset=11 yoffset=39 xadvance=102 page=0 chnl=15
|
||||
char id=54 x=1663 y=6 width=87 height=125 xoffset=9 yoffset=37 xadvance=105 page=0 chnl=15
|
||||
char id=55 x=1762 y=6 width=80 height=121 xoffset=7 yoffset=39 xadvance=95 page=0 chnl=15
|
||||
char id=56 x=1854 y=6 width=86 height=124 xoffset=9 yoffset=38 xadvance=104 page=0 chnl=15
|
||||
char id=57 x=6 y=218 width=87 height=125 xoffset=9 yoffset=37 xadvance=105 page=0 chnl=15
|
||||
char id=58 x=105 y=218 width=25 height=90 xoffset=11 yoffset=72 xadvance=46 page=0 chnl=15
|
||||
char id=59 x=142 y=218 width=28 height=118 xoffset=10 yoffset=72 xadvance=47 page=0 chnl=15
|
||||
char id=60 x=182 y=218 width=79 height=90 xoffset=16 yoffset=68 xadvance=110 page=0 chnl=15
|
||||
char id=61 x=273 y=218 width=76 height=53 xoffset=17 yoffset=86 xadvance=110 page=0 chnl=15
|
||||
char id=62 x=361 y=218 width=79 height=90 xoffset=16 yoffset=68 xadvance=110 page=0 chnl=15
|
||||
char id=63 x=452 y=218 width=73 height=124 xoffset=6 yoffset=38 xadvance=86 page=0 chnl=15
|
||||
char id=64 x=537 y=218 width=145 height=151 xoffset=8 yoffset=42 xadvance=159 page=0 chnl=15
|
||||
char id=65 x=694 y=218 width=108 height=121 xoffset=4 yoffset=39 xadvance=115 page=0 chnl=15
|
||||
char id=66 x=814 y=218 width=87 height=121 xoffset=13 yoffset=39 xadvance=108 page=0 chnl=15
|
||||
char id=67 x=913 y=218 width=105 height=124 xoffset=9 yoffset=38 xadvance=121 page=0 chnl=15
|
||||
char id=68 x=1030 y=218 width=97 height=121 xoffset=13 yoffset=39 xadvance=119 page=0 chnl=15
|
||||
char id=69 x=1139 y=218 width=77 height=121 xoffset=13 yoffset=39 xadvance=99 page=0 chnl=15
|
||||
char id=70 x=1228 y=218 width=75 height=121 xoffset=13 yoffset=39 xadvance=96 page=0 chnl=15
|
||||
char id=71 x=1315 y=218 width=105 height=124 xoffset=9 yoffset=38 xadvance=123 page=0 chnl=15
|
||||
char id=72 x=1432 y=218 width=97 height=121 xoffset=13 yoffset=39 xadvance=122 page=0 chnl=15
|
||||
char id=73 x=1541 y=218 width=19 height=121 xoffset=13 yoffset=39 xadvance=44 page=0 chnl=15
|
||||
char id=74 x=1572 y=218 width=73 height=123 xoffset=5 yoffset=39 xadvance=91 page=0 chnl=15
|
||||
char id=75 x=1657 y=218 width=94 height=121 xoffset=13 yoffset=39 xadvance=109 page=0 chnl=15
|
||||
char id=76 x=1763 y=218 width=73 height=121 xoffset=13 yoffset=39 xadvance=93 page=0 chnl=15
|
||||
char id=77 x=1848 y=218 width=123 height=121 xoffset=13 yoffset=39 xadvance=148 page=0 chnl=15
|
||||
char id=78 x=6 y=430 width=98 height=121 xoffset=13 yoffset=39 xadvance=123 page=0 chnl=15
|
||||
char id=79 x=116 y=430 width=109 height=124 xoffset=9 yoffset=38 xadvance=126 page=0 chnl=15
|
||||
char id=80 x=237 y=430 width=85 height=121 xoffset=13 yoffset=39 xadvance=105 page=0 chnl=15
|
||||
char id=81 x=334 y=430 width=109 height=133 xoffset=9 yoffset=38 xadvance=126 page=0 chnl=15
|
||||
char id=82 x=455 y=430 width=90 height=121 xoffset=13 yoffset=39 xadvance=106 page=0 chnl=15
|
||||
char id=83 x=557 y=430 width=90 height=124 xoffset=8 yoffset=38 xadvance=106 page=0 chnl=15
|
||||
char id=84 x=659 y=430 width=94 height=121 xoffset=7 yoffset=39 xadvance=107 page=0 chnl=15
|
||||
char id=85 x=765 y=430 width=96 height=123 xoffset=13 yoffset=39 xadvance=121 page=0 chnl=15
|
||||
char id=86 x=873 y=430 width=108 height=121 xoffset=4 yoffset=39 xadvance=115 page=0 chnl=15
|
||||
char id=87 x=993 y=430 width=156 height=121 xoffset=3 yoffset=39 xadvance=161 page=0 chnl=15
|
||||
char id=88 x=1161 y=430 width=102 height=121 xoffset=4 yoffset=39 xadvance=110 page=0 chnl=15
|
||||
char id=89 x=1275 y=430 width=107 height=121 xoffset=3 yoffset=39 xadvance=113 page=0 chnl=15
|
||||
char id=90 x=1394 y=430 width=88 height=121 xoffset=9 yoffset=39 xadvance=105 page=0 chnl=15
|
||||
char id=91 x=1494 y=430 width=40 height=150 xoffset=18 yoffset=35 xadvance=62 page=0 chnl=15
|
||||
char id=92 x=1546 y=430 width=55 height=145 xoffset=3 yoffset=34 xadvance=60 page=0 chnl=15
|
||||
char id=93 x=1613 y=430 width=39 height=150 xoffset=5 yoffset=35 xadvance=62 page=0 chnl=15
|
||||
char id=94 x=1664 y=430 width=67 height=52 xoffset=6 yoffset=43 xadvance=78 page=0 chnl=15
|
||||
char id=95 x=1743 y=430 width=78 height=15 xoffset=-1 yoffset=160 xadvance=75 page=0 chnl=15
|
||||
char id=96 x=1833 y=430 width=33 height=28 xoffset=25 yoffset=30 xadvance=82 page=0 chnl=15
|
||||
char id=97 x=1878 y=430 width=76 height=94 xoffset=7 yoffset=68 xadvance=94 page=0 chnl=15
|
||||
char id=98 x=6 y=642 width=83 height=123 xoffset=13 yoffset=39 xadvance=103 page=0 chnl=15
|
||||
char id=99 x=101 y=642 width=80 height=94 xoffset=7 yoffset=68 xadvance=93 page=0 chnl=15
|
||||
char id=100 x=193 y=642 width=84 height=123 xoffset=7 yoffset=39 xadvance=103 page=0 chnl=15
|
||||
char id=101 x=289 y=642 width=83 height=94 xoffset=7 yoffset=68 xadvance=97 page=0 chnl=15
|
||||
char id=102 x=384 y=642 width=57 height=126 xoffset=3 yoffset=34 xadvance=60 page=0 chnl=15
|
||||
char id=103 x=453 y=642 width=84 height=128 xoffset=7 yoffset=68 xadvance=101 page=0 chnl=15
|
||||
char id=104 x=549 y=642 width=77 height=121 xoffset=11 yoffset=39 xadvance=99 page=0 chnl=15
|
||||
char id=105 x=638 y=642 width=23 height=125 xoffset=9 yoffset=35 xadvance=41 page=0 chnl=15
|
||||
char id=106 x=673 y=642 width=35 height=159 xoffset=-3 yoffset=35 xadvance=41 page=0 chnl=15
|
||||
char id=107 x=720 y=642 width=80 height=121 xoffset=11 yoffset=39 xadvance=91 page=0 chnl=15
|
||||
char id=108 x=812 y=642 width=19 height=121 xoffset=11 yoffset=39 xadvance=41 page=0 chnl=15
|
||||
char id=109 x=843 y=642 width=124 height=92 xoffset=11 yoffset=68 xadvance=146 page=0 chnl=15
|
||||
char id=110 x=979 y=642 width=77 height=92 xoffset=11 yoffset=68 xadvance=98 page=0 chnl=15
|
||||
char id=111 x=1068 y=642 width=85 height=94 xoffset=7 yoffset=68 xadvance=99 page=0 chnl=15
|
||||
char id=112 x=1165 y=642 width=84 height=126 xoffset=11 yoffset=68 xadvance=101 page=0 chnl=15
|
||||
char id=113 x=1261 y=642 width=84 height=126 xoffset=7 yoffset=68 xadvance=101 page=0 chnl=15
|
||||
char id=114 x=1357 y=642 width=50 height=92 xoffset=11 yoffset=68 xadvance=63 page=0 chnl=15
|
||||
char id=115 x=1419 y=642 width=75 height=94 xoffset=7 yoffset=68 xadvance=88 page=0 chnl=15
|
||||
char id=116 x=1506 y=642 width=52 height=114 xoffset=4 yoffset=48 xadvance=61 page=0 chnl=15
|
||||
char id=117 x=1570 y=642 width=76 height=93 xoffset=11 yoffset=69 xadvance=98 page=0 chnl=15
|
||||
char id=118 x=1658 y=642 width=85 height=91 xoffset=4 yoffset=69 xadvance=93 page=0 chnl=15
|
||||
char id=119 x=1755 y=642 width=128 height=91 xoffset=4 yoffset=69 xadvance=136 page=0 chnl=15
|
||||
char id=120 x=1895 y=642 width=80 height=91 xoffset=6 yoffset=69 xadvance=91 page=0 chnl=15
|
||||
char id=121 x=6 y=854 width=86 height=125 xoffset=4 yoffset=69 xadvance=93 page=0 chnl=15
|
||||
char id=122 x=104 y=854 width=74 height=91 xoffset=9 yoffset=69 xadvance=91 page=0 chnl=15
|
||||
char id=123 x=190 y=854 width=53 height=150 xoffset=5 yoffset=35 xadvance=62 page=0 chnl=15
|
||||
char id=124 x=255 y=854 width=17 height=198 xoffset=20 yoffset=1 xadvance=56 page=0 chnl=15
|
||||
char id=125 x=284 y=854 width=52 height=150 xoffset=5 yoffset=35 xadvance=62 page=0 chnl=15
|
||||
char id=126 x=348 y=854 width=85 height=34 xoffset=13 yoffset=95 xadvance=110 page=0 chnl=15
|
||||
char id=163 x=445 y=854 width=86 height=122 xoffset=10 yoffset=38 xadvance=106 page=0 chnl=15
|
||||
char id=165 x=543 y=854 width=88 height=121 xoffset=2 yoffset=39 xadvance=91 page=0 chnl=15
|
||||
char id=167 x=643 y=854 width=77 height=147 xoffset=8 yoffset=38 xadvance=93 page=0 chnl=15
|
||||
char id=176 x=732 y=854 width=56 height=57 xoffset=10 yoffset=38 xadvance=75 page=0 chnl=15
|
||||
char id=178 x=800 y=854 width=57 height=74 xoffset=7 yoffset=26 xadvance=69 page=0 chnl=15
|
||||
char id=215 x=869 y=854 width=80 height=79 xoffset=15 yoffset=73 xadvance=110 page=0 chnl=15
|
||||
char id=231 x=961 y=854 width=80 height=129 xoffset=7 yoffset=68 xadvance=93 page=0 chnl=15
|
||||
char id=234 x=1053 y=854 width=83 height=129 xoffset=7 yoffset=33 xadvance=97 page=0 chnl=15
|
||||
char id=241 x=1148 y=854 width=77 height=124 xoffset=11 yoffset=36 xadvance=98 page=0 chnl=15
|
||||
char id=252 x=1237 y=854 width=76 height=127 xoffset=11 yoffset=35 xadvance=98 page=0 chnl=15
|
||||
char id=8209 x=1325 y=854 width=54 height=16 xoffset=6 yoffset=101 xadvance=65 page=0 chnl=15
|
||||
char id=8211 x=1391 y=854 width=83 height=16 xoffset=0 yoffset=101 xadvance=82 page=0 chnl=15
|
||||
char id=8226 x=1486 y=854 width=53 height=52 xoffset=17 yoffset=86 xadvance=87 page=0 chnl=15
|
||||
char id=8364 x=1551 y=854 width=98 height=124 xoffset=4 yoffset=38 xadvance=111 page=0 chnl=15
|
||||
char id=8627 x=1661 y=854 width=133 height=105 xoffset=16 yoffset=39 xadvance=164 page=0 chnl=15
|
||||
char id=8679 x=1806 y=854 width=146 height=130 xoffset=14 yoffset=30 xadvance=173 page=0 chnl=15
|
||||
char id=9003 x=6 y=1066 width=173 height=121 xoffset=13 yoffset=39 xadvance=200 page=0 chnl=15
|
||||
char id=9251 x=191 y=1066 width=99 height=39 xoffset=11 yoffset=148 xadvance=121 page=0 chnl=15
|
||||
char id=9654 x=302 y=1066 width=130 height=152 xoffset=16 yoffset=24 xadvance=153 page=0 chnl=15
|
||||
char id=9664 x=444 y=1066 width=130 height=152 xoffset=8 yoffset=24 xadvance=153 page=0 chnl=15
|
||||
char id=9675 x=586 y=1066 width=125 height=125 xoffset=13 yoffset=37 xadvance=151 page=0 chnl=15
|
||||
char id=9679 x=723 y=1066 width=125 height=125 xoffset=13 yoffset=37 xadvance=151 page=0 chnl=15
|
||||
char id=9881 x=860 y=1066 width=0 height=0 xoffset=0 yoffset=0 xadvance=0 page=0 chnl=15
|
||||
char id=10003 x=872 y=1066 width=116 height=86 xoffset=17 yoffset=62 xadvance=145 page=0 chnl=15
|
||||
char id=10004 x=1000 y=1066 width=0 height=0 xoffset=0 yoffset=0 xadvance=0 page=0 chnl=15
|
||||
char id=10005 x=1012 y=1066 width=0 height=0 xoffset=0 yoffset=0 xadvance=0 page=0 chnl=15
|
||||
|
After Width: | Height: | Size: 220 KiB |
@@ -0,0 +1,125 @@
|
||||
info face="Inter-Regular" size=-200 bold=0 italic=0 charset="" unicode=1 stretchH=100 smooth=0 aa=1 padding=0,0,0,0 spacing=0,0 outline=0
|
||||
common lineHeight=200 base=200 scaleW=2048 scaleH=2048 pages=1 packed=0 alphaChnl=0 redChnl=4 greenChnl=4 blueChnl=4
|
||||
page id=0 file="Inter-Regular.png"
|
||||
chars count=121
|
||||
char id=32 x=6 y=6 width=46 height=200 xoffset=0 yoffset=0 xadvance=46 page=0 chnl=15
|
||||
char id=33 x=64 y=6 width=22 height=122 xoffset=12 yoffset=39 xadvance=46 page=0 chnl=15
|
||||
char id=34 x=98 y=6 width=44 height=43 xoffset=11 yoffset=39 xadvance=66 page=0 chnl=15
|
||||
char id=35 x=154 y=6 width=100 height=121 xoffset=2 yoffset=39 xadvance=104 page=0 chnl=15
|
||||
char id=36 x=266 y=6 width=88 height=152 xoffset=9 yoffset=24 xadvance=105 page=0 chnl=15
|
||||
char id=37 x=366 y=6 width=108 height=124 xoffset=13 yoffset=38 xadvance=134 page=0 chnl=15
|
||||
char id=38 x=486 y=6 width=96 height=124 xoffset=8 yoffset=38 xadvance=105 page=0 chnl=15
|
||||
char id=39 x=594 y=6 width=14 height=43 xoffset=11 yoffset=39 xadvance=36 page=0 chnl=15
|
||||
char id=40 x=620 y=6 width=36 height=151 xoffset=17 yoffset=34 xadvance=59 page=0 chnl=15
|
||||
char id=41 x=668 y=6 width=36 height=151 xoffset=7 yoffset=34 xadvance=59 page=0 chnl=15
|
||||
char id=42 x=716 y=6 width=65 height=69 xoffset=9 yoffset=39 xadvance=82 page=0 chnl=15
|
||||
char id=43 x=793 y=6 width=79 height=78 xoffset=15 yoffset=74 xadvance=108 page=0 chnl=15
|
||||
char id=44 x=884 y=6 width=23 height=47 xoffset=11 yoffset=143 xadvance=46 page=0 chnl=15
|
||||
char id=45 x=919 y=6 width=54 height=13 xoffset=11 yoffset=102 xadvance=76 page=0 chnl=15
|
||||
char id=46 x=985 y=6 width=22 height=22 xoffset=12 yoffset=139 xadvance=45 page=0 chnl=15
|
||||
char id=47 x=1019 y=6 width=53 height=145 xoffset=3 yoffset=34 xadvance=58 page=0 chnl=15
|
||||
char id=48 x=1084 y=6 width=85 height=124 xoffset=9 yoffset=38 xadvance=103 page=0 chnl=15
|
||||
char id=49 x=1181 y=6 width=45 height=121 xoffset=10 yoffset=39 xadvance=76 page=0 chnl=15
|
||||
char id=50 x=1238 y=6 width=77 height=122 xoffset=12 yoffset=38 xadvance=100 page=0 chnl=15
|
||||
char id=51 x=1327 y=6 width=82 height=124 xoffset=12 yoffset=38 xadvance=105 page=0 chnl=15
|
||||
char id=52 x=1421 y=6 width=88 height=121 xoffset=9 yoffset=39 xadvance=106 page=0 chnl=15
|
||||
char id=53 x=1521 y=6 width=78 height=123 xoffset=12 yoffset=39 xadvance=100 page=0 chnl=15
|
||||
char id=54 x=1611 y=6 width=83 height=125 xoffset=10 yoffset=37 xadvance=103 page=0 chnl=15
|
||||
char id=55 x=1706 y=6 width=78 height=121 xoffset=7 yoffset=39 xadvance=94 page=0 chnl=15
|
||||
char id=56 x=1796 y=6 width=82 height=124 xoffset=10 yoffset=38 xadvance=101 page=0 chnl=15
|
||||
char id=57 x=1890 y=6 width=83 height=125 xoffset=10 yoffset=37 xadvance=103 page=0 chnl=15
|
||||
char id=58 x=1985 y=6 width=22 height=89 xoffset=12 yoffset=72 xadvance=45 page=0 chnl=15
|
||||
char id=59 x=6 y=218 width=25 height=118 xoffset=11 yoffset=72 xadvance=46 page=0 chnl=15
|
||||
char id=60 x=43 y=218 width=77 height=88 xoffset=16 yoffset=69 xadvance=108 page=0 chnl=15
|
||||
char id=61 x=132 y=218 width=73 height=48 xoffset=18 yoffset=89 xadvance=108 page=0 chnl=15
|
||||
char id=62 x=217 y=218 width=77 height=88 xoffset=16 yoffset=69 xadvance=108 page=0 chnl=15
|
||||
char id=63 x=306 y=218 width=70 height=123 xoffset=6 yoffset=38 xadvance=83 page=0 chnl=15
|
||||
char id=64 x=388 y=218 width=140 height=148 xoffset=7 yoffset=44 xadvance=154 page=0 chnl=15
|
||||
char id=65 x=540 y=218 width=104 height=121 xoffset=4 yoffset=39 xadvance=111 page=0 chnl=15
|
||||
char id=66 x=656 y=218 width=85 height=121 xoffset=14 yoffset=39 xadvance=107 page=0 chnl=15
|
||||
char id=67 x=753 y=218 width=103 height=124 xoffset=9 yoffset=38 xadvance=120 page=0 chnl=15
|
||||
char id=68 x=868 y=218 width=95 height=121 xoffset=14 yoffset=39 xadvance=118 page=0 chnl=15
|
||||
char id=69 x=975 y=218 width=75 height=121 xoffset=14 yoffset=39 xadvance=98 page=0 chnl=15
|
||||
char id=70 x=1062 y=218 width=73 height=121 xoffset=14 yoffset=39 xadvance=96 page=0 chnl=15
|
||||
char id=71 x=1147 y=218 width=104 height=124 xoffset=9 yoffset=38 xadvance=122 page=0 chnl=15
|
||||
char id=72 x=1263 y=218 width=94 height=121 xoffset=14 yoffset=39 xadvance=122 page=0 chnl=15
|
||||
char id=73 x=1369 y=218 width=16 height=121 xoffset=14 yoffset=39 xadvance=43 page=0 chnl=15
|
||||
char id=74 x=1397 y=218 width=70 height=123 xoffset=6 yoffset=39 xadvance=89 page=0 chnl=15
|
||||
char id=75 x=1479 y=218 width=90 height=121 xoffset=14 yoffset=39 xadvance=107 page=0 chnl=15
|
||||
char id=76 x=1581 y=218 width=71 height=121 xoffset=14 yoffset=39 xadvance=92 page=0 chnl=15
|
||||
char id=77 x=1664 y=218 width=119 height=121 xoffset=14 yoffset=39 xadvance=146 page=0 chnl=15
|
||||
char id=78 x=1795 y=218 width=96 height=121 xoffset=14 yoffset=39 xadvance=124 page=0 chnl=15
|
||||
char id=79 x=1903 y=218 width=107 height=124 xoffset=9 yoffset=38 xadvance=125 page=0 chnl=15
|
||||
char id=80 x=6 y=430 width=83 height=121 xoffset=14 yoffset=39 xadvance=104 page=0 chnl=15
|
||||
char id=81 x=101 y=430 width=107 height=134 xoffset=9 yoffset=38 xadvance=125 page=0 chnl=15
|
||||
char id=82 x=220 y=430 width=87 height=121 xoffset=14 yoffset=39 xadvance=105 page=0 chnl=15
|
||||
char id=83 x=319 y=430 width=88 height=125 xoffset=9 yoffset=38 xadvance=105 page=0 chnl=15
|
||||
char id=84 x=419 y=430 width=92 height=121 xoffset=7 yoffset=39 xadvance=106 page=0 chnl=15
|
||||
char id=85 x=523 y=430 width=94 height=124 xoffset=14 yoffset=39 xadvance=122 page=0 chnl=15
|
||||
char id=86 x=629 y=430 width=104 height=121 xoffset=4 yoffset=39 xadvance=111 page=0 chnl=15
|
||||
char id=87 x=745 y=430 width=151 height=121 xoffset=3 yoffset=39 xadvance=156 page=0 chnl=15
|
||||
char id=88 x=908 y=430 width=98 height=121 xoffset=4 yoffset=39 xadvance=106 page=0 chnl=15
|
||||
char id=89 x=1018 y=430 width=102 height=121 xoffset=4 yoffset=39 xadvance=109 page=0 chnl=15
|
||||
char id=90 x=1132 y=430 width=85 height=121 xoffset=9 yoffset=39 xadvance=103 page=0 chnl=15
|
||||
char id=91 x=1229 y=430 width=36 height=151 xoffset=19 yoffset=34 xadvance=59 page=0 chnl=15
|
||||
char id=92 x=1277 y=430 width=53 height=145 xoffset=3 yoffset=34 xadvance=58 page=0 chnl=15
|
||||
char id=93 x=1342 y=430 width=36 height=151 xoffset=5 yoffset=34 xadvance=59 page=0 chnl=15
|
||||
char id=94 x=1390 y=430 width=65 height=52 xoffset=6 yoffset=43 xadvance=77 page=0 chnl=15
|
||||
char id=95 x=1467 y=430 width=76 height=13 xoffset=-1 yoffset=160 xadvance=74 page=0 chnl=15
|
||||
char id=96 x=1555 y=430 width=32 height=28 xoffset=25 yoffset=30 xadvance=82 page=0 chnl=15
|
||||
char id=97 x=1599 y=430 width=73 height=95 xoffset=8 yoffset=68 xadvance=93 page=0 chnl=15
|
||||
char id=98 x=1684 y=430 width=81 height=123 xoffset=14 yoffset=39 xadvance=102 page=0 chnl=15
|
||||
char id=99 x=1777 y=430 width=77 height=94 xoffset=8 yoffset=68 xadvance=92 page=0 chnl=15
|
||||
char id=100 x=1866 y=430 width=81 height=123 xoffset=8 yoffset=39 xadvance=102 page=0 chnl=15
|
||||
char id=101 x=6 y=642 width=80 height=94 xoffset=8 yoffset=68 xadvance=96 page=0 chnl=15
|
||||
char id=102 x=98 y=642 width=54 height=126 xoffset=4 yoffset=34 xadvance=59 page=0 chnl=15
|
||||
char id=103 x=164 y=642 width=81 height=128 xoffset=8 yoffset=68 xadvance=100 page=0 chnl=15
|
||||
char id=104 x=257 y=642 width=73 height=121 xoffset=12 yoffset=39 xadvance=97 page=0 chnl=15
|
||||
char id=105 x=342 y=642 width=21 height=124 xoffset=9 yoffset=36 xadvance=39 page=0 chnl=15
|
||||
char id=106 x=375 y=642 width=33 height=158 xoffset=-3 yoffset=36 xadvance=39 page=0 chnl=15
|
||||
char id=107 x=420 y=642 width=76 height=121 xoffset=12 yoffset=39 xadvance=89 page=0 chnl=15
|
||||
char id=108 x=508 y=642 width=15 height=121 xoffset=12 yoffset=39 xadvance=39 page=0 chnl=15
|
||||
char id=109 x=535 y=642 width=119 height=92 xoffset=12 yoffset=68 xadvance=143 page=0 chnl=15
|
||||
char id=110 x=666 y=642 width=73 height=92 xoffset=12 yoffset=68 xadvance=96 page=0 chnl=15
|
||||
char id=111 x=751 y=642 width=83 height=94 xoffset=8 yoffset=68 xadvance=98 page=0 chnl=15
|
||||
char id=112 x=846 y=642 width=81 height=126 xoffset=12 yoffset=68 xadvance=100 page=0 chnl=15
|
||||
char id=113 x=939 y=642 width=81 height=126 xoffset=8 yoffset=68 xadvance=100 page=0 chnl=15
|
||||
char id=114 x=1032 y=642 width=46 height=92 xoffset=12 yoffset=68 xadvance=61 page=0 chnl=15
|
||||
char id=115 x=1090 y=642 width=71 height=94 xoffset=8 yoffset=68 xadvance=86 page=0 chnl=15
|
||||
char id=116 x=1173 y=642 width=49 height=114 xoffset=5 yoffset=48 xadvance=60 page=0 chnl=15
|
||||
char id=117 x=1234 y=642 width=72 height=93 xoffset=12 yoffset=69 xadvance=96 page=0 chnl=15
|
||||
char id=118 x=1318 y=642 width=82 height=91 xoffset=5 yoffset=69 xadvance=92 page=0 chnl=15
|
||||
char id=119 x=1412 y=642 width=124 height=91 xoffset=5 yoffset=69 xadvance=134 page=0 chnl=15
|
||||
char id=120 x=1548 y=642 width=76 height=91 xoffset=7 yoffset=69 xadvance=89 page=0 chnl=15
|
||||
char id=121 x=1636 y=642 width=82 height=125 xoffset=5 yoffset=69 xadvance=92 page=0 chnl=15
|
||||
char id=122 x=1730 y=642 width=71 height=91 xoffset=10 yoffset=69 xadvance=89 page=0 chnl=15
|
||||
char id=123 x=1813 y=642 width=49 height=151 xoffset=6 yoffset=34 xadvance=59 page=0 chnl=15
|
||||
char id=124 x=1874 y=642 width=12 height=198 xoffset=21 yoffset=1 xadvance=53 page=0 chnl=15
|
||||
char id=125 x=1898 y=642 width=49 height=151 xoffset=5 yoffset=34 xadvance=59 page=0 chnl=15
|
||||
char id=126 x=6 y=854 width=83 height=31 xoffset=13 yoffset=97 xadvance=108 page=0 chnl=15
|
||||
char id=163 x=101 y=854 width=84 height=122 xoffset=10 yoffset=38 xadvance=104 page=0 chnl=15
|
||||
char id=165 x=197 y=854 width=87 height=121 xoffset=2 yoffset=39 xadvance=90 page=0 chnl=15
|
||||
char id=167 x=296 y=854 width=75 height=147 xoffset=8 yoffset=38 xadvance=91 page=0 chnl=15
|
||||
char id=176 x=383 y=854 width=55 height=55 xoffset=10 yoffset=38 xadvance=75 page=0 chnl=15
|
||||
char id=178 x=450 y=854 width=56 height=74 xoffset=7 yoffset=26 xadvance=69 page=0 chnl=15
|
||||
char id=215 x=518 y=854 width=77 height=76 xoffset=16 yoffset=75 xadvance=108 page=0 chnl=15
|
||||
char id=231 x=607 y=854 width=77 height=128 xoffset=8 yoffset=68 xadvance=92 page=0 chnl=15
|
||||
char id=234 x=696 y=854 width=80 height=129 xoffset=8 yoffset=33 xadvance=96 page=0 chnl=15
|
||||
char id=241 x=788 y=854 width=73 height=124 xoffset=12 yoffset=36 xadvance=96 page=0 chnl=15
|
||||
char id=252 x=873 y=854 width=72 height=126 xoffset=12 yoffset=36 xadvance=96 page=0 chnl=15
|
||||
char id=8209 x=957 y=854 width=53 height=13 xoffset=3 yoffset=102 xadvance=59 page=0 chnl=15
|
||||
char id=8211 x=1022 y=854 width=83 height=13 xoffset=0 yoffset=102 xadvance=82 page=0 chnl=15
|
||||
char id=8226 x=1117 y=854 width=53 height=52 xoffset=20 yoffset=86 xadvance=92 page=0 chnl=15
|
||||
char id=8364 x=1182 y=854 width=96 height=124 xoffset=4 yoffset=38 xadvance=109 page=0 chnl=15
|
||||
char id=8627 x=1290 y=854 width=131 height=104 xoffset=16 yoffset=39 xadvance=162 page=0 chnl=15
|
||||
char id=8679 x=1433 y=854 width=139 height=130 xoffset=15 yoffset=30 xadvance=169 page=0 chnl=15
|
||||
char id=9003 x=1584 y=854 width=173 height=121 xoffset=13 yoffset=39 xadvance=200 page=0 chnl=15
|
||||
char id=9251 x=1769 y=854 width=96 height=38 xoffset=12 yoffset=148 xadvance=119 page=0 chnl=15
|
||||
char id=9654 x=1877 y=854 width=129 height=152 xoffset=15 yoffset=24 xadvance=151 page=0 chnl=15
|
||||
char id=9664 x=6 y=1066 width=129 height=152 xoffset=7 yoffset=24 xadvance=151 page=0 chnl=15
|
||||
char id=9675 x=147 y=1066 width=125 height=125 xoffset=13 yoffset=37 xadvance=151 page=0 chnl=15
|
||||
char id=9679 x=284 y=1066 width=125 height=125 xoffset=13 yoffset=37 xadvance=151 page=0 chnl=15
|
||||
char id=9881 x=421 y=1066 width=0 height=0 xoffset=0 yoffset=0 xadvance=0 page=0 chnl=15
|
||||
char id=10003 x=433 y=1066 width=114 height=83 xoffset=17 yoffset=63 xadvance=145 page=0 chnl=15
|
||||
char id=10004 x=559 y=1066 width=0 height=0 xoffset=0 yoffset=0 xadvance=0 page=0 chnl=15
|
||||
char id=10005 x=571 y=1066 width=0 height=0 xoffset=0 yoffset=0 xadvance=0 page=0 chnl=15
|
||||
|
After Width: | Height: | Size: 221 KiB |
@@ -0,0 +1,125 @@
|
||||
info face="Inter-SemiBold" size=-200 bold=0 italic=0 charset="" unicode=1 stretchH=100 smooth=0 aa=1 padding=0,0,0,0 spacing=0,0 outline=0
|
||||
common lineHeight=200 base=200 scaleW=2048 scaleH=2048 pages=1 packed=0 alphaChnl=0 redChnl=4 greenChnl=4 blueChnl=4
|
||||
page id=0 file="Inter-SemiBold.png"
|
||||
chars count=121
|
||||
char id=32 x=6 y=6 width=41 height=200 xoffset=0 yoffset=0 xadvance=41 page=0 chnl=15
|
||||
char id=33 x=59 y=6 width=27 height=123 xoffset=12 yoffset=39 xadvance=51 page=0 chnl=15
|
||||
char id=34 x=98 y=6 width=47 height=46 xoffset=9 yoffset=39 xadvance=65 page=0 chnl=15
|
||||
char id=35 x=157 y=6 width=103 height=121 xoffset=2 yoffset=39 xadvance=106 page=0 chnl=15
|
||||
char id=36 x=272 y=6 width=93 height=152 xoffset=7 yoffset=24 xadvance=107 page=0 chnl=15
|
||||
char id=37 x=377 y=6 width=113 height=128 xoffset=13 yoffset=36 xadvance=139 page=0 chnl=15
|
||||
char id=38 x=502 y=6 width=102 height=124 xoffset=7 yoffset=38 xadvance=109 page=0 chnl=15
|
||||
char id=39 x=616 y=6 width=17 height=46 xoffset=9 yoffset=39 xadvance=35 page=0 chnl=15
|
||||
char id=40 x=645 y=6 width=44 height=150 xoffset=16 yoffset=36 xadvance=65 page=0 chnl=15
|
||||
char id=41 x=701 y=6 width=44 height=150 xoffset=5 yoffset=36 xadvance=65 page=0 chnl=15
|
||||
char id=42 x=757 y=6 width=67 height=69 xoffset=11 yoffset=39 xadvance=89 page=0 chnl=15
|
||||
char id=43 x=836 y=6 width=83 height=82 xoffset=14 yoffset=71 xadvance=111 page=0 chnl=15
|
||||
char id=44 x=931 y=6 width=27 height=47 xoffset=9 yoffset=143 xadvance=48 page=0 chnl=15
|
||||
char id=45 x=970 y=6 width=55 height=18 xoffset=11 yoffset=101 xadvance=76 page=0 chnl=15
|
||||
char id=46 x=1037 y=6 width=26 height=27 xoffset=11 yoffset=135 xadvance=48 page=0 chnl=15
|
||||
char id=47 x=1075 y=6 width=58 height=145 xoffset=2 yoffset=34 xadvance=62 page=0 chnl=15
|
||||
char id=48 x=1145 y=6 width=94 height=125 xoffset=8 yoffset=38 xadvance=110 page=0 chnl=15
|
||||
char id=49 x=1251 y=6 width=52 height=121 xoffset=8 yoffset=39 xadvance=79 page=0 chnl=15
|
||||
char id=50 x=1315 y=6 width=85 height=122 xoffset=9 yoffset=38 xadvance=102 page=0 chnl=15
|
||||
char id=51 x=1412 y=6 width=90 height=124 xoffset=9 yoffset=38 xadvance=107 page=0 chnl=15
|
||||
char id=52 x=1514 y=6 width=94 height=121 xoffset=8 yoffset=39 xadvance=110 page=0 chnl=15
|
||||
char id=53 x=1620 y=6 width=86 height=123 xoffset=10 yoffset=39 xadvance=104 page=0 chnl=15
|
||||
char id=54 x=1718 y=6 width=91 height=125 xoffset=8 yoffset=37 xadvance=107 page=0 chnl=15
|
||||
char id=55 x=1821 y=6 width=83 height=121 xoffset=6 yoffset=39 xadvance=97 page=0 chnl=15
|
||||
char id=56 x=1916 y=6 width=91 height=124 xoffset=8 yoffset=38 xadvance=106 page=0 chnl=15
|
||||
char id=57 x=6 y=218 width=90 height=125 xoffset=9 yoffset=37 xadvance=107 page=0 chnl=15
|
||||
char id=58 x=108 y=218 width=26 height=91 xoffset=11 yoffset=71 xadvance=48 page=0 chnl=15
|
||||
char id=59 x=146 y=218 width=31 height=119 xoffset=9 yoffset=71 xadvance=48 page=0 chnl=15
|
||||
char id=60 x=189 y=218 width=81 height=92 xoffset=15 yoffset=66 xadvance=111 page=0 chnl=15
|
||||
char id=61 x=282 y=218 width=79 height=57 xoffset=16 yoffset=84 xadvance=111 page=0 chnl=15
|
||||
char id=62 x=373 y=218 width=81 height=92 xoffset=15 yoffset=66 xadvance=111 page=0 chnl=15
|
||||
char id=63 x=466 y=218 width=77 height=124 xoffset=6 yoffset=38 xadvance=89 page=0 chnl=15
|
||||
char id=64 x=555 y=218 width=149 height=153 xoffset=9 yoffset=41 xadvance=165 page=0 chnl=15
|
||||
char id=65 x=716 y=218 width=112 height=121 xoffset=4 yoffset=39 xadvance=119 page=0 chnl=15
|
||||
char id=66 x=840 y=218 width=91 height=121 xoffset=11 yoffset=39 xadvance=108 page=0 chnl=15
|
||||
char id=67 x=943 y=218 width=107 height=124 xoffset=8 yoffset=38 xadvance=122 page=0 chnl=15
|
||||
char id=68 x=1062 y=218 width=100 height=121 xoffset=11 yoffset=39 xadvance=119 page=0 chnl=15
|
||||
char id=69 x=1174 y=218 width=80 height=121 xoffset=11 yoffset=39 xadvance=100 page=0 chnl=15
|
||||
char id=70 x=1266 y=218 width=78 height=121 xoffset=11 yoffset=39 xadvance=96 page=0 chnl=15
|
||||
char id=71 x=1356 y=218 width=108 height=124 xoffset=8 yoffset=38 xadvance=124 page=0 chnl=15
|
||||
char id=72 x=1476 y=218 width=101 height=121 xoffset=11 yoffset=39 xadvance=122 page=0 chnl=15
|
||||
char id=73 x=1589 y=218 width=23 height=121 xoffset=11 yoffset=39 xadvance=45 page=0 chnl=15
|
||||
char id=74 x=1624 y=218 width=77 height=123 xoffset=4 yoffset=39 xadvance=92 page=0 chnl=15
|
||||
char id=75 x=1713 y=218 width=98 height=121 xoffset=11 yoffset=39 xadvance=111 page=0 chnl=15
|
||||
char id=76 x=1823 y=218 width=76 height=121 xoffset=11 yoffset=39 xadvance=93 page=0 chnl=15
|
||||
char id=77 x=6 y=430 width=127 height=121 xoffset=11 yoffset=39 xadvance=149 page=0 chnl=15
|
||||
char id=78 x=145 y=430 width=100 height=121 xoffset=11 yoffset=39 xadvance=122 page=0 chnl=15
|
||||
char id=79 x=257 y=430 width=112 height=124 xoffset=8 yoffset=38 xadvance=128 page=0 chnl=15
|
||||
char id=80 x=381 y=430 width=89 height=121 xoffset=11 yoffset=39 xadvance=106 page=0 chnl=15
|
||||
char id=81 x=482 y=430 width=112 height=133 xoffset=8 yoffset=38 xadvance=128 page=0 chnl=15
|
||||
char id=82 x=606 y=430 width=93 height=121 xoffset=11 yoffset=39 xadvance=107 page=0 chnl=15
|
||||
char id=83 x=711 y=430 width=93 height=124 xoffset=7 yoffset=38 xadvance=107 page=0 chnl=15
|
||||
char id=84 x=816 y=430 width=97 height=121 xoffset=6 yoffset=39 xadvance=109 page=0 chnl=15
|
||||
char id=85 x=925 y=430 width=99 height=123 xoffset=11 yoffset=39 xadvance=121 page=0 chnl=15
|
||||
char id=86 x=1036 y=430 width=112 height=121 xoffset=4 yoffset=39 xadvance=119 page=0 chnl=15
|
||||
char id=87 x=1160 y=430 width=161 height=121 xoffset=3 yoffset=39 xadvance=166 page=0 chnl=15
|
||||
char id=88 x=1333 y=430 width=106 height=121 xoffset=4 yoffset=39 xadvance=114 page=0 chnl=15
|
||||
char id=89 x=1451 y=430 width=110 height=121 xoffset=3 yoffset=39 xadvance=116 page=0 chnl=15
|
||||
char id=90 x=1573 y=430 width=90 height=121 xoffset=9 yoffset=39 xadvance=107 page=0 chnl=15
|
||||
char id=91 x=1675 y=430 width=43 height=150 xoffset=18 yoffset=36 xadvance=65 page=0 chnl=15
|
||||
char id=92 x=1730 y=430 width=58 height=145 xoffset=2 yoffset=34 xadvance=62 page=0 chnl=15
|
||||
char id=93 x=1800 y=430 width=43 height=150 xoffset=4 yoffset=36 xadvance=65 page=0 chnl=15
|
||||
char id=94 x=1855 y=430 width=69 height=53 xoffset=5 yoffset=42 xadvance=79 page=0 chnl=15
|
||||
char id=95 x=1936 y=430 width=79 height=17 xoffset=-1 yoffset=160 xadvance=77 page=0 chnl=15
|
||||
char id=96 x=6 y=642 width=35 height=28 xoffset=24 yoffset=31 xadvance=82 page=0 chnl=15
|
||||
char id=97 x=53 y=642 width=79 height=94 xoffset=6 yoffset=68 xadvance=94 page=0 chnl=15
|
||||
char id=98 x=144 y=642 width=86 height=123 xoffset=11 yoffset=39 xadvance=104 page=0 chnl=15
|
||||
char id=99 x=242 y=642 width=82 height=94 xoffset=7 yoffset=68 xadvance=95 page=0 chnl=15
|
||||
char id=100 x=336 y=642 width=86 height=123 xoffset=7 yoffset=39 xadvance=104 page=0 chnl=15
|
||||
char id=101 x=434 y=642 width=84 height=94 xoffset=7 yoffset=68 xadvance=97 page=0 chnl=15
|
||||
char id=102 x=530 y=642 width=58 height=126 xoffset=3 yoffset=34 xadvance=62 page=0 chnl=15
|
||||
char id=103 x=600 y=642 width=86 height=128 xoffset=7 yoffset=68 xadvance=103 page=0 chnl=15
|
||||
char id=104 x=698 y=642 width=81 height=121 xoffset=10 yoffset=39 xadvance=101 page=0 chnl=15
|
||||
char id=105 x=791 y=642 width=25 height=126 xoffset=9 yoffset=34 xadvance=43 page=0 chnl=15
|
||||
char id=106 x=828 y=642 width=38 height=160 xoffset=-4 yoffset=34 xadvance=43 page=0 chnl=15
|
||||
char id=107 x=878 y=642 width=83 height=121 xoffset=10 yoffset=39 xadvance=94 page=0 chnl=15
|
||||
char id=108 x=973 y=642 width=23 height=121 xoffset=10 yoffset=39 xadvance=43 page=0 chnl=15
|
||||
char id=109 x=1008 y=642 width=128 height=92 xoffset=10 yoffset=68 xadvance=148 page=0 chnl=15
|
||||
char id=110 x=1148 y=642 width=81 height=92 xoffset=10 yoffset=68 xadvance=100 page=0 chnl=15
|
||||
char id=111 x=1241 y=642 width=87 height=94 xoffset=7 yoffset=68 xadvance=100 page=0 chnl=15
|
||||
char id=112 x=1340 y=642 width=87 height=126 xoffset=10 yoffset=68 xadvance=103 page=0 chnl=15
|
||||
char id=113 x=1439 y=642 width=86 height=126 xoffset=7 yoffset=68 xadvance=103 page=0 chnl=15
|
||||
char id=114 x=1537 y=642 width=53 height=92 xoffset=10 yoffset=68 xadvance=65 page=0 chnl=15
|
||||
char id=115 x=1602 y=642 width=79 height=94 xoffset=6 yoffset=68 xadvance=90 page=0 chnl=15
|
||||
char id=116 x=1693 y=642 width=54 height=114 xoffset=4 yoffset=48 xadvance=62 page=0 chnl=15
|
||||
char id=117 x=1759 y=642 width=80 height=93 xoffset=10 yoffset=69 xadvance=100 page=0 chnl=15
|
||||
char id=118 x=1851 y=642 width=89 height=91 xoffset=3 yoffset=69 xadvance=95 page=0 chnl=15
|
||||
char id=119 x=6 y=854 width=131 height=91 xoffset=4 yoffset=69 xadvance=138 page=0 chnl=15
|
||||
char id=120 x=149 y=854 width=83 height=91 xoffset=5 yoffset=69 xadvance=92 page=0 chnl=15
|
||||
char id=121 x=244 y=854 width=89 height=125 xoffset=3 yoffset=69 xadvance=95 page=0 chnl=15
|
||||
char id=122 x=345 y=854 width=75 height=91 xoffset=9 yoffset=69 xadvance=92 page=0 chnl=15
|
||||
char id=123 x=432 y=854 width=56 height=150 xoffset=5 yoffset=36 xadvance=65 page=0 chnl=15
|
||||
char id=124 x=500 y=854 width=21 height=198 xoffset=19 yoffset=1 xadvance=59 page=0 chnl=15
|
||||
char id=125 x=533 y=854 width=56 height=150 xoffset=4 yoffset=36 xadvance=65 page=0 chnl=15
|
||||
char id=126 x=601 y=854 width=87 height=36 xoffset=12 yoffset=94 xadvance=111 page=0 chnl=15
|
||||
char id=163 x=700 y=854 width=88 height=122 xoffset=10 yoffset=38 xadvance=108 page=0 chnl=15
|
||||
char id=165 x=800 y=854 width=89 height=121 xoffset=2 yoffset=39 xadvance=93 page=0 chnl=15
|
||||
char id=167 x=901 y=854 width=80 height=147 xoffset=8 yoffset=38 xadvance=95 page=0 chnl=15
|
||||
char id=176 x=993 y=854 width=58 height=58 xoffset=9 yoffset=38 xadvance=75 page=0 chnl=15
|
||||
char id=178 x=1063 y=854 width=58 height=74 xoffset=6 yoffset=26 xadvance=69 page=0 chnl=15
|
||||
char id=215 x=1133 y=854 width=83 height=83 xoffset=14 yoffset=71 xadvance=111 page=0 chnl=15
|
||||
char id=231 x=1228 y=854 width=82 height=130 xoffset=7 yoffset=68 xadvance=95 page=0 chnl=15
|
||||
char id=234 x=1322 y=854 width=84 height=130 xoffset=7 yoffset=32 xadvance=97 page=0 chnl=15
|
||||
char id=241 x=1418 y=854 width=81 height=125 xoffset=10 yoffset=35 xadvance=100 page=0 chnl=15
|
||||
char id=252 x=1511 y=854 width=80 height=128 xoffset=10 yoffset=34 xadvance=100 page=0 chnl=15
|
||||
char id=8209 x=1603 y=854 width=55 height=18 xoffset=9 yoffset=101 xadvance=70 page=0 chnl=15
|
||||
char id=8211 x=1670 y=854 width=83 height=18 xoffset=0 yoffset=101 xadvance=82 page=0 chnl=15
|
||||
char id=8226 x=1765 y=854 width=52 height=52 xoffset=15 yoffset=86 xadvance=82 page=0 chnl=15
|
||||
char id=8364 x=1829 y=854 width=99 height=124 xoffset=4 yoffset=38 xadvance=112 page=0 chnl=15
|
||||
char id=8627 x=6 y=1066 width=134 height=107 xoffset=16 yoffset=39 xadvance=165 page=0 chnl=15
|
||||
char id=8679 x=152 y=1066 width=152 height=130 xoffset=13 yoffset=30 xadvance=178 page=0 chnl=15
|
||||
char id=9003 x=316 y=1066 width=173 height=121 xoffset=13 yoffset=39 xadvance=200 page=0 chnl=15
|
||||
char id=9251 x=501 y=1066 width=102 height=39 xoffset=11 yoffset=148 xadvance=123 page=0 chnl=15
|
||||
char id=9654 x=615 y=1066 width=131 height=152 xoffset=16 yoffset=24 xadvance=155 page=0 chnl=15
|
||||
char id=9664 x=758 y=1066 width=131 height=152 xoffset=8 yoffset=24 xadvance=155 page=0 chnl=15
|
||||
char id=9675 x=901 y=1066 width=125 height=125 xoffset=13 yoffset=37 xadvance=151 page=0 chnl=15
|
||||
char id=9679 x=1038 y=1066 width=125 height=125 xoffset=13 yoffset=37 xadvance=151 page=0 chnl=15
|
||||
char id=9881 x=1175 y=1066 width=0 height=0 xoffset=0 yoffset=0 xadvance=0 page=0 chnl=15
|
||||
char id=10003 x=1187 y=1066 width=119 height=91 xoffset=16 yoffset=60 xadvance=145 page=0 chnl=15
|
||||
char id=10004 x=1318 y=1066 width=0 height=0 xoffset=0 yoffset=0 xadvance=0 page=0 chnl=15
|
||||
char id=10005 x=1330 y=1066 width=0 height=0 xoffset=0 yoffset=0 xadvance=0 page=0 chnl=15
|
||||
|
After Width: | Height: | Size: 218 KiB |
@@ -0,0 +1,125 @@
|
||||
info face="Inter-Thin" size=-200 bold=0 italic=0 charset="" unicode=1 stretchH=100 smooth=0 aa=1 padding=0,0,0,0 spacing=0,0 outline=0
|
||||
common lineHeight=200 base=200 scaleW=2048 scaleH=2048 pages=1 packed=0 alphaChnl=0 redChnl=4 greenChnl=4 blueChnl=4
|
||||
page id=0 file="Inter-Thin.png"
|
||||
chars count=121
|
||||
char id=32 x=6 y=6 width=46 height=200 xoffset=0 yoffset=0 xadvance=46 page=0 chnl=15
|
||||
char id=33 x=64 y=6 width=9 height=122 xoffset=17 yoffset=39 xadvance=43 page=0 chnl=15
|
||||
char id=34 x=85 y=6 width=27 height=39 xoffset=15 yoffset=39 xadvance=56 page=0 chnl=15
|
||||
char id=35 x=124 y=6 width=98 height=121 xoffset=3 yoffset=39 xadvance=104 page=0 chnl=15
|
||||
char id=36 x=234 y=6 width=77 height=152 xoffset=12 yoffset=24 xadvance=101 page=0 chnl=15
|
||||
char id=37 x=323 y=6 width=102 height=124 xoffset=13 yoffset=38 xadvance=127 page=0 chnl=15
|
||||
char id=38 x=437 y=6 width=84 height=124 xoffset=10 yoffset=38 xadvance=96 page=0 chnl=15
|
||||
char id=39 x=533 y=6 width=4 height=39 xoffset=15 yoffset=39 xadvance=33 page=0 chnl=15
|
||||
char id=40 x=549 y=6 width=22 height=151 xoffset=19 yoffset=34 xadvance=52 page=0 chnl=15
|
||||
char id=41 x=583 y=6 width=22 height=151 xoffset=11 yoffset=34 xadvance=52 page=0 chnl=15
|
||||
char id=42 x=617 y=6 width=61 height=69 xoffset=11 yoffset=39 xadvance=82 page=0 chnl=15
|
||||
char id=43 x=690 y=6 width=69 height=69 xoffset=18 yoffset=79 xadvance=105 page=0 chnl=15
|
||||
char id=44 x=771 y=6 width=13 height=47 xoffset=14 yoffset=143 xadvance=42 page=0 chnl=15
|
||||
char id=45 x=796 y=6 width=46 height=5 xoffset=13 yoffset=106 xadvance=72 page=0 chnl=15
|
||||
char id=46 x=854 y=6 width=10 height=9 xoffset=16 yoffset=152 xadvance=41 page=0 chnl=15
|
||||
char id=47 x=876 y=6 width=44 height=145 xoffset=5 yoffset=34 xadvance=53 page=0 chnl=15
|
||||
char id=48 x=932 y=6 width=75 height=124 xoffset=11 yoffset=38 xadvance=97 page=0 chnl=15
|
||||
char id=49 x=1019 y=6 width=32 height=121 xoffset=11 yoffset=39 xadvance=67 page=0 chnl=15
|
||||
char id=50 x=1063 y=6 width=73 height=122 xoffset=15 yoffset=38 xadvance=102 page=0 chnl=15
|
||||
char id=51 x=1148 y=6 width=72 height=124 xoffset=15 yoffset=38 xadvance=101 page=0 chnl=15
|
||||
char id=52 x=1232 y=6 width=75 height=121 xoffset=12 yoffset=39 xadvance=98 page=0 chnl=15
|
||||
char id=53 x=1319 y=6 width=69 height=123 xoffset=13 yoffset=39 xadvance=94 page=0 chnl=15
|
||||
char id=54 x=1400 y=6 width=70 height=124 xoffset=13 yoffset=38 xadvance=96 page=0 chnl=15
|
||||
char id=55 x=1482 y=6 width=71 height=121 xoffset=9 yoffset=39 xadvance=90 page=0 chnl=15
|
||||
char id=56 x=1565 y=6 width=76 height=124 xoffset=12 yoffset=38 xadvance=99 page=0 chnl=15
|
||||
char id=57 x=1653 y=6 width=70 height=125 xoffset=13 yoffset=37 xadvance=96 page=0 chnl=15
|
||||
char id=58 x=1735 y=6 width=10 height=71 xoffset=16 yoffset=81 xadvance=41 page=0 chnl=15
|
||||
char id=59 x=1757 y=6 width=14 height=109 xoffset=14 yoffset=81 xadvance=42 page=0 chnl=15
|
||||
char id=60 x=1783 y=6 width=65 height=88 xoffset=20 yoffset=69 xadvance=105 page=0 chnl=15
|
||||
char id=61 x=1860 y=6 width=57 height=36 xoffset=24 yoffset=95 xadvance=105 page=0 chnl=15
|
||||
char id=62 x=1929 y=6 width=65 height=88 xoffset=20 yoffset=69 xadvance=105 page=0 chnl=15
|
||||
char id=63 x=6 y=218 width=63 height=123 xoffset=7 yoffset=38 xadvance=79 page=0 chnl=15
|
||||
char id=64 x=81 y=218 width=139 height=148 xoffset=8 yoffset=44 xadvance=154 page=0 chnl=15
|
||||
char id=65 x=232 y=218 width=92 height=121 xoffset=5 yoffset=39 xadvance=101 page=0 chnl=15
|
||||
char id=66 x=336 y=218 width=76 height=121 xoffset=18 yoffset=39 xadvance=105 page=0 chnl=15
|
||||
char id=67 x=424 y=218 width=97 height=124 xoffset=11 yoffset=38 xadvance=119 page=0 chnl=15
|
||||
char id=68 x=533 y=218 width=89 height=121 xoffset=18 yoffset=39 xadvance=118 page=0 chnl=15
|
||||
char id=69 x=634 y=218 width=67 height=121 xoffset=18 yoffset=39 xadvance=96 page=0 chnl=15
|
||||
char id=70 x=713 y=218 width=66 height=121 xoffset=18 yoffset=39 xadvance=96 page=0 chnl=15
|
||||
char id=71 x=791 y=218 width=98 height=124 xoffset=11 yoffset=38 xadvance=122 page=0 chnl=15
|
||||
char id=72 x=901 y=218 width=85 height=121 xoffset=18 yoffset=39 xadvance=121 page=0 chnl=15
|
||||
char id=73 x=998 y=218 width=5 height=121 xoffset=18 yoffset=39 xadvance=41 page=0 chnl=15
|
||||
char id=74 x=1015 y=218 width=60 height=123 xoffset=8 yoffset=39 xadvance=85 page=0 chnl=15
|
||||
char id=75 x=1087 y=218 width=79 height=121 xoffset=18 yoffset=39 xadvance=101 page=0 chnl=15
|
||||
char id=76 x=1178 y=218 width=65 height=121 xoffset=18 yoffset=39 xadvance=92 page=0 chnl=15
|
||||
char id=77 x=1255 y=218 width=106 height=121 xoffset=18 yoffset=39 xadvance=142 page=0 chnl=15
|
||||
char id=78 x=1373 y=218 width=91 height=121 xoffset=18 yoffset=39 xadvance=127 page=0 chnl=15
|
||||
char id=79 x=1476 y=218 width=101 height=124 xoffset=11 yoffset=38 xadvance=123 page=0 chnl=15
|
||||
char id=80 x=1589 y=218 width=71 height=121 xoffset=18 yoffset=39 xadvance=100 page=0 chnl=15
|
||||
char id=81 x=1672 y=218 width=101 height=134 xoffset=11 yoffset=38 xadvance=123 page=0 chnl=15
|
||||
char id=82 x=1785 y=218 width=77 height=121 xoffset=18 yoffset=39 xadvance=101 page=0 chnl=15
|
||||
char id=83 x=1874 y=218 width=77 height=125 xoffset=12 yoffset=37 xadvance=101 page=0 chnl=15
|
||||
char id=84 x=6 y=430 width=82 height=121 xoffset=10 yoffset=39 xadvance=101 page=0 chnl=15
|
||||
char id=85 x=100 y=430 width=88 height=123 xoffset=18 yoffset=39 xadvance=123 page=0 chnl=15
|
||||
char id=86 x=200 y=430 width=92 height=121 xoffset=5 yoffset=39 xadvance=101 page=0 chnl=15
|
||||
char id=87 x=304 y=430 width=139 height=121 xoffset=4 yoffset=39 xadvance=147 page=0 chnl=15
|
||||
char id=88 x=455 y=430 width=87 height=121 xoffset=5 yoffset=39 xadvance=96 page=0 chnl=15
|
||||
char id=89 x=554 y=430 width=91 height=121 xoffset=5 yoffset=39 xadvance=101 page=0 chnl=15
|
||||
char id=90 x=657 y=430 width=77 height=121 xoffset=12 yoffset=39 xadvance=100 page=0 chnl=15
|
||||
char id=91 x=746 y=430 width=22 height=151 xoffset=20 yoffset=34 xadvance=52 page=0 chnl=15
|
||||
char id=92 x=780 y=430 width=44 height=145 xoffset=5 yoffset=34 xadvance=53 page=0 chnl=15
|
||||
char id=93 x=836 y=430 width=22 height=151 xoffset=10 yoffset=34 xadvance=52 page=0 chnl=15
|
||||
char id=94 x=870 y=430 width=52 height=52 xoffset=7 yoffset=43 xadvance=66 page=0 chnl=15
|
||||
char id=95 x=934 y=430 width=72 height=4 xoffset=-1 yoffset=160 xadvance=70 page=0 chnl=15
|
||||
char id=96 x=1018 y=430 width=22 height=26 xoffset=29 yoffset=31 xadvance=82 page=0 chnl=15
|
||||
char id=97 x=1052 y=430 width=63 height=94 xoffset=11 yoffset=68 xadvance=89 page=0 chnl=15
|
||||
char id=98 x=1127 y=430 width=71 height=123 xoffset=18 yoffset=39 xadvance=98 page=0 chnl=15
|
||||
char id=99 x=1210 y=430 width=71 height=94 xoffset=10 yoffset=68 xadvance=91 page=0 chnl=15
|
||||
char id=100 x=1293 y=430 width=71 height=123 xoffset=10 yoffset=39 xadvance=98 page=0 chnl=15
|
||||
char id=101 x=1376 y=430 width=75 height=94 xoffset=10 yoffset=68 xadvance=95 page=0 chnl=15
|
||||
char id=102 x=1463 y=430 width=41 height=124 xoffset=11 yoffset=36 xadvance=54 page=0 chnl=15
|
||||
char id=103 x=1516 y=430 width=71 height=128 xoffset=10 yoffset=68 xadvance=96 page=0 chnl=15
|
||||
char id=104 x=1599 y=430 width=63 height=121 xoffset=15 yoffset=39 xadvance=93 page=0 chnl=15
|
||||
char id=105 x=1674 y=430 width=9 height=115 xoffset=14 yoffset=45 xadvance=36 page=0 chnl=15
|
||||
char id=106 x=1695 y=430 width=26 height=149 xoffset=-3 yoffset=45 xadvance=36 page=0 chnl=15
|
||||
char id=107 x=1733 y=430 width=60 height=121 xoffset=15 yoffset=39 xadvance=80 page=0 chnl=15
|
||||
char id=108 x=1805 y=430 width=5 height=121 xoffset=15 yoffset=39 xadvance=35 page=0 chnl=15
|
||||
char id=109 x=1822 y=430 width=108 height=92 xoffset=15 yoffset=68 xadvance=138 page=0 chnl=15
|
||||
char id=110 x=1942 y=430 width=63 height=92 xoffset=15 yoffset=68 xadvance=93 page=0 chnl=15
|
||||
char id=111 x=6 y=642 width=76 height=94 xoffset=10 yoffset=68 xadvance=95 page=0 chnl=15
|
||||
char id=112 x=94 y=642 width=70 height=126 xoffset=15 yoffset=68 xadvance=95 page=0 chnl=15
|
||||
char id=113 x=176 y=642 width=71 height=126 xoffset=10 yoffset=68 xadvance=95 page=0 chnl=15
|
||||
char id=114 x=259 y=642 width=35 height=92 xoffset=15 yoffset=68 xadvance=53 page=0 chnl=15
|
||||
char id=115 x=306 y=642 width=60 height=94 xoffset=11 yoffset=68 xadvance=81 page=0 chnl=15
|
||||
char id=116 x=378 y=642 width=42 height=114 xoffset=8 yoffset=48 xadvance=57 page=0 chnl=15
|
||||
char id=117 x=432 y=642 width=63 height=93 xoffset=15 yoffset=69 xadvance=93 page=0 chnl=15
|
||||
char id=118 x=507 y=642 width=70 height=91 xoffset=8 yoffset=69 xadvance=86 page=0 chnl=15
|
||||
char id=119 x=589 y=642 width=115 height=91 xoffset=8 yoffset=69 xadvance=130 page=0 chnl=15
|
||||
char id=120 x=716 y=642 width=63 height=91 xoffset=10 yoffset=69 xadvance=82 page=0 chnl=15
|
||||
char id=121 x=791 y=642 width=70 height=126 xoffset=8 yoffset=69 xadvance=86 page=0 chnl=15
|
||||
char id=122 x=873 y=642 width=62 height=91 xoffset=12 yoffset=69 xadvance=84 page=0 chnl=15
|
||||
char id=123 x=947 y=642 width=40 height=151 xoffset=10 yoffset=34 xadvance=52 page=0 chnl=15
|
||||
char id=124 x=999 y=642 width=5 height=198 xoffset=20 yoffset=1 xadvance=45 page=0 chnl=15
|
||||
char id=125 x=1016 y=642 width=40 height=151 xoffset=2 yoffset=34 xadvance=52 page=0 chnl=15
|
||||
char id=126 x=1068 y=642 width=75 height=18 xoffset=15 yoffset=104 xadvance=105 page=0 chnl=15
|
||||
char id=163 x=1155 y=642 width=77 height=122 xoffset=13 yoffset=38 xadvance=104 page=0 chnl=15
|
||||
char id=165 x=1244 y=642 width=80 height=121 xoffset=3 yoffset=39 xadvance=86 page=0 chnl=15
|
||||
char id=167 x=1336 y=642 width=67 height=147 xoffset=10 yoffset=38 xadvance=87 page=0 chnl=15
|
||||
char id=176 x=1415 y=642 width=49 height=49 xoffset=13 yoffset=38 xadvance=75 page=0 chnl=15
|
||||
char id=178 x=1476 y=642 width=50 height=74 xoffset=9 yoffset=26 xadvance=68 page=0 chnl=15
|
||||
char id=215 x=1538 y=642 width=70 height=70 xoffset=18 yoffset=78 xadvance=105 page=0 chnl=15
|
||||
char id=231 x=1620 y=642 width=71 height=123 xoffset=10 yoffset=68 xadvance=91 page=0 chnl=15
|
||||
char id=234 x=1703 y=642 width=75 height=125 xoffset=10 yoffset=37 xadvance=95 page=0 chnl=15
|
||||
char id=241 x=1790 y=642 width=63 height=119 xoffset=15 yoffset=41 xadvance=93 page=0 chnl=15
|
||||
char id=252 x=1865 y=642 width=63 height=117 xoffset=15 yoffset=45 xadvance=93 page=0 chnl=15
|
||||
char id=8209 x=1940 y=642 width=46 height=5 xoffset=-5 yoffset=106 xadvance=41 page=0 chnl=15
|
||||
char id=8211 x=6 y=854 width=83 height=5 xoffset=0 yoffset=106 xadvance=82 page=0 chnl=15
|
||||
char id=8226 x=101 y=854 width=31 height=31 xoffset=31 yoffset=97 xadvance=92 page=0 chnl=15
|
||||
char id=8364 x=144 y=854 width=95 height=124 xoffset=5 yoffset=38 xadvance=112 page=0 chnl=15
|
||||
char id=8627 x=251 y=854 width=122 height=98 xoffset=16 yoffset=39 xadvance=153 page=0 chnl=15
|
||||
char id=8679 x=385 y=854 width=113 height=130 xoffset=15 yoffset=30 xadvance=142 page=0 chnl=15
|
||||
char id=9003 x=510 y=854 width=173 height=121 xoffset=13 yoffset=39 xadvance=200 page=0 chnl=15
|
||||
char id=9251 x=695 y=854 width=87 height=36 xoffset=13 yoffset=148 xadvance=112 page=0 chnl=15
|
||||
char id=9654 x=794 y=854 width=125 height=152 xoffset=13 yoffset=24 xadvance=144 page=0 chnl=15
|
||||
char id=9664 x=931 y=854 width=125 height=152 xoffset=6 yoffset=24 xadvance=144 page=0 chnl=15
|
||||
char id=9675 x=1068 y=854 width=125 height=125 xoffset=13 yoffset=37 xadvance=151 page=0 chnl=15
|
||||
char id=9679 x=1205 y=854 width=125 height=125 xoffset=13 yoffset=37 xadvance=151 page=0 chnl=15
|
||||
char id=9881 x=1342 y=854 width=0 height=0 xoffset=0 yoffset=0 xadvance=0 page=0 chnl=15
|
||||
char id=10003 x=1354 y=854 width=108 height=73 xoffset=19 yoffset=66 xadvance=145 page=0 chnl=15
|
||||
char id=10004 x=1474 y=854 width=0 height=0 xoffset=0 yoffset=0 xadvance=0 page=0 chnl=15
|
||||
char id=10005 x=1486 y=854 width=0 height=0 xoffset=0 yoffset=0 xadvance=0 page=0 chnl=15
|
||||
|
After Width: | Height: | Size: 219 KiB |
@@ -0,0 +1,125 @@
|
||||
info face="JetBrainsMono-Medium" size=-200 bold=0 italic=0 charset="" unicode=1 stretchH=100 smooth=0 aa=1 padding=0,0,0,0 spacing=0,0 outline=0
|
||||
common lineHeight=200 base=200 scaleW=2048 scaleH=2048 pages=1 packed=0 alphaChnl=0 redChnl=4 greenChnl=4 blueChnl=4
|
||||
page id=0 file="JetBrainsMono-Medium.png"
|
||||
chars count=121
|
||||
char id=32 x=6 y=6 width=90 height=200 xoffset=0 yoffset=0 xadvance=90 page=0 chnl=15
|
||||
char id=33 x=108 y=6 width=25 height=112 xoffset=33 yoffset=43 xadvance=90 page=0 chnl=15
|
||||
char id=34 x=145 y=6 width=49 height=46 xoffset=21 yoffset=43 xadvance=90 page=0 chnl=15
|
||||
char id=35 x=206 y=6 width=81 height=111 xoffset=5 yoffset=43 xadvance=90 page=0 chnl=15
|
||||
char id=36 x=299 y=6 width=71 height=154 xoffset=10 yoffset=22 xadvance=90 page=0 chnl=15
|
||||
char id=37 x=382 y=6 width=89 height=113 xoffset=1 yoffset=42 xadvance=90 page=0 chnl=15
|
||||
char id=38 x=483 y=6 width=80 height=115 xoffset=7 yoffset=41 xadvance=90 page=0 chnl=15
|
||||
char id=39 x=575 y=6 width=17 height=46 xoffset=36 yoffset=43 xadvance=90 page=0 chnl=15
|
||||
char id=40 x=604 y=6 width=47 height=147 xoffset=27 yoffset=26 xadvance=90 page=0 chnl=15
|
||||
char id=41 x=663 y=6 width=47 height=147 xoffset=17 yoffset=26 xadvance=90 page=0 chnl=15
|
||||
char id=42 x=722 y=6 width=81 height=80 xoffset=5 yoffset=61 xadvance=90 page=0 chnl=15
|
||||
char id=43 x=815 y=6 width=73 height=72 xoffset=9 yoffset=68 xadvance=90 page=0 chnl=15
|
||||
char id=44 x=900 y=6 width=34 height=48 xoffset=24 yoffset=131 xadvance=90 page=0 chnl=15
|
||||
char id=45 x=946 y=6 width=49 height=14 xoffset=21 yoffset=97 xadvance=90 page=0 chnl=15
|
||||
char id=46 x=1007 y=6 width=27 height=27 xoffset=32 yoffset=129 xadvance=90 page=0 chnl=15
|
||||
char id=47 x=1046 y=6 width=71 height=143 xoffset=10 yoffset=28 xadvance=90 page=0 chnl=15
|
||||
char id=48 x=1129 y=6 width=69 height=115 xoffset=11 yoffset=41 xadvance=90 page=0 chnl=15
|
||||
char id=49 x=1210 y=6 width=70 height=111 xoffset=13 yoffset=43 xadvance=90 page=0 chnl=15
|
||||
char id=50 x=1292 y=6 width=70 height=113 xoffset=10 yoffset=41 xadvance=90 page=0 chnl=15
|
||||
char id=51 x=1374 y=6 width=69 height=113 xoffset=10 yoffset=43 xadvance=90 page=0 chnl=15
|
||||
char id=52 x=1455 y=6 width=65 height=111 xoffset=10 yoffset=43 xadvance=90 page=0 chnl=15
|
||||
char id=53 x=1532 y=6 width=67 height=113 xoffset=12 yoffset=43 xadvance=90 page=0 chnl=15
|
||||
char id=54 x=1611 y=6 width=73 height=113 xoffset=9 yoffset=43 xadvance=90 page=0 chnl=15
|
||||
char id=55 x=1696 y=6 width=73 height=111 xoffset=11 yoffset=43 xadvance=90 page=0 chnl=15
|
||||
char id=56 x=1781 y=6 width=73 height=115 xoffset=9 yoffset=41 xadvance=90 page=0 chnl=15
|
||||
char id=57 x=1866 y=6 width=73 height=113 xoffset=9 yoffset=41 xadvance=90 page=0 chnl=15
|
||||
char id=58 x=1951 y=6 width=27 height=87 xoffset=32 yoffset=69 xadvance=90 page=0 chnl=15
|
||||
char id=59 x=1990 y=6 width=34 height=110 xoffset=25 yoffset=69 xadvance=90 page=0 chnl=15
|
||||
char id=60 x=6 y=218 width=67 height=82 xoffset=12 yoffset=63 xadvance=90 page=0 chnl=15
|
||||
char id=61 x=85 y=218 width=67 height=50 xoffset=12 yoffset=79 xadvance=90 page=0 chnl=15
|
||||
char id=62 x=164 y=218 width=67 height=82 xoffset=12 yoffset=63 xadvance=90 page=0 chnl=15
|
||||
char id=63 x=243 y=218 width=56 height=112 xoffset=19 yoffset=43 xadvance=90 page=0 chnl=15
|
||||
char id=64 x=311 y=218 width=80 height=141 xoffset=6 yoffset=41 xadvance=90 page=0 chnl=15
|
||||
char id=65 x=403 y=218 width=78 height=111 xoffset=6 yoffset=43 xadvance=90 page=0 chnl=15
|
||||
char id=66 x=493 y=218 width=69 height=111 xoffset=13 yoffset=43 xadvance=90 page=0 chnl=15
|
||||
char id=67 x=574 y=218 width=68 height=115 xoffset=12 yoffset=41 xadvance=90 page=0 chnl=15
|
||||
char id=68 x=654 y=218 width=66 height=111 xoffset=13 yoffset=43 xadvance=90 page=0 chnl=15
|
||||
char id=69 x=732 y=218 width=66 height=111 xoffset=14 yoffset=43 xadvance=90 page=0 chnl=15
|
||||
char id=70 x=810 y=218 width=67 height=111 xoffset=13 yoffset=43 xadvance=90 page=0 chnl=15
|
||||
char id=71 x=889 y=218 width=68 height=115 xoffset=12 yoffset=41 xadvance=90 page=0 chnl=15
|
||||
char id=72 x=969 y=218 width=65 height=111 xoffset=13 yoffset=43 xadvance=90 page=0 chnl=15
|
||||
char id=73 x=1046 y=218 width=61 height=111 xoffset=15 yoffset=43 xadvance=90 page=0 chnl=15
|
||||
char id=74 x=1119 y=218 width=70 height=113 xoffset=6 yoffset=43 xadvance=90 page=0 chnl=15
|
||||
char id=75 x=1201 y=218 width=73 height=111 xoffset=13 yoffset=43 xadvance=90 page=0 chnl=15
|
||||
char id=76 x=1286 y=218 width=65 height=111 xoffset=19 yoffset=43 xadvance=90 page=0 chnl=15
|
||||
char id=77 x=1363 y=218 width=71 height=111 xoffset=10 yoffset=43 xadvance=90 page=0 chnl=15
|
||||
char id=78 x=1446 y=218 width=66 height=111 xoffset=13 yoffset=43 xadvance=90 page=0 chnl=15
|
||||
char id=79 x=1524 y=218 width=67 height=115 xoffset=12 yoffset=41 xadvance=90 page=0 chnl=15
|
||||
char id=80 x=1603 y=218 width=71 height=111 xoffset=13 yoffset=43 xadvance=90 page=0 chnl=15
|
||||
char id=81 x=1686 y=218 width=70 height=141 xoffset=11 yoffset=41 xadvance=90 page=0 chnl=15
|
||||
char id=82 x=1768 y=218 width=71 height=111 xoffset=13 yoffset=43 xadvance=90 page=0 chnl=15
|
||||
char id=83 x=1851 y=218 width=71 height=115 xoffset=10 yoffset=41 xadvance=90 page=0 chnl=15
|
||||
char id=84 x=1934 y=218 width=75 height=111 xoffset=8 yoffset=43 xadvance=90 page=0 chnl=15
|
||||
char id=85 x=6 y=430 width=66 height=113 xoffset=13 yoffset=43 xadvance=90 page=0 chnl=15
|
||||
char id=86 x=84 y=430 width=78 height=111 xoffset=6 yoffset=43 xadvance=90 page=0 chnl=15
|
||||
char id=87 x=174 y=430 width=85 height=111 xoffset=3 yoffset=43 xadvance=90 page=0 chnl=15
|
||||
char id=88 x=271 y=430 width=81 height=111 xoffset=5 yoffset=43 xadvance=90 page=0 chnl=15
|
||||
char id=89 x=364 y=430 width=83 height=111 xoffset=4 yoffset=43 xadvance=90 page=0 chnl=15
|
||||
char id=90 x=459 y=430 width=67 height=111 xoffset=12 yoffset=43 xadvance=90 page=0 chnl=15
|
||||
char id=91 x=538 y=430 width=39 height=143 xoffset=30 yoffset=28 xadvance=90 page=0 chnl=15
|
||||
char id=92 x=589 y=430 width=71 height=143 xoffset=10 yoffset=28 xadvance=90 page=0 chnl=15
|
||||
char id=93 x=672 y=430 width=39 height=143 xoffset=22 yoffset=28 xadvance=90 page=0 chnl=15
|
||||
char id=94 x=723 y=430 width=69 height=60 xoffset=11 yoffset=43 xadvance=90 page=0 chnl=15
|
||||
char id=95 x=804 y=430 width=73 height=14 xoffset=9 yoffset=157 xadvance=90 page=0 chnl=15
|
||||
char id=96 x=889 y=430 width=34 height=22 xoffset=23 yoffset=35 xadvance=90 page=0 chnl=15
|
||||
char id=97 x=935 y=430 width=69 height=87 xoffset=9 yoffset=69 xadvance=90 page=0 chnl=15
|
||||
char id=98 x=1016 y=430 width=66 height=113 xoffset=13 yoffset=43 xadvance=90 page=0 chnl=15
|
||||
char id=99 x=1094 y=430 width=68 height=87 xoffset=12 yoffset=69 xadvance=90 page=0 chnl=15
|
||||
char id=100 x=1174 y=430 width=66 height=113 xoffset=12 yoffset=43 xadvance=90 page=0 chnl=15
|
||||
char id=101 x=1252 y=430 width=67 height=87 xoffset=12 yoffset=69 xadvance=90 page=0 chnl=15
|
||||
char id=102 x=1331 y=430 width=74 height=111 xoffset=8 yoffset=43 xadvance=90 page=0 chnl=15
|
||||
char id=103 x=1417 y=430 width=66 height=113 xoffset=12 yoffset=69 xadvance=90 page=0 chnl=15
|
||||
char id=104 x=1495 y=430 width=65 height=111 xoffset=13 yoffset=43 xadvance=90 page=0 chnl=15
|
||||
char id=105 x=1572 y=430 width=73 height=119 xoffset=12 yoffset=35 xadvance=90 page=0 chnl=15
|
||||
char id=106 x=1657 y=430 width=60 height=147 xoffset=10 yoffset=35 xadvance=90 page=0 chnl=15
|
||||
char id=107 x=1729 y=430 width=72 height=111 xoffset=13 yoffset=43 xadvance=90 page=0 chnl=15
|
||||
char id=108 x=1813 y=430 width=80 height=111 xoffset=4 yoffset=43 xadvance=90 page=0 chnl=15
|
||||
char id=109 x=1905 y=430 width=75 height=85 xoffset=8 yoffset=69 xadvance=90 page=0 chnl=15
|
||||
char id=110 x=6 y=642 width=65 height=85 xoffset=13 yoffset=69 xadvance=90 page=0 chnl=15
|
||||
char id=111 x=83 y=642 width=67 height=87 xoffset=12 yoffset=69 xadvance=90 page=0 chnl=15
|
||||
char id=112 x=162 y=642 width=66 height=113 xoffset=13 yoffset=69 xadvance=90 page=0 chnl=15
|
||||
char id=113 x=240 y=642 width=66 height=113 xoffset=12 yoffset=69 xadvance=90 page=0 chnl=15
|
||||
char id=114 x=318 y=642 width=67 height=85 xoffset=15 yoffset=69 xadvance=90 page=0 chnl=15
|
||||
char id=115 x=397 y=642 width=68 height=87 xoffset=12 yoffset=69 xadvance=90 page=0 chnl=15
|
||||
char id=116 x=477 y=642 width=73 height=107 xoffset=7 yoffset=47 xadvance=90 page=0 chnl=15
|
||||
char id=117 x=562 y=642 width=65 height=86 xoffset=13 yoffset=70 xadvance=90 page=0 chnl=15
|
||||
char id=118 x=639 y=642 width=79 height=84 xoffset=6 yoffset=70 xadvance=90 page=0 chnl=15
|
||||
char id=119 x=730 y=642 width=83 height=84 xoffset=4 yoffset=70 xadvance=90 page=0 chnl=15
|
||||
char id=120 x=825 y=642 width=78 height=84 xoffset=6 yoffset=70 xadvance=90 page=0 chnl=15
|
||||
char id=121 x=915 y=642 width=79 height=112 xoffset=6 yoffset=70 xadvance=90 page=0 chnl=15
|
||||
char id=122 x=1006 y=642 width=65 height=84 xoffset=13 yoffset=70 xadvance=90 page=0 chnl=15
|
||||
char id=123 x=1083 y=642 width=66 height=143 xoffset=11 yoffset=28 xadvance=90 page=0 chnl=15
|
||||
char id=124 x=1161 y=642 width=16 height=143 xoffset=38 yoffset=28 xadvance=90 page=0 chnl=15
|
||||
char id=125 x=1189 y=642 width=66 height=143 xoffset=14 yoffset=28 xadvance=90 page=0 chnl=15
|
||||
char id=126 x=1267 y=642 width=72 height=33 xoffset=10 yoffset=84 xadvance=90 page=0 chnl=15
|
||||
char id=163 x=1351 y=642 width=71 height=113 xoffset=11 yoffset=41 xadvance=90 page=0 chnl=15
|
||||
char id=165 x=1434 y=642 width=83 height=111 xoffset=4 yoffset=43 xadvance=90 page=0 chnl=15
|
||||
char id=167 x=1529 y=642 width=70 height=137 xoffset=10 yoffset=41 xadvance=90 page=0 chnl=15
|
||||
char id=176 x=1611 y=642 width=47 height=45 xoffset=22 yoffset=41 xadvance=90 page=0 chnl=15
|
||||
char id=178 x=1670 y=642 width=48 height=67 xoffset=22 yoffset=26 xadvance=90 page=0 chnl=15
|
||||
char id=215 x=1730 y=642 width=61 height=62 xoffset=15 yoffset=73 xadvance=90 page=0 chnl=15
|
||||
char id=231 x=1803 y=642 width=68 height=117 xoffset=12 yoffset=69 xadvance=90 page=0 chnl=15
|
||||
char id=234 x=1883 y=642 width=67 height=121 xoffset=12 yoffset=35 xadvance=90 page=0 chnl=15
|
||||
char id=241 x=1962 y=642 width=65 height=119 xoffset=13 yoffset=35 xadvance=90 page=0 chnl=15
|
||||
char id=252 x=6 y=854 width=65 height=121 xoffset=13 yoffset=35 xadvance=90 page=0 chnl=15
|
||||
char id=8209 x=83 y=854 width=0 height=0 xoffset=0 yoffset=0 xadvance=0 page=0 chnl=15
|
||||
char id=8211 x=95 y=854 width=68 height=14 xoffset=11 yoffset=97 xadvance=90 page=0 chnl=15
|
||||
char id=8226 x=175 y=854 width=38 height=38 xoffset=26 yoffset=80 xadvance=90 page=0 chnl=15
|
||||
char id=8364 x=225 y=854 width=81 height=113 xoffset=2 yoffset=43 xadvance=90 page=0 chnl=15
|
||||
char id=8627 x=318 y=854 width=0 height=0 xoffset=0 yoffset=0 xadvance=0 page=0 chnl=15
|
||||
char id=8679 x=330 y=854 width=87 height=115 xoffset=2 yoffset=39 xadvance=90 page=0 chnl=15
|
||||
char id=9003 x=429 y=854 width=0 height=0 xoffset=0 yoffset=0 xadvance=0 page=0 chnl=15
|
||||
char id=9251 x=441 y=854 width=0 height=0 xoffset=0 yoffset=0 xadvance=0 page=0 chnl=15
|
||||
char id=9654 x=453 y=854 width=93 height=95 xoffset=0 yoffset=52 xadvance=90 page=0 chnl=15
|
||||
char id=9664 x=558 y=854 width=93 height=95 xoffset=-2 yoffset=52 xadvance=90 page=0 chnl=15
|
||||
char id=9675 x=663 y=854 width=0 height=0 xoffset=0 yoffset=0 xadvance=0 page=0 chnl=15
|
||||
char id=9679 x=675 y=854 width=95 height=95 xoffset=-2 yoffset=52 xadvance=90 page=0 chnl=15
|
||||
char id=9881 x=782 y=854 width=0 height=0 xoffset=0 yoffset=0 xadvance=0 page=0 chnl=15
|
||||
char id=10003 x=794 y=854 width=81 height=79 xoffset=5 yoffset=59 xadvance=90 page=0 chnl=15
|
||||
char id=10004 x=887 y=854 width=0 height=0 xoffset=0 yoffset=0 xadvance=0 page=0 chnl=15
|
||||
char id=10005 x=899 y=854 width=73 height=73 xoffset=9 yoffset=67 xadvance=90 page=0 chnl=15
|
||||
|
After Width: | Height: | Size: 195 KiB |
@@ -10,7 +10,7 @@ TRANSLATIONS_DIR = SELFDRIVE_DIR / "ui" / "translations"
|
||||
LANGUAGES_FILE = TRANSLATIONS_DIR / "languages.json"
|
||||
|
||||
GLYPH_PADDING = 6
|
||||
EXTRA_CHARS = "–‑✓×°§•X⚙✕◀▶✔⌫⇧␣○●↳çêüñ–‑✓×°§•€£¥"
|
||||
EXTRA_CHARS = "–‑✓×°§•X⚙✕◀▶✔⌫⇧␣○●↳çêüñ–‑✓×°§•€£¥²"
|
||||
UNIFONT_LANGUAGES = {"ar", "th", "zh-CHT", "zh-CHS", "ko", "ja"}
|
||||
|
||||
|
||||
@@ -48,16 +48,18 @@ def _glyph_metrics(glyphs, rects, codepoints):
|
||||
offset_y = int(round(glyph.offsetY))
|
||||
min_offset_y = offset_y if min_offset_y is None else min(min_offset_y, offset_y)
|
||||
max_extent = max(max_extent, offset_y + height)
|
||||
entries.append({
|
||||
"id": codepoint,
|
||||
"x": int(round(rect.x)),
|
||||
"y": int(round(rect.y)),
|
||||
"width": width,
|
||||
"height": height,
|
||||
"xoffset": int(round(glyph.offsetX)),
|
||||
"yoffset": offset_y,
|
||||
"xadvance": int(round(glyph.advanceX)),
|
||||
})
|
||||
entries.append(
|
||||
{
|
||||
"id": codepoint,
|
||||
"x": int(round(rect.x)),
|
||||
"y": int(round(rect.y)),
|
||||
"width": width,
|
||||
"height": height,
|
||||
"xoffset": int(round(glyph.offsetX)),
|
||||
"yoffset": offset_y,
|
||||
"xadvance": int(round(glyph.advanceX)),
|
||||
}
|
||||
)
|
||||
|
||||
if min_offset_y is None:
|
||||
raise RuntimeError("No glyphs were generated")
|
||||
@@ -80,8 +82,10 @@ def _write_bmfont(path: Path, font_size: int, face: str, atlas_name: str, line_h
|
||||
]
|
||||
for entry in entries:
|
||||
lines.append(
|
||||
("char id={id:<4} x={x:<5} y={y:<5} width={width:<5} height={height:<5} " +
|
||||
"xoffset={xoffset:<5} yoffset={yoffset:<5} xadvance={xadvance:<5} page=0 chnl=15").format(**entry)
|
||||
(
|
||||
"char id={id:<4} x={x:<5} y={y:<5} width={width:<5} height={height:<5} "
|
||||
+ "xoffset={xoffset:<5} yoffset={yoffset:<5} xadvance={xadvance:<5} page=0 chnl=15"
|
||||
).format(**entry)
|
||||
)
|
||||
path.write_text("\n".join(lines) + "\n")
|
||||
|
||||
|
||||
@@ -0,0 +1,157 @@
|
||||
info face="unifont" size=-16 bold=0 italic=0 charset="" unicode=1 stretchH=100 smooth=0 aa=1 padding=0,0,0,0 spacing=0,0 outline=0
|
||||
common lineHeight=16 base=16 scaleW=512 scaleH=256 pages=1 packed=0 alphaChnl=0 redChnl=4 greenChnl=4 blueChnl=4
|
||||
page id=0 file="unifont.png"
|
||||
chars count=153
|
||||
char id=32 x=6 y=6 width=8 height=16 xoffset=0 yoffset=0 xadvance=8 page=0 chnl=15
|
||||
char id=33 x=26 y=6 width=1 height=10 xoffset=4 yoffset=4 xadvance=8 page=0 chnl=15
|
||||
char id=34 x=39 y=6 width=5 height=4 xoffset=2 yoffset=2 xadvance=8 page=0 chnl=15
|
||||
char id=35 x=56 y=6 width=6 height=10 xoffset=1 yoffset=4 xadvance=8 page=0 chnl=15
|
||||
char id=36 x=74 y=6 width=7 height=10 xoffset=1 yoffset=4 xadvance=8 page=0 chnl=15
|
||||
char id=37 x=93 y=6 width=7 height=10 xoffset=1 yoffset=4 xadvance=8 page=0 chnl=15
|
||||
char id=38 x=112 y=6 width=7 height=10 xoffset=1 yoffset=4 xadvance=8 page=0 chnl=15
|
||||
char id=39 x=131 y=6 width=1 height=4 xoffset=4 yoffset=2 xadvance=8 page=0 chnl=15
|
||||
char id=40 x=144 y=6 width=3 height=12 xoffset=3 yoffset=3 xadvance=8 page=0 chnl=15
|
||||
char id=41 x=159 y=6 width=3 height=12 xoffset=2 yoffset=3 xadvance=8 page=0 chnl=15
|
||||
char id=42 x=174 y=6 width=7 height=7 xoffset=1 yoffset=6 xadvance=8 page=0 chnl=15
|
||||
char id=43 x=193 y=6 width=7 height=7 xoffset=1 yoffset=6 xadvance=8 page=0 chnl=15
|
||||
char id=44 x=212 y=6 width=2 height=4 xoffset=3 yoffset=12 xadvance=8 page=0 chnl=15
|
||||
char id=45 x=226 y=6 width=4 height=1 xoffset=2 yoffset=9 xadvance=8 page=0 chnl=15
|
||||
char id=46 x=242 y=6 width=2 height=2 xoffset=3 yoffset=12 xadvance=8 page=0 chnl=15
|
||||
char id=47 x=256 y=6 width=6 height=10 xoffset=1 yoffset=4 xadvance=8 page=0 chnl=15
|
||||
char id=48 x=274 y=6 width=6 height=10 xoffset=1 yoffset=4 xadvance=8 page=0 chnl=15
|
||||
char id=49 x=292 y=6 width=5 height=10 xoffset=2 yoffset=4 xadvance=8 page=0 chnl=15
|
||||
char id=50 x=309 y=6 width=6 height=10 xoffset=1 yoffset=4 xadvance=8 page=0 chnl=15
|
||||
char id=51 x=327 y=6 width=6 height=10 xoffset=1 yoffset=4 xadvance=8 page=0 chnl=15
|
||||
char id=52 x=345 y=6 width=6 height=10 xoffset=1 yoffset=4 xadvance=8 page=0 chnl=15
|
||||
char id=53 x=363 y=6 width=6 height=10 xoffset=1 yoffset=4 xadvance=8 page=0 chnl=15
|
||||
char id=54 x=381 y=6 width=6 height=10 xoffset=1 yoffset=4 xadvance=8 page=0 chnl=15
|
||||
char id=55 x=399 y=6 width=6 height=10 xoffset=1 yoffset=4 xadvance=8 page=0 chnl=15
|
||||
char id=56 x=417 y=6 width=6 height=10 xoffset=1 yoffset=4 xadvance=8 page=0 chnl=15
|
||||
char id=57 x=435 y=6 width=6 height=10 xoffset=1 yoffset=4 xadvance=8 page=0 chnl=15
|
||||
char id=58 x=453 y=6 width=2 height=7 xoffset=3 yoffset=6 xadvance=8 page=0 chnl=15
|
||||
char id=59 x=467 y=6 width=2 height=9 xoffset=3 yoffset=6 xadvance=8 page=0 chnl=15
|
||||
char id=60 x=481 y=6 width=5 height=9 xoffset=2 yoffset=5 xadvance=8 page=0 chnl=15
|
||||
char id=61 x=6 y=34 width=6 height=5 xoffset=1 yoffset=7 xadvance=8 page=0 chnl=15
|
||||
char id=62 x=24 y=34 width=5 height=9 xoffset=1 yoffset=5 xadvance=8 page=0 chnl=15
|
||||
char id=63 x=41 y=34 width=6 height=10 xoffset=1 yoffset=4 xadvance=8 page=0 chnl=15
|
||||
char id=64 x=59 y=34 width=6 height=10 xoffset=1 yoffset=4 xadvance=8 page=0 chnl=15
|
||||
char id=65 x=77 y=34 width=6 height=10 xoffset=1 yoffset=4 xadvance=8 page=0 chnl=15
|
||||
char id=66 x=95 y=34 width=6 height=10 xoffset=1 yoffset=4 xadvance=8 page=0 chnl=15
|
||||
char id=67 x=113 y=34 width=6 height=10 xoffset=1 yoffset=4 xadvance=8 page=0 chnl=15
|
||||
char id=68 x=131 y=34 width=6 height=10 xoffset=1 yoffset=4 xadvance=8 page=0 chnl=15
|
||||
char id=69 x=149 y=34 width=6 height=10 xoffset=1 yoffset=4 xadvance=8 page=0 chnl=15
|
||||
char id=70 x=167 y=34 width=6 height=10 xoffset=1 yoffset=4 xadvance=8 page=0 chnl=15
|
||||
char id=71 x=185 y=34 width=6 height=10 xoffset=1 yoffset=4 xadvance=8 page=0 chnl=15
|
||||
char id=72 x=203 y=34 width=6 height=10 xoffset=1 yoffset=4 xadvance=8 page=0 chnl=15
|
||||
char id=73 x=221 y=34 width=5 height=10 xoffset=2 yoffset=4 xadvance=8 page=0 chnl=15
|
||||
char id=74 x=238 y=34 width=7 height=10 xoffset=1 yoffset=4 xadvance=8 page=0 chnl=15
|
||||
char id=75 x=257 y=34 width=6 height=10 xoffset=1 yoffset=4 xadvance=8 page=0 chnl=15
|
||||
char id=76 x=275 y=34 width=6 height=10 xoffset=1 yoffset=4 xadvance=8 page=0 chnl=15
|
||||
char id=77 x=293 y=34 width=6 height=10 xoffset=1 yoffset=4 xadvance=8 page=0 chnl=15
|
||||
char id=78 x=311 y=34 width=6 height=10 xoffset=1 yoffset=4 xadvance=8 page=0 chnl=15
|
||||
char id=79 x=329 y=34 width=6 height=10 xoffset=1 yoffset=4 xadvance=8 page=0 chnl=15
|
||||
char id=80 x=347 y=34 width=6 height=10 xoffset=1 yoffset=4 xadvance=8 page=0 chnl=15
|
||||
char id=81 x=365 y=34 width=7 height=11 xoffset=1 yoffset=4 xadvance=8 page=0 chnl=15
|
||||
char id=82 x=384 y=34 width=6 height=10 xoffset=1 yoffset=4 xadvance=8 page=0 chnl=15
|
||||
char id=83 x=402 y=34 width=6 height=10 xoffset=1 yoffset=4 xadvance=8 page=0 chnl=15
|
||||
char id=84 x=420 y=34 width=7 height=10 xoffset=1 yoffset=4 xadvance=8 page=0 chnl=15
|
||||
char id=85 x=439 y=34 width=6 height=10 xoffset=1 yoffset=4 xadvance=8 page=0 chnl=15
|
||||
char id=86 x=457 y=34 width=7 height=10 xoffset=1 yoffset=4 xadvance=8 page=0 chnl=15
|
||||
char id=87 x=476 y=34 width=6 height=10 xoffset=1 yoffset=4 xadvance=8 page=0 chnl=15
|
||||
char id=88 x=6 y=62 width=6 height=10 xoffset=1 yoffset=4 xadvance=8 page=0 chnl=15
|
||||
char id=89 x=24 y=62 width=7 height=10 xoffset=1 yoffset=4 xadvance=8 page=0 chnl=15
|
||||
char id=90 x=43 y=62 width=6 height=10 xoffset=1 yoffset=4 xadvance=8 page=0 chnl=15
|
||||
char id=91 x=61 y=62 width=3 height=12 xoffset=4 yoffset=3 xadvance=8 page=0 chnl=15
|
||||
char id=92 x=76 y=62 width=6 height=10 xoffset=1 yoffset=4 xadvance=8 page=0 chnl=15
|
||||
char id=93 x=94 y=62 width=3 height=12 xoffset=1 yoffset=3 xadvance=8 page=0 chnl=15
|
||||
char id=94 x=109 y=62 width=6 height=3 xoffset=1 yoffset=2 xadvance=8 page=0 chnl=15
|
||||
char id=95 x=127 y=62 width=7 height=1 xoffset=1 yoffset=14 xadvance=8 page=0 chnl=15
|
||||
char id=96 x=146 y=62 width=3 height=3 xoffset=2 yoffset=1 xadvance=8 page=0 chnl=15
|
||||
char id=97 x=161 y=62 width=6 height=8 xoffset=1 yoffset=6 xadvance=8 page=0 chnl=15
|
||||
char id=98 x=179 y=62 width=6 height=11 xoffset=1 yoffset=3 xadvance=8 page=0 chnl=15
|
||||
char id=99 x=197 y=62 width=6 height=8 xoffset=1 yoffset=6 xadvance=8 page=0 chnl=15
|
||||
char id=100 x=215 y=62 width=6 height=11 xoffset=1 yoffset=3 xadvance=8 page=0 chnl=15
|
||||
char id=101 x=233 y=62 width=6 height=8 xoffset=1 yoffset=6 xadvance=8 page=0 chnl=15
|
||||
char id=102 x=251 y=62 width=5 height=11 xoffset=1 yoffset=3 xadvance=8 page=0 chnl=15
|
||||
char id=103 x=268 y=62 width=6 height=11 xoffset=1 yoffset=5 xadvance=8 page=0 chnl=15
|
||||
char id=104 x=286 y=62 width=6 height=11 xoffset=1 yoffset=3 xadvance=8 page=0 chnl=15
|
||||
char id=105 x=304 y=62 width=5 height=11 xoffset=2 yoffset=3 xadvance=8 page=0 chnl=15
|
||||
char id=106 x=321 y=62 width=5 height=13 xoffset=1 yoffset=3 xadvance=8 page=0 chnl=15
|
||||
char id=107 x=338 y=62 width=6 height=11 xoffset=1 yoffset=3 xadvance=8 page=0 chnl=15
|
||||
char id=108 x=356 y=62 width=5 height=11 xoffset=2 yoffset=3 xadvance=8 page=0 chnl=15
|
||||
char id=109 x=373 y=62 width=7 height=8 xoffset=1 yoffset=6 xadvance=8 page=0 chnl=15
|
||||
char id=110 x=392 y=62 width=6 height=8 xoffset=1 yoffset=6 xadvance=8 page=0 chnl=15
|
||||
char id=111 x=410 y=62 width=6 height=8 xoffset=1 yoffset=6 xadvance=8 page=0 chnl=15
|
||||
char id=112 x=428 y=62 width=6 height=10 xoffset=1 yoffset=6 xadvance=8 page=0 chnl=15
|
||||
char id=113 x=446 y=62 width=6 height=10 xoffset=1 yoffset=6 xadvance=8 page=0 chnl=15
|
||||
char id=114 x=464 y=62 width=6 height=8 xoffset=1 yoffset=6 xadvance=8 page=0 chnl=15
|
||||
char id=115 x=482 y=62 width=6 height=8 xoffset=1 yoffset=6 xadvance=8 page=0 chnl=15
|
||||
char id=116 x=6 y=90 width=5 height=10 xoffset=1 yoffset=4 xadvance=8 page=0 chnl=15
|
||||
char id=117 x=23 y=90 width=6 height=8 xoffset=1 yoffset=6 xadvance=8 page=0 chnl=15
|
||||
char id=118 x=41 y=90 width=6 height=8 xoffset=1 yoffset=6 xadvance=8 page=0 chnl=15
|
||||
char id=119 x=59 y=90 width=7 height=8 xoffset=1 yoffset=6 xadvance=8 page=0 chnl=15
|
||||
char id=120 x=78 y=90 width=6 height=8 xoffset=1 yoffset=6 xadvance=8 page=0 chnl=15
|
||||
char id=121 x=96 y=90 width=6 height=10 xoffset=1 yoffset=6 xadvance=8 page=0 chnl=15
|
||||
char id=122 x=114 y=90 width=6 height=8 xoffset=1 yoffset=6 xadvance=8 page=0 chnl=15
|
||||
char id=123 x=132 y=90 width=4 height=13 xoffset=2 yoffset=3 xadvance=8 page=0 chnl=15
|
||||
char id=124 x=148 y=90 width=1 height=14 xoffset=4 yoffset=2 xadvance=8 page=0 chnl=15
|
||||
char id=125 x=161 y=90 width=4 height=13 xoffset=2 yoffset=3 xadvance=8 page=0 chnl=15
|
||||
char id=126 x=177 y=90 width=7 height=3 xoffset=1 yoffset=3 xadvance=8 page=0 chnl=15
|
||||
char id=163 x=196 y=90 width=7 height=10 xoffset=1 yoffset=4 xadvance=8 page=0 chnl=15
|
||||
char id=165 x=215 y=90 width=7 height=10 xoffset=1 yoffset=4 xadvance=8 page=0 chnl=15
|
||||
char id=167 x=234 y=90 width=6 height=10 xoffset=1 yoffset=4 xadvance=8 page=0 chnl=15
|
||||
char id=176 x=252 y=90 width=4 height=4 xoffset=2 yoffset=4 xadvance=8 page=0 chnl=15
|
||||
char id=178 x=268 y=90 width=5 height=7 xoffset=1 yoffset=3 xadvance=8 page=0 chnl=15
|
||||
char id=215 x=285 y=90 width=6 height=5 xoffset=1 yoffset=7 xadvance=8 page=0 chnl=15
|
||||
char id=231 x=303 y=90 width=6 height=10 xoffset=1 yoffset=6 xadvance=8 page=0 chnl=15
|
||||
char id=234 x=321 y=90 width=6 height=12 xoffset=1 yoffset=2 xadvance=8 page=0 chnl=15
|
||||
char id=241 x=339 y=90 width=6 height=12 xoffset=1 yoffset=2 xadvance=8 page=0 chnl=15
|
||||
char id=252 x=357 y=90 width=6 height=12 xoffset=1 yoffset=2 xadvance=8 page=0 chnl=15
|
||||
char id=1059 x=375 y=90 width=7 height=10 xoffset=1 yoffset=4 xadvance=8 page=0 chnl=15
|
||||
char id=1072 x=394 y=90 width=6 height=8 xoffset=1 yoffset=6 xadvance=8 page=0 chnl=15
|
||||
char id=1082 x=412 y=90 width=6 height=8 xoffset=1 yoffset=6 xadvance=8 page=0 chnl=15
|
||||
char id=1085 x=430 y=90 width=6 height=8 xoffset=1 yoffset=6 xadvance=8 page=0 chnl=15
|
||||
char id=1088 x=448 y=90 width=6 height=10 xoffset=1 yoffset=6 xadvance=8 page=0 chnl=15
|
||||
char id=1089 x=466 y=90 width=6 height=8 xoffset=1 yoffset=6 xadvance=8 page=0 chnl=15
|
||||
char id=1100 x=484 y=90 width=6 height=8 xoffset=1 yoffset=6 xadvance=8 page=0 chnl=15
|
||||
char id=1111 x=6 y=118 width=5 height=11 xoffset=2 yoffset=3 xadvance=8 page=0 chnl=15
|
||||
char id=1575 x=23 y=118 width=1 height=8 xoffset=4 yoffset=3 xadvance=8 page=0 chnl=15
|
||||
char id=1576 x=36 y=118 width=6 height=7 xoffset=1 yoffset=7 xadvance=8 page=0 chnl=15
|
||||
char id=1577 x=54 y=118 width=4 height=7 xoffset=2 yoffset=4 xadvance=8 page=0 chnl=15
|
||||
char id=1585 x=70 y=118 width=6 height=7 xoffset=1 yoffset=8 xadvance=8 page=0 chnl=15
|
||||
char id=1593 x=88 y=118 width=6 height=9 xoffset=1 yoffset=7 xadvance=8 page=0 chnl=15
|
||||
char id=1604 x=106 y=118 width=6 height=11 xoffset=1 yoffset=3 xadvance=8 page=0 chnl=15
|
||||
char id=1610 x=124 y=118 width=6 height=8 xoffset=1 yoffset=8 xadvance=8 page=0 chnl=15
|
||||
char id=3607 x=142 y=118 width=6 height=8 xoffset=1 yoffset=6 xadvance=8 page=0 chnl=15
|
||||
char id=3618 x=160 y=118 width=6 height=8 xoffset=1 yoffset=6 xadvance=8 page=0 chnl=15
|
||||
char id=3652 x=178 y=118 width=6 height=11 xoffset=1 yoffset=3 xadvance=8 page=0 chnl=15
|
||||
char id=8209 x=196 y=118 width=4 height=1 xoffset=2 yoffset=9 xadvance=8 page=0 chnl=15
|
||||
char id=8211 x=212 y=118 width=6 height=1 xoffset=1 yoffset=9 xadvance=8 page=0 chnl=15
|
||||
char id=8226 x=230 y=118 width=5 height=5 xoffset=1 yoffset=7 xadvance=8 page=0 chnl=15
|
||||
char id=8364 x=247 y=118 width=6 height=10 xoffset=1 yoffset=4 xadvance=8 page=0 chnl=15
|
||||
char id=8627 x=265 y=118 width=5 height=12 xoffset=2 yoffset=0 xadvance=8 page=0 chnl=15
|
||||
char id=8679 x=282 y=118 width=7 height=12 xoffset=1 yoffset=2 xadvance=8 page=0 chnl=15
|
||||
char id=9003 x=301 y=118 width=8 height=5 xoffset=0 yoffset=7 xadvance=8 page=0 chnl=15
|
||||
char id=9251 x=321 y=118 width=6 height=2 xoffset=1 yoffset=12 xadvance=8 page=0 chnl=15
|
||||
char id=9654 x=339 y=118 width=6 height=6 xoffset=1 yoffset=6 xadvance=8 page=0 chnl=15
|
||||
char id=9664 x=357 y=118 width=6 height=6 xoffset=1 yoffset=6 xadvance=8 page=0 chnl=15
|
||||
char id=9675 x=375 y=118 width=7 height=7 xoffset=1 yoffset=5 xadvance=8 page=0 chnl=15
|
||||
char id=9679 x=394 y=118 width=7 height=7 xoffset=1 yoffset=5 xadvance=8 page=0 chnl=15
|
||||
char id=9881 x=413 y=118 width=13 height=13 xoffset=1 yoffset=2 xadvance=16 page=0 chnl=15
|
||||
char id=10003 x=438 y=118 width=7 height=10 xoffset=1 yoffset=4 xadvance=8 page=0 chnl=15
|
||||
char id=10004 x=457 y=118 width=15 height=11 xoffset=0 yoffset=4 xadvance=16 page=0 chnl=15
|
||||
char id=10005 x=484 y=118 width=11 height=11 xoffset=2 yoffset=2 xadvance=16 page=0 chnl=15
|
||||
char id=20013 x=6 y=146 width=11 height=16 xoffset=2 yoffset=0 xadvance=16 page=0 chnl=15
|
||||
char id=20307 x=29 y=146 width=15 height=16 xoffset=0 yoffset=0 xadvance=16 page=0 chnl=15
|
||||
char id=25991 x=56 y=146 width=15 height=16 xoffset=0 yoffset=0 xadvance=16 page=0 chnl=15
|
||||
char id=26085 x=83 y=146 width=9 height=15 xoffset=3 yoffset=1 xadvance=16 page=0 chnl=15
|
||||
char id=26412 x=104 y=146 width=15 height=16 xoffset=0 yoffset=0 xadvance=16 page=0 chnl=15
|
||||
char id=31616 x=131 y=146 width=15 height=16 xoffset=0 yoffset=0 xadvance=16 page=0 chnl=15
|
||||
char id=32321 x=158 y=146 width=15 height=16 xoffset=0 yoffset=0 xadvance=16 page=0 chnl=15
|
||||
char id=35486 x=185 y=146 width=15 height=16 xoffset=0 yoffset=0 xadvance=16 page=0 chnl=15
|
||||
char id=39636 x=212 y=146 width=15 height=16 xoffset=0 yoffset=0 xadvance=16 page=0 chnl=15
|
||||
char id=44397 x=239 y=146 width=15 height=14 xoffset=1 yoffset=2 xadvance=16 page=0 chnl=15
|
||||
char id=50612 x=266 y=146 width=12 height=14 xoffset=2 yoffset=2 xadvance=16 page=0 chnl=15
|
||||
char id=54620 x=290 y=146 width=15 height=14 xoffset=1 yoffset=2 xadvance=16 page=0 chnl=15
|
||||
char id=65288 x=317 y=146 width=5 height=14 xoffset=7 yoffset=1 xadvance=16 page=0 chnl=15
|
||||
char id=65289 x=334 y=146 width=5 height=14 xoffset=3 yoffset=1 xadvance=16 page=0 chnl=15
|
||||
|
After Width: | Height: | Size: 6.4 KiB |
|
Before Width: | Height: | Size: 565 KiB After Width: | Height: | Size: 418 KiB |
|
Before Width: | Height: | Size: 79 KiB After Width: | Height: | Size: 69 KiB |
|
After Width: | Height: | Size: 418 KiB |
|
After Width: | Height: | Size: 69 KiB |
@@ -46,6 +46,7 @@ class CarSpecificEvents:
|
||||
|
||||
self.steering_unpressed = 0
|
||||
self.low_speed_alert = False
|
||||
self.gm_low_speed_alert_shown = False
|
||||
self.no_steer_warning = False
|
||||
self.silent_steer_warning = True
|
||||
|
||||
@@ -111,7 +112,17 @@ class CarSpecificEvents:
|
||||
events.add(EventName.manualRestart)
|
||||
|
||||
elif self.CP.brand == 'gm':
|
||||
events = self.create_common_events(CS, CS_prev, extra_gears=extra_gears, pcm_enable=self.CP.pcmCruise)
|
||||
events = self.create_common_events(CS, CS_prev, extra_gears=extra_gears, pcm_enable=self.CP.pcmCruise,
|
||||
suppress_low_speed_alert=True)
|
||||
|
||||
# StarPilot behavior: show the low-speed steer alert once per ignition cycle.
|
||||
if (not CS.cruiseState.available and not CS_prev.cruiseState.available and
|
||||
CS.vEgo < 0.1 and not CC.enabled):
|
||||
self.gm_low_speed_alert_shown = False
|
||||
|
||||
if CS.lowSpeedAlert and not self.gm_low_speed_alert_shown:
|
||||
events.add(EventName.belowSteerSpeed)
|
||||
self.gm_low_speed_alert_shown = True
|
||||
|
||||
# Enabling at a standstill with brake is allowed
|
||||
# TODO: verify 17 Volt can enable for the first time at a stop and allow for all GMs
|
||||
@@ -147,7 +158,7 @@ class CarSpecificEvents:
|
||||
return events
|
||||
|
||||
def create_common_events(self, CS: structs.CarState, CS_prev: car.CarState, extra_gears: list | None = None, pcm_enable=True,
|
||||
allow_button_cancel=True):
|
||||
allow_button_cancel=True, suppress_low_speed_alert=False):
|
||||
events = Events()
|
||||
|
||||
if CS.doorOpen:
|
||||
@@ -191,7 +202,7 @@ class CarSpecificEvents:
|
||||
events.add(EventName.vehicleSensorsInvalid)
|
||||
if CS.invalidLkasSetting:
|
||||
events.add(EventName.invalidLkasSetting)
|
||||
if CS.lowSpeedAlert:
|
||||
if CS.lowSpeedAlert and not suppress_low_speed_alert:
|
||||
events.add(EventName.belowSteerSpeed)
|
||||
if CS.buttonEnable:
|
||||
events.add(EventName.buttonEnable)
|
||||
|
||||
@@ -177,6 +177,8 @@ class Car:
|
||||
|
||||
if self.frogpilot_toggles.always_on_lateral:
|
||||
self.FPCP.alternativeExperience |= ALTERNATIVE_EXPERIENCE.ALWAYS_ON_LATERAL
|
||||
if getattr(self.frogpilot_toggles, "remap_cancel_to_distance", False):
|
||||
self.FPCP.alternativeExperience |= ALTERNATIVE_EXPERIENCE.GM_REMAP_CANCEL_TO_DISTANCE
|
||||
|
||||
fpcp_bytes = self.FPCP.to_bytes()
|
||||
self.params.put("FrogPilotCarParams", fpcp_bytes)
|
||||
|
||||
@@ -141,12 +141,12 @@ class VCruiseHelper:
|
||||
if self.CP.pcmCruise and not self.gm_cc_only:
|
||||
return
|
||||
|
||||
initial = V_CRUISE_INITIAL_EXPERIMENTAL_MODE if experimental_mode and not frogpilot_toggles.conditional_experimental_mode else V_CRUISE_INITIAL
|
||||
engage_floor_kph = max(V_CRUISE_MIN, 7.0 * CV.MPH_TO_KPH)
|
||||
|
||||
if (any(b.type in (ButtonType.accelCruise, ButtonType.resumeCruise) for b in CS.buttonEvents)
|
||||
and self.v_cruise_initialized or (self.gm_cc_only and resume_prev_button)):
|
||||
self.v_cruise_kph = self.v_cruise_kph_last
|
||||
else:
|
||||
self.v_cruise_kph = int(round(np.clip(CS.vEgo * CV.MS_TO_KPH, initial, V_CRUISE_MAX)))
|
||||
self.v_cruise_kph = int(round(np.clip(CS.vEgo * CV.MS_TO_KPH, engage_floor_kph, V_CRUISE_MAX)))
|
||||
|
||||
self.v_cruise_cluster_kph = self.v_cruise_kph
|
||||
|
||||
@@ -133,6 +133,7 @@ class Controls:
|
||||
|
||||
# accel PID loop
|
||||
pid_accel_limits = self.CI.get_pid_accel_limits(self.CP, CS.vEgo, CS.vCruise * CV.KPH_TO_MS)
|
||||
self.LoC.experimental_mode = bool(self.sm['selfdriveState'].experimentalMode)
|
||||
actuators.accel = float(min(self.LoC.update(CC.longActive, CS, long_plan.aTarget, long_plan.shouldStop, pid_accel_limits, self.frogpilot_toggles), self.frogpilot_toggles.max_desired_acceleration))
|
||||
|
||||
# Steering PID loop and lateral MPC
|
||||
|
||||
@@ -54,6 +54,10 @@ def get_accel_from_plan(speeds, accels, t_idxs, action_t=DT_MDL, vEgoStopping=0.
|
||||
v_target_1sec < vEgoStopping)
|
||||
return a_target, should_stop
|
||||
|
||||
|
||||
# Backward-compatible alias used by StarPilot tinygrad_modeld.
|
||||
get_accel_from_plan_tomb_raider = get_accel_from_plan
|
||||
|
||||
def curv_from_psis(psi_target, psi_rate, vego, action_t):
|
||||
vego = np.clip(vego, MIN_SPEED, np.inf)
|
||||
curv_from_psi = psi_target / (vego * action_t)
|
||||
|
||||
@@ -3,11 +3,13 @@ import numpy as np
|
||||
from collections import deque
|
||||
|
||||
from cereal import log
|
||||
from opendbc.car.lateral import FRICTION_THRESHOLD, get_friction
|
||||
from openpilot.common.constants import ACCELERATION_DUE_TO_GRAVITY
|
||||
from opendbc.car.gm.values import CAR as GM_CAR
|
||||
from opendbc.car.lateral import get_friction
|
||||
from openpilot.common.constants import ACCELERATION_DUE_TO_GRAVITY, CV
|
||||
from openpilot.common.filter_simple import FirstOrderFilter
|
||||
from openpilot.selfdrive.controls.lib.latcontrol import LatControl
|
||||
from openpilot.common.pid import PIDController
|
||||
from openpilot.selfdrive.controls.lib.drive_helpers import MIN_SPEED
|
||||
from openpilot.selfdrive.controls.lib.latcontrol import LatControl
|
||||
|
||||
# At higher speeds (25+mph) we can assume:
|
||||
# Lateral acceleration achieved by a specific car correlates to
|
||||
@@ -20,15 +22,46 @@ from openpilot.common.pid import PIDController
|
||||
# Additionally, there is friction in the steering wheel that needs
|
||||
# to be overcome to move it at all, this is compensated for too.
|
||||
|
||||
KP = 1.0
|
||||
KI = 0.3
|
||||
KD = 0.0
|
||||
KP = 0.7
|
||||
KI = 0.35
|
||||
|
||||
INTERP_SPEEDS = [1, 1.5, 2.0, 3.0, 5, 7.5, 10, 15, 30]
|
||||
KP_INTERP = [250, 120, 65, 30, 11.5, 5.5, 3.5, 2.0, KP]
|
||||
|
||||
LOW_SPEED_X = [0, 10, 20, 30]
|
||||
LOW_SPEED_Y = [12, 10.5, 8, 5]
|
||||
MAX_LAT_JERK_UP = 2.5 # m/s^3
|
||||
|
||||
LP_FILTER_CUTOFF_HZ = 1.2
|
||||
JERK_LOOKAHEAD_SECONDS = 0.19
|
||||
JERK_GAIN = 0.22
|
||||
LAT_ACCEL_REQUEST_BUFFER_SECONDS = 1.0
|
||||
VERSION = 0
|
||||
VERSION = 2
|
||||
DEBUG_TORQUE_TUNE = False
|
||||
FF_SCALE_BLEND_LAT_ACCEL = 0.05
|
||||
DEADZONE_BOOST_LAT_ACCEL = 0.15
|
||||
UNWIND_D_DES_THRESHOLD = -1.0
|
||||
UNWIND_LAT_ACCEL_NEAR_ZERO = 0.3
|
||||
MIN_LATERAL_CONTROL_SPEED = 0.3
|
||||
|
||||
BOLT_2022_2023_CARS = (
|
||||
GM_CAR.CHEVROLET_BOLT_ACC_2022_2023,
|
||||
GM_CAR.CHEVROLET_BOLT_ACC_2022_2023_PEDAL,
|
||||
GM_CAR.CHEVROLET_BOLT_CC_2022_2023,
|
||||
)
|
||||
BOLT_2018_2021_CARS = (
|
||||
GM_CAR.CHEVROLET_BOLT_CC_2018_2021,
|
||||
)
|
||||
BOLT_2017_CARS = (
|
||||
GM_CAR.CHEVROLET_BOLT_CC_2017,
|
||||
)
|
||||
BOLT_CARS = BOLT_2022_2023_CARS + BOLT_2018_2021_CARS + BOLT_2017_CARS
|
||||
|
||||
|
||||
def get_friction_threshold(v_ego: float) -> float:
|
||||
# Keep StarPilot's speed-scaled friction threshold behavior.
|
||||
return float(np.interp(v_ego, [1 * CV.MPH_TO_MS, 20 * CV.MPH_TO_MS, 75 * CV.MPH_TO_MS], [0.16, 0.19, 0.27]))
|
||||
|
||||
|
||||
class LatControlTorque(LatControl):
|
||||
def __init__(self, CP, CI, dt):
|
||||
@@ -36,13 +69,41 @@ class LatControlTorque(LatControl):
|
||||
self.torque_params = CP.lateralTuning.torque.as_builder()
|
||||
self.torque_from_lateral_accel = CI.torque_from_lateral_accel()
|
||||
self.lateral_accel_from_torque = CI.lateral_accel_from_torque()
|
||||
self.pid = PIDController([INTERP_SPEEDS, KP_INTERP], KI, KD, rate=1/self.dt)
|
||||
self.pid = PIDController([INTERP_SPEEDS, KP_INTERP], KI, rate=1/self.dt)
|
||||
self.update_limits()
|
||||
self.steering_angle_deadzone_deg = self.torque_params.steeringAngleDeadzoneDeg
|
||||
self.lat_accel_request_buffer_len = int(LAT_ACCEL_REQUEST_BUFFER_SECONDS / self.dt)
|
||||
self.lat_accel_request_buffer = deque([0.] * self.lat_accel_request_buffer_len , maxlen=self.lat_accel_request_buffer_len)
|
||||
self.lat_accel_request_buffer = deque([0.] * self.lat_accel_request_buffer_len, maxlen=self.lat_accel_request_buffer_len)
|
||||
self.lookahead_frames = int(JERK_LOOKAHEAD_SECONDS / self.dt)
|
||||
self.jerk_filter = FirstOrderFilter(0.0, 1 / (2 * np.pi * LP_FILTER_CUTOFF_HZ), self.dt)
|
||||
self.previous_measurement = 0.0
|
||||
self.measurement_rate_filter = FirstOrderFilter(0.0, 1 / (2 * np.pi * LP_FILTER_CUTOFF_HZ), self.dt)
|
||||
self.measurement_rate_filter = FirstOrderFilter(0.0, 1 / (2 * np.pi * (MAX_LAT_JERK_UP - 0.5)), self.dt)
|
||||
self.low_speed_reset_threshold = max(CP.minSteerSpeed, MIN_LATERAL_CONTROL_SPEED)
|
||||
self.steer_release_i_decay = 0.8
|
||||
self.prev_steering_pressed = False
|
||||
self.debug_counter = 0
|
||||
self.prev_desired_lateral_accel = 0.0
|
||||
|
||||
self.is_bolt = CP.carFingerprint in BOLT_CARS
|
||||
self.is_bolt_2022_2023 = CP.carFingerprint in BOLT_2022_2023_CARS
|
||||
self.is_bolt_2018_2021 = CP.carFingerprint in BOLT_2018_2021_CARS
|
||||
self.is_bolt_2017 = CP.carFingerprint in BOLT_2017_CARS
|
||||
self.use_bolt_ff_scaling = self.is_bolt_2022_2023 or self.is_bolt_2018_2021 or self.is_bolt_2017
|
||||
self.use_bolt_ki_multiplier = self.use_bolt_ff_scaling
|
||||
self.torque_ff_scale_pos = 1.0
|
||||
self.torque_ff_scale_neg = 1.0
|
||||
self.torque_deadzone_boost = float(getattr(self.torque_params, "kfDEPRECATED", 0.0))
|
||||
self.torque_ki_mult = 1.0
|
||||
|
||||
if self.is_bolt:
|
||||
kp_scale = getattr(self.torque_params, "kp", getattr(self.torque_params, "kpDEPRECATED", 1.0))
|
||||
ki_scale = getattr(self.torque_params, "ki", getattr(self.torque_params, "kiDEPRECATED", 1.0))
|
||||
kd_scale = getattr(self.torque_params, "kd", getattr(self.torque_params, "kdDEPRECATED", 1.0))
|
||||
self.torque_ff_scale_pos = float(kp_scale)
|
||||
self.torque_ff_scale_neg = float(ki_scale)
|
||||
self.torque_ki_mult = float(kd_scale)
|
||||
if self.use_bolt_ki_multiplier and self.torque_ki_mult > 0.0 and self.torque_ki_mult != 1.0:
|
||||
self.pid._k_i = [self.pid._k_i[0], [k * self.torque_ki_mult for k in self.pid._k_i[1]]]
|
||||
|
||||
def update_live_torque_params(self, latAccelFactor, latAccelOffset, friction):
|
||||
self.torque_params.latAccelFactor = latAccelFactor
|
||||
@@ -60,7 +121,15 @@ class LatControlTorque(LatControl):
|
||||
if not active:
|
||||
output_torque = 0.0
|
||||
pid_log.active = False
|
||||
self.pid.reset()
|
||||
self.previous_measurement = 0.0
|
||||
self.measurement_rate_filter.x = 0.0
|
||||
self.lat_accel_request_buffer = deque([0.] * self.lat_accel_request_buffer_len, maxlen=self.lat_accel_request_buffer_len)
|
||||
self.prev_desired_lateral_accel = 0.0
|
||||
else:
|
||||
if self.prev_steering_pressed and not CS.steeringPressed:
|
||||
self.pid.i *= self.steer_release_i_decay
|
||||
|
||||
measured_curvature = -VM.calc_curvature(math.radians(CS.steeringAngleDeg - params.angleOffsetDeg), CS.vEgo, params.roll)
|
||||
roll_compensation = params.roll * ACCELERATION_DUE_TO_GRAVITY
|
||||
curvature_deadzone = abs(VM.calc_curvature(math.radians(self.steering_angle_deadzone_deg), CS.vEgo, 0.0))
|
||||
@@ -68,33 +137,50 @@ class LatControlTorque(LatControl):
|
||||
|
||||
delay_frames = int(np.clip(lat_delay / self.dt, 1, self.lat_accel_request_buffer_len))
|
||||
expected_lateral_accel = self.lat_accel_request_buffer[-delay_frames]
|
||||
# TODO factor out lateral jerk from error to later replace it with delay independent alternative
|
||||
future_desired_lateral_accel = desired_curvature * CS.vEgo ** 2
|
||||
self.lat_accel_request_buffer.append(future_desired_lateral_accel)
|
||||
raw_lateral_jerk = (future_desired_lateral_accel - expected_lateral_accel) / max(lat_delay, self.dt)
|
||||
raw_lateral_jerk = np.clip(raw_lateral_jerk, -MAX_LAT_JERK_UP, MAX_LAT_JERK_UP)
|
||||
desired_lateral_jerk = np.clip(self.jerk_filter.update(raw_lateral_jerk), -MAX_LAT_JERK_UP, MAX_LAT_JERK_UP)
|
||||
gravity_adjusted_future_lateral_accel = future_desired_lateral_accel - roll_compensation
|
||||
desired_lateral_jerk = (future_desired_lateral_accel - expected_lateral_accel) / lat_delay
|
||||
setpoint = expected_lateral_accel + desired_lateral_jerk * lat_delay
|
||||
desired_lateral_accel_rate = (setpoint - self.prev_desired_lateral_accel) / self.dt
|
||||
unwind_detected = (desired_lateral_accel_rate < UNWIND_D_DES_THRESHOLD and
|
||||
abs(setpoint) < UNWIND_LAT_ACCEL_NEAR_ZERO)
|
||||
self.prev_desired_lateral_accel = setpoint
|
||||
|
||||
measurement = measured_curvature * CS.vEgo ** 2
|
||||
measurement_rate = self.measurement_rate_filter.update((measurement - self.previous_measurement) / self.dt)
|
||||
measurement_rate = np.clip(measurement_rate, -MAX_LAT_JERK_UP, MAX_LAT_JERK_UP)
|
||||
self.previous_measurement = measurement
|
||||
|
||||
setpoint = lat_delay * desired_lateral_jerk + expected_lateral_accel
|
||||
low_speed_factor = (np.interp(CS.vEgo, LOW_SPEED_X, LOW_SPEED_Y) / max(CS.vEgo, MIN_SPEED)) ** 2
|
||||
current_kp = np.interp(CS.vEgo, self.pid._k_p[0], self.pid._k_p[1])
|
||||
error = setpoint - measurement
|
||||
error_with_lsf = error * (1 + low_speed_factor / max(current_kp, 1e-3))
|
||||
|
||||
# do error correction in lateral acceleration space, convert at end to handle non-linear torque responses correctly
|
||||
pid_log.error = float(error)
|
||||
pid_log.error = float(error_with_lsf)
|
||||
ff = gravity_adjusted_future_lateral_accel
|
||||
# latAccelOffset corrects roll compensation bias from device roll misalignment relative to car roll
|
||||
ff -= self.torque_params.latAccelOffset
|
||||
# TODO jerk is weighted by lat_delay for legacy reasons, but should be made independent of it
|
||||
ff += get_friction(error, lateral_accel_deadzone, FRICTION_THRESHOLD, self.torque_params)
|
||||
ff_scale = 1.0
|
||||
if self.use_bolt_ff_scaling:
|
||||
ff_scale = np.interp(ff, [-FF_SCALE_BLEND_LAT_ACCEL, 0.0, FF_SCALE_BLEND_LAT_ACCEL],
|
||||
[self.torque_ff_scale_neg, 1.0, self.torque_ff_scale_pos])
|
||||
ff *= ff_scale
|
||||
ff += get_friction(error_with_lsf + JERK_GAIN * desired_lateral_jerk, lateral_accel_deadzone, get_friction_threshold(CS.vEgo), self.torque_params)
|
||||
deadzone_boost_active = False
|
||||
if self.torque_deadzone_boost > 0.0 and abs(gravity_adjusted_future_lateral_accel) < DEADZONE_BOOST_LAT_ACCEL:
|
||||
boost_scale = np.interp(abs(gravity_adjusted_future_lateral_accel), [0.0, DEADZONE_BOOST_LAT_ACCEL], [1.0, 0.0])
|
||||
ff += np.sign(gravity_adjusted_future_lateral_accel) * self.torque_deadzone_boost * boost_scale
|
||||
deadzone_boost_active = True
|
||||
|
||||
freeze_integrator = steer_limited_by_safety or CS.steeringPressed or CS.vEgo < 5
|
||||
output_lataccel = self.pid.update(pid_log.error,
|
||||
-measurement_rate,
|
||||
feedforward=ff,
|
||||
speed=CS.vEgo,
|
||||
freeze_integrator=freeze_integrator)
|
||||
if CS.vEgo < self.low_speed_reset_threshold:
|
||||
self.pid.reset()
|
||||
freeze_integrator = (steer_limited_by_safety or CS.steeringPressed or
|
||||
CS.vEgo < self.low_speed_reset_threshold or unwind_detected)
|
||||
output_lataccel = self.pid.update(pid_log.error, error_rate=-measurement_rate, speed=CS.vEgo, feedforward=ff, freeze_integrator=freeze_integrator)
|
||||
output_torque = self.torque_from_lateral_accel(output_lataccel, self.torque_params)
|
||||
|
||||
pid_log.active = True
|
||||
@@ -102,11 +188,19 @@ class LatControlTorque(LatControl):
|
||||
pid_log.i = float(self.pid.i)
|
||||
pid_log.d = float(self.pid.d)
|
||||
pid_log.f = float(self.pid.f)
|
||||
pid_log.output = float(-output_torque) # TODO: log lat accel?
|
||||
pid_log.output = float(-output_torque) # TODO: log lat accel?
|
||||
pid_log.actualLateralAccel = float(measurement)
|
||||
pid_log.desiredLateralAccel = float(setpoint)
|
||||
pid_log.desiredLateralJerk = float(desired_lateral_jerk)
|
||||
pid_log.saturated = bool(self._check_saturation(self.steer_max - abs(output_torque) < 1e-3, CS, steer_limited_by_safety, curvature_limited))
|
||||
|
||||
if DEBUG_TORQUE_TUNE and self.is_bolt:
|
||||
self.debug_counter += 1
|
||||
if self.debug_counter % 50 == 0:
|
||||
print(f"bolt_torque ff_scale={ff_scale:.3f} pos={self.torque_ff_scale_pos:.3f} "
|
||||
f"neg={self.torque_ff_scale_neg:.3f} deadzone_boost_active={deadzone_boost_active}")
|
||||
|
||||
self.prev_steering_pressed = CS.steeringPressed
|
||||
|
||||
# TODO left is positive in this convention
|
||||
return -output_torque, 0.0, pid_log
|
||||
|
||||
@@ -1,2 +1,4 @@
|
||||
acados_ocp_lat.json
|
||||
c_generated_code/
|
||||
c_generated_code/**
|
||||
!c_generated_code/acados_ocp_solver_pyx.so
|
||||
!c_generated_code/libacados_ocp_solver_lat.so
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
import numpy as np
|
||||
from cereal import car
|
||||
from openpilot.common.realtime import DT_CTRL
|
||||
from openpilot.common.filter_simple import FirstOrderFilter
|
||||
from openpilot.selfdrive.controls.lib.drive_helpers import CONTROL_N
|
||||
from openpilot.common.pid import PIDController
|
||||
from openpilot.selfdrive.modeld.constants import ModelConstants
|
||||
from opendbc.car.gm.values import CarControllerParams, GMFlags
|
||||
|
||||
CONTROL_N_T_IDX = ModelConstants.T_IDXS[:CONTROL_N]
|
||||
|
||||
@@ -12,6 +14,8 @@ LongCtrlState = car.CarControl.Actuators.LongControlState
|
||||
|
||||
def long_control_state_trans(CP, active, long_control_state, v_ego,
|
||||
should_stop, brake_pressed, cruise_standstill, frogpilot_toggles):
|
||||
# Ignore cruise standstill if car has a gas interceptor.
|
||||
cruise_standstill = cruise_standstill and not CP.enableGasInterceptorDEPRECATED
|
||||
stopping_condition = should_stop
|
||||
starting_condition = (not should_stop and
|
||||
not cruise_standstill and
|
||||
@@ -48,13 +52,73 @@ class LongControl:
|
||||
def __init__(self, CP):
|
||||
self.CP = CP
|
||||
self.long_control_state = LongCtrlState.off
|
||||
self.experimental_mode = False
|
||||
self.pid = PIDController((CP.longitudinalTuning.kpBP, CP.longitudinalTuning.kpV),
|
||||
(CP.longitudinalTuning.kiBP, CP.longitudinalTuning.kiV),
|
||||
rate=1 / DT_CTRL)
|
||||
# Preserve legacy behavior when no feedforward gain is provided (default of 0.0).
|
||||
kf = getattr(CP.longitudinalTuning, 'kfDEPRECATED', 0.0)
|
||||
self.feedforward_gain = kf if kf != 0.0 else 1.0
|
||||
self._mode_setup()
|
||||
self.last_output_accel = 0.0
|
||||
self.last_a_target = 0.0
|
||||
self.integrator_hold_frames = 0
|
||||
self.is_gm_pedal_long = bool(
|
||||
CP.brand == "gm" and CP.enableGasInterceptorDEPRECATED and (CP.flags & GMFlags.PEDAL_LONG.value)
|
||||
)
|
||||
|
||||
def update_mpc_mode(self, experimental_mode: bool):
|
||||
new_mode = 'blended' if experimental_mode else 'acc'
|
||||
if self.transitioning and self.prev_mode == 'blended' and self.current_mode == 'acc':
|
||||
self.mode_transition_timer = 0.0
|
||||
|
||||
if new_mode != self.current_mode:
|
||||
self.prev_mode = self.current_mode
|
||||
self.transitioning = True
|
||||
self.mode_transition_timer = 0.0
|
||||
self.mode_transition_filter.x = self.last_output_accel
|
||||
self.current_mode = new_mode
|
||||
|
||||
if self.transitioning:
|
||||
self.mode_transition_timer += DT_CTRL
|
||||
if self.mode_transition_timer >= self.mode_transition_duration:
|
||||
self.transitioning = False
|
||||
|
||||
def _mode_setup(self):
|
||||
self.prev_mode = 'acc'
|
||||
self.current_mode = 'acc'
|
||||
self.mode_transition_filter = FirstOrderFilter(0.0, 0.5, DT_CTRL)
|
||||
self.mode_transition_timer = 0.0
|
||||
self.mode_transition_duration = 1.0
|
||||
self.transitioning = False
|
||||
|
||||
def reset(self):
|
||||
self.pid.reset()
|
||||
self.last_a_target = 0.0
|
||||
self.integrator_hold_frames = 0
|
||||
|
||||
def _get_pedal_long_freeze(self, a_target, error, v_ego, accel_limits):
|
||||
if not self.is_gm_pedal_long:
|
||||
self.last_a_target = a_target
|
||||
self.integrator_hold_frames = 0
|
||||
return False
|
||||
|
||||
handoff_threshold = np.interp(v_ego, [0.0, 4.0, 12.0, 25.0], [0.35, 0.45, 0.55, 0.70])
|
||||
if abs(a_target - self.last_a_target) > handoff_threshold:
|
||||
hold_frames = int(round(np.interp(v_ego, [0.0, 4.0, 12.0, 25.0], [25.0, 20.0, 14.0, 10.0])))
|
||||
self.integrator_hold_frames = max(self.integrator_hold_frames, hold_frames)
|
||||
self.last_a_target = a_target
|
||||
|
||||
if self.integrator_hold_frames > 0:
|
||||
self.integrator_hold_frames -= 1
|
||||
|
||||
sat_buffer = 0.03
|
||||
at_neg_sat = self.last_output_accel <= (accel_limits[0] + sat_buffer)
|
||||
at_pos_sat = self.last_output_accel >= (accel_limits[1] - sat_buffer)
|
||||
sat_pushing_lower = at_neg_sat and error < -0.05
|
||||
sat_pushing_upper = at_pos_sat and error > 0.05
|
||||
|
||||
return self.integrator_hold_frames > 0 or sat_pushing_lower or sat_pushing_upper
|
||||
|
||||
def update(self, active, CS, a_target, should_stop, accel_limits, frogpilot_toggles):
|
||||
"""Update longitudinal control. This updates the state machine and runs a PID loop"""
|
||||
@@ -76,16 +140,30 @@ class LongControl:
|
||||
self.reset()
|
||||
|
||||
elif self.long_control_state == LongCtrlState.starting:
|
||||
if frogpilot_toggles.human_acceleration:
|
||||
output_accel = max(a_target, frogpilot_toggles.startAccel)
|
||||
else:
|
||||
output_accel = frogpilot_toggles.startAccel
|
||||
output_accel = (a_target if frogpilot_toggles.human_acceleration else frogpilot_toggles.startAccel)
|
||||
self.reset()
|
||||
|
||||
else: # LongCtrlState.pid
|
||||
error = a_target - CS.aEgo
|
||||
output_accel = self.pid.update(error, speed=CS.vEgo,
|
||||
feedforward=a_target)
|
||||
self.update_mpc_mode(self.experimental_mode)
|
||||
freeze_integrator = self._get_pedal_long_freeze(a_target, error, CS.vEgo, accel_limits)
|
||||
raw_output_accel = self.pid.update(error, speed=CS.vEgo,
|
||||
feedforward=a_target * self.feedforward_gain,
|
||||
freeze_integrator=freeze_integrator)
|
||||
|
||||
if self.transitioning and self.prev_mode == 'acc' and self.current_mode == 'blended':
|
||||
if raw_output_accel < 0 and raw_output_accel < self.last_output_accel:
|
||||
progress = min(1.0, self.mode_transition_timer / self.mode_transition_duration)
|
||||
# Soften transition at low urgency, but keep sharp for high decel.
|
||||
urgency_denom = max(1e-3, abs(CarControllerParams.ACCEL_MIN))
|
||||
urgency = abs(raw_output_accel / urgency_denom)
|
||||
urgency_smooth = min(1.0, urgency ** 0.4)
|
||||
blend_factor = 1.0 - (1.0 - progress) * (1.0 - urgency_smooth)
|
||||
output_accel = self.last_output_accel + (raw_output_accel - self.last_output_accel) * blend_factor
|
||||
else:
|
||||
output_accel = raw_output_accel
|
||||
else:
|
||||
output_accel = raw_output_accel
|
||||
|
||||
self.last_output_accel = np.clip(output_accel, accel_limits[0], accel_limits[1])
|
||||
return self.last_output_accel
|
||||
|
||||
@@ -1,2 +1,4 @@
|
||||
acados_ocp_long.json
|
||||
c_generated_code/
|
||||
c_generated_code/**
|
||||
!c_generated_code/acados_ocp_solver_pyx.so
|
||||
!c_generated_code/libacados_ocp_solver_long.so
|
||||
|
||||
@@ -3,12 +3,21 @@ import os
|
||||
import time
|
||||
import numpy as np
|
||||
from cereal import log
|
||||
from opendbc.car.interfaces import ACCEL_MIN, ACCEL_MAX
|
||||
try:
|
||||
from opendbc.car.interfaces import ACCEL_MIN, ACCEL_MAX
|
||||
except Exception:
|
||||
# Build-time fallback for generated-code steps before full python extension availability.
|
||||
ACCEL_MIN = -3.5
|
||||
ACCEL_MAX = 2.0
|
||||
from openpilot.common.constants import CV
|
||||
from openpilot.common.filter_simple import FirstOrderFilter
|
||||
from openpilot.common.realtime import DT_MDL
|
||||
from openpilot.common.swaglog import cloudlog
|
||||
# WARNING: imports outside of constants will not trigger a rebuild
|
||||
from openpilot.selfdrive.modeld.constants import index_function
|
||||
from openpilot.selfdrive.controls.radard import _LEAD_ACCEL_TAU
|
||||
|
||||
# Keep in sync with selfdrive/controls/radard.py
|
||||
_LEAD_ACCEL_TAU = 1.5
|
||||
|
||||
if __name__ == '__main__': # generating code
|
||||
from openpilot.third_party.acados.acados_template import AcadosModel, AcadosOcp, AcadosOcpSolver
|
||||
@@ -31,12 +40,25 @@ COST_E_DIM = 5
|
||||
COST_DIM = COST_E_DIM + 1
|
||||
CONSTR_DIM = 4
|
||||
|
||||
X_EGO_OBSTACLE_COST = 3.
|
||||
# ===== VOACC SPEED-BASED TUNING PARAMETERS =====
|
||||
# Speed ranges: [0-35, 35-55, 55-70, 70+ mph]
|
||||
SPEED_BREAKPOINTS = [0, 35, 55, 70]
|
||||
|
||||
# [City, Urban Hwy, Rural Hwy, High Speed]
|
||||
X_EGO_OBSTACLE_COSTS = [3.0, 3.0, 2.5, 2.0]
|
||||
J_EGO_COSTS = [5.0, 4.75, 4.5, 4.0]
|
||||
A_CHANGE_COSTS = [200.0, 195.0, 180.0, 170.0]
|
||||
LEAD_FILTER_TIME_LOW = 0.8
|
||||
LEAD_FILTER_TIME_HIGH = 1.2
|
||||
DIST_ADAPTS = [0.04, 0.06, 0.06, 0.05]
|
||||
|
||||
# Current active values
|
||||
X_EGO_OBSTACLE_COST = 2.75
|
||||
X_EGO_COST = 0.
|
||||
V_EGO_COST = 0.
|
||||
A_EGO_COST = 0.
|
||||
J_EGO_COST = 5.0
|
||||
A_CHANGE_COST = 200.
|
||||
J_EGO_COST = 5.5
|
||||
A_CHANGE_COST = 250.0
|
||||
DANGER_ZONE_COST = 100.
|
||||
CRASH_DISTANCE = .25
|
||||
LEAD_DANGER_FACTOR = 0.75
|
||||
@@ -58,6 +80,11 @@ STOP_DISTANCE = 6.0
|
||||
CRUISE_MIN_ACCEL = -1.2
|
||||
CRUISE_MAX_ACCEL = 1.6
|
||||
|
||||
|
||||
def get_speed_based_param(speed_mph, param_array):
|
||||
return float(np.interp(speed_mph, SPEED_BREAKPOINTS, param_array))
|
||||
|
||||
|
||||
def get_jerk_factor(aggressive_jerk_acceleration=0.5, aggressive_jerk_danger=0.5, aggressive_jerk_speed=0.5,
|
||||
standard_jerk_acceleration=1.0, standard_jerk_danger=1.0, standard_jerk_speed=1.0,
|
||||
relaxed_jerk_acceleration=1.0, relaxed_jerk_danger=1.0, relaxed_jerk_speed=1.0,
|
||||
@@ -105,13 +132,15 @@ def get_T_FOLLOW(aggressive_follow=1.25, standard_follow=1.45, relaxed_follow=1.
|
||||
def get_stopped_equivalence_factor(v_lead):
|
||||
return (v_lead**2) / (2 * COMFORT_BRAKE)
|
||||
|
||||
def get_safe_obstacle_distance(v_ego, t_follow):
|
||||
return (v_ego**2) / (2 * COMFORT_BRAKE) + t_follow * v_ego + STOP_DISTANCE
|
||||
|
||||
def desired_follow_distance(v_ego, v_lead, t_follow=None):
|
||||
def get_safe_obstacle_distance(v_ego, t_follow, stop_distance=STOP_DISTANCE):
|
||||
return (v_ego**2) / (2 * COMFORT_BRAKE) + t_follow * v_ego + stop_distance
|
||||
|
||||
|
||||
def desired_follow_distance(v_ego, v_lead, t_follow=None, stop_distance=STOP_DISTANCE):
|
||||
if t_follow is None:
|
||||
t_follow = get_T_FOLLOW()
|
||||
return get_safe_obstacle_distance(v_ego, t_follow) - get_stopped_equivalence_factor(v_lead)
|
||||
return get_safe_obstacle_distance(v_ego, t_follow, stop_distance) - get_stopped_equivalence_factor(v_lead)
|
||||
|
||||
|
||||
def gen_long_model():
|
||||
@@ -249,9 +278,27 @@ class LongitudinalMpc:
|
||||
self.mode = mode
|
||||
self.dt = dt
|
||||
self.solver = AcadosOcpSolverCython(MODEL_NAME, ACADOS_SOLVER_TYPE, N)
|
||||
self.reset()
|
||||
self.source = SOURCES[2]
|
||||
|
||||
self.current_filter_time = LEAD_FILTER_TIME_LOW
|
||||
self.prev_filter_time = self.current_filter_time
|
||||
self.lead_a_filter = FirstOrderFilter(0.0, self.current_filter_time, self.dt)
|
||||
self.lead_v_filter = FirstOrderFilter(0.0, self.current_filter_time, self.dt)
|
||||
self.filter_time_factor = 1.0
|
||||
self.prev_filter_time_factor = 1.0
|
||||
self.slew_per_sec = 1.0
|
||||
|
||||
self.current_x_ego_cost = X_EGO_OBSTACLE_COSTS[0]
|
||||
self.current_j_ego_cost = J_EGO_COSTS[0]
|
||||
self.current_a_change_cost = A_CHANGE_COSTS[0]
|
||||
self.current_dist_adapt = DIST_ADAPTS[0]
|
||||
|
||||
self.cruise_min_a = ACCEL_MIN
|
||||
self.max_a = min(ACCEL_MAX, 1.2)
|
||||
self.stop_distance = STOP_DISTANCE
|
||||
|
||||
self.reset()
|
||||
|
||||
def reset(self):
|
||||
# self.solver = AcadosOcpSolverCython(MODEL_NAME, ACADOS_SOLVER_TYPE, N)
|
||||
self.solver.reset()
|
||||
@@ -297,10 +344,56 @@ class LongitudinalMpc:
|
||||
for i in range(N):
|
||||
self.solver.cost_set(i, 'Zl', Zl)
|
||||
|
||||
def set_weights(self, acceleration_jerk=1.0, danger_jerk=1.0, speed_jerk=1.0, prev_accel_constraint=True, personality=log.LongitudinalPersonality.standard):
|
||||
def set_weights(self, acceleration_jerk=1.0, danger_jerk=1.0, speed_jerk=1.0, prev_accel_constraint=True,
|
||||
personality=log.LongitudinalPersonality.standard, v_ego=0.0, lead_dist=50.0,
|
||||
uncertainty=0.0, accel_reengage=False, panic_bypass=False, stop_distance=STOP_DISTANCE):
|
||||
self.stop_distance = max(float(stop_distance), 4.0)
|
||||
|
||||
speed_mph = v_ego * CV.MS_TO_MPH
|
||||
self.current_x_ego_cost = get_speed_based_param(speed_mph, X_EGO_OBSTACLE_COSTS)
|
||||
self.current_j_ego_cost = get_speed_based_param(speed_mph, J_EGO_COSTS)
|
||||
self.current_a_change_cost = get_speed_based_param(speed_mph, A_CHANGE_COSTS)
|
||||
|
||||
dist_adapt_array = [0.0, DIST_ADAPTS[1], DIST_ADAPTS[2], DIST_ADAPTS[3]]
|
||||
self.current_dist_adapt = get_speed_based_param(speed_mph, dist_adapt_array)
|
||||
|
||||
if speed_mph < 47:
|
||||
self.current_filter_time = 0.0
|
||||
else:
|
||||
self.current_filter_time = float(np.interp(speed_mph, [47, 65], [0.0, LEAD_FILTER_TIME_HIGH]))
|
||||
|
||||
if abs(self.current_filter_time - self.prev_filter_time) > 0.1:
|
||||
self.lead_a_filter = FirstOrderFilter(self.lead_a_filter.x, self.current_filter_time, self.dt)
|
||||
self.lead_v_filter = FirstOrderFilter(self.lead_v_filter.x, self.current_filter_time, self.dt)
|
||||
self.prev_filter_time = self.current_filter_time
|
||||
|
||||
dist_factor = 1.0 + self.current_dist_adapt * (20.0 / max(lead_dist, 5.0))
|
||||
acceleration_jerk *= dist_factor
|
||||
danger_jerk *= dist_factor
|
||||
speed_jerk *= dist_factor
|
||||
|
||||
if uncertainty <= 0.45:
|
||||
tgt_factor = 1.0
|
||||
elif uncertainty >= 0.70:
|
||||
tgt_factor = 0.0
|
||||
else:
|
||||
tgt_factor = float(np.interp(uncertainty, [0.45, 0.70], [1.0, 0.30]))
|
||||
|
||||
if accel_reengage:
|
||||
tgt_factor = min(tgt_factor, 0.5)
|
||||
if panic_bypass:
|
||||
tgt_factor = 0.0
|
||||
|
||||
max_step = self.slew_per_sec * self.dt
|
||||
delta = np.clip(tgt_factor - self.filter_time_factor, -max_step, max_step)
|
||||
self.filter_time_factor += float(delta)
|
||||
|
||||
if 0.45 <= uncertainty < 0.60:
|
||||
speed_jerk *= float(np.interp(uncertainty, [0.45, 0.60], [1.2, 1.5]))
|
||||
|
||||
if self.mode == 'acc':
|
||||
a_change_cost = acceleration_jerk if prev_accel_constraint else 0
|
||||
cost_weights = [X_EGO_OBSTACLE_COST, X_EGO_COST, V_EGO_COST, A_EGO_COST, a_change_cost, speed_jerk]
|
||||
cost_weights = [self.current_x_ego_cost, X_EGO_COST, V_EGO_COST, A_EGO_COST, a_change_cost, speed_jerk]
|
||||
constraint_cost_weights = [LIMIT_COST, LIMIT_COST, LIMIT_COST, danger_jerk]
|
||||
elif self.mode == 'blended':
|
||||
a_change_cost = 40.0 if prev_accel_constraint else 0
|
||||
@@ -310,6 +403,12 @@ class LongitudinalMpc:
|
||||
raise NotImplementedError(f'Planner mode {self.mode} not recognized in planner cost set')
|
||||
self.set_cost_weights(cost_weights, constraint_cost_weights)
|
||||
|
||||
if abs(self.filter_time_factor - self.prev_filter_time_factor) > 0.05:
|
||||
new_filter_time = self.current_filter_time * self.filter_time_factor
|
||||
self.lead_a_filter = FirstOrderFilter(self.lead_a_filter.x, new_filter_time, self.dt)
|
||||
self.lead_v_filter = FirstOrderFilter(self.lead_v_filter.x, new_filter_time, self.dt)
|
||||
self.prev_filter_time_factor = self.filter_time_factor
|
||||
|
||||
def set_cur_state(self, v, a):
|
||||
v_prev = self.x0[1]
|
||||
self.x0[1] = v
|
||||
@@ -319,22 +418,35 @@ class LongitudinalMpc:
|
||||
self.solver.set(i, 'x', self.x0)
|
||||
|
||||
@staticmethod
|
||||
def extrapolate_lead(x_lead, v_lead, a_lead, a_lead_tau):
|
||||
a_lead_traj = a_lead * np.exp(-a_lead_tau * (T_IDXS**2)/2.)
|
||||
v_lead_traj = np.clip(v_lead + np.cumsum(T_DIFFS * a_lead_traj), 0.0, 1e8)
|
||||
x_lead_traj = x_lead + np.cumsum(T_DIFFS * v_lead_traj)
|
||||
def extrapolate_lead(x_lead, v_lead, a_lead, a_lead_tau, v_ego=0.0):
|
||||
speed_mph = v_ego * CV.MS_TO_MPH
|
||||
exp_weight = float(np.interp(speed_mph, [0, 20, 35], [1.0, 1.0, 0.0]))
|
||||
|
||||
if exp_weight > 0.0:
|
||||
a_lead_traj_exp = a_lead * np.exp(-a_lead_tau * (T_IDXS**2)/2.)
|
||||
v_lead_traj_exp = np.clip(v_lead + np.cumsum(T_DIFFS * a_lead_traj_exp), 0.0, 1e8)
|
||||
x_lead_traj_exp = x_lead + np.cumsum(T_DIFFS * v_lead_traj_exp)
|
||||
else:
|
||||
x_lead_traj_exp = np.zeros_like(T_IDXS)
|
||||
v_lead_traj_exp = np.zeros_like(T_IDXS)
|
||||
|
||||
v_lead_traj_const = np.clip(v_lead + a_lead * T_IDXS, 0.0, 1e8)
|
||||
x_lead_traj_const = x_lead + v_lead * T_IDXS + 0.5 * a_lead * T_IDXS**2
|
||||
|
||||
v_lead_traj = exp_weight * v_lead_traj_exp + (1.0 - exp_weight) * v_lead_traj_const
|
||||
x_lead_traj = exp_weight * x_lead_traj_exp + (1.0 - exp_weight) * x_lead_traj_const
|
||||
|
||||
lead_xv = np.column_stack((x_lead_traj, v_lead_traj))
|
||||
return lead_xv
|
||||
|
||||
def process_lead(self, lead):
|
||||
def process_lead(self, lead, tracking_lead=True):
|
||||
v_ego = self.x0[1]
|
||||
if lead is not None and lead.status:
|
||||
if lead is not None and lead.status and tracking_lead:
|
||||
x_lead = lead.dRel
|
||||
v_lead = lead.vLead
|
||||
a_lead = lead.aLeadK
|
||||
a_lead_tau = lead.aLeadTau
|
||||
else:
|
||||
# Fake a fast lead car, so mpc can keep running in the same mode
|
||||
x_lead = 50.0
|
||||
v_lead = v_ego + 10.0
|
||||
a_lead = 0.0
|
||||
@@ -346,15 +458,25 @@ class LongitudinalMpc:
|
||||
x_lead = np.clip(x_lead, min_x_lead, 1e8)
|
||||
v_lead = np.clip(v_lead, 0.0, 1e8)
|
||||
a_lead = np.clip(a_lead, -10., 5.)
|
||||
lead_xv = self.extrapolate_lead(x_lead, v_lead, a_lead, a_lead_tau)
|
||||
|
||||
self.lead_a_filter.update(a_lead)
|
||||
self.lead_v_filter.update(v_lead)
|
||||
lead_xv = self.extrapolate_lead(x_lead, self.lead_v_filter.x, self.lead_a_filter.x, a_lead_tau, v_ego)
|
||||
return lead_xv
|
||||
|
||||
def update(self, radarstate, v_cruise, x, v, a, j, danger_factor, t_follow, personality=log.LongitudinalPersonality.standard):
|
||||
v_ego = self.x0[1]
|
||||
self.status = radarstate.leadOne.status or radarstate.leadTwo.status
|
||||
def set_accel_limits(self, min_a, max_a):
|
||||
self.cruise_min_a = min_a
|
||||
self.max_a = max_a
|
||||
|
||||
lead_xv_0 = self.process_lead(radarstate.leadOne)
|
||||
lead_xv_1 = self.process_lead(radarstate.leadTwo)
|
||||
def update(self, radarstate, v_cruise, x, v, a, j, danger_factor, t_follow,
|
||||
personality=log.LongitudinalPersonality.standard, tracking_lead=True):
|
||||
v_ego = self.x0[1]
|
||||
lead_one = radarstate.leadOne
|
||||
lead_two = radarstate.leadTwo
|
||||
self.status = (lead_one.status and tracking_lead) or lead_two.status
|
||||
|
||||
lead_xv_0 = self.process_lead(lead_one, tracking_lead)
|
||||
lead_xv_1 = self.process_lead(lead_two, True)
|
||||
|
||||
# To estimate a safe distance from a moving lead, we calculate how much stopping
|
||||
# distance that lead needs as a minimum. We can add that to the current distance
|
||||
@@ -363,7 +485,7 @@ class LongitudinalMpc:
|
||||
lead_1_obstacle = lead_xv_1[:,0] + get_stopped_equivalence_factor(lead_xv_1[:,1])
|
||||
|
||||
self.params[:,0] = ACCEL_MIN
|
||||
self.params[:,1] = ACCEL_MAX
|
||||
self.params[:,1] = max(0.0, self.max_a)
|
||||
|
||||
# Update in ACC mode or ACC/e2e blend
|
||||
if self.mode == 'acc':
|
||||
@@ -371,13 +493,13 @@ class LongitudinalMpc:
|
||||
|
||||
# Fake an obstacle for cruise, this ensures smooth acceleration to set speed
|
||||
# when the leads are no factor.
|
||||
v_lower = v_ego + (T_IDXS * CRUISE_MIN_ACCEL * 1.05)
|
||||
v_lower = v_ego + (T_IDXS * self.cruise_min_a * 1.05)
|
||||
# TODO does this make sense when max_a is negative?
|
||||
v_upper = v_ego + (T_IDXS * CRUISE_MAX_ACCEL * 1.05)
|
||||
v_upper = v_ego + (T_IDXS * self.max_a * 1.05)
|
||||
v_cruise_clipped = np.clip(v_cruise * np.ones(N+1),
|
||||
v_lower,
|
||||
v_upper)
|
||||
cruise_obstacle = np.cumsum(T_DIFFS * v_cruise_clipped) + get_safe_obstacle_distance(v_cruise_clipped, t_follow)
|
||||
cruise_obstacle = np.cumsum(T_DIFFS * v_cruise_clipped) + get_safe_obstacle_distance(v_cruise_clipped, t_follow, self.stop_distance)
|
||||
x_obstacles = np.column_stack([lead_0_obstacle, lead_1_obstacle, cruise_obstacle])
|
||||
self.source = SOURCES[np.argmin(x_obstacles[0])]
|
||||
|
||||
@@ -415,7 +537,7 @@ class LongitudinalMpc:
|
||||
|
||||
self.run()
|
||||
if (np.any(lead_xv_0[FCW_IDXS,0] - self.x_sol[FCW_IDXS,0] < CRASH_DISTANCE) and
|
||||
radarstate.leadOne.modelProb > 0.9):
|
||||
lead_one.modelProb > 0.9):
|
||||
self.crash_cnt += 1
|
||||
else:
|
||||
self.crash_cnt = 0
|
||||
@@ -423,9 +545,9 @@ class LongitudinalMpc:
|
||||
# Check if it got within lead comfort range
|
||||
# TODO This should be done cleaner
|
||||
if self.mode == 'blended':
|
||||
if any((lead_0_obstacle - get_safe_obstacle_distance(self.x_sol[:,1], t_follow))- self.x_sol[:,0] < 0.0):
|
||||
if any((lead_0_obstacle - get_safe_obstacle_distance(self.x_sol[:,1], t_follow, self.stop_distance))- self.x_sol[:,0] < 0.0):
|
||||
self.source = 'lead0'
|
||||
if any((lead_1_obstacle - get_safe_obstacle_distance(self.x_sol[:,1], t_follow))- self.x_sol[:,0] < 0.0) and \
|
||||
if any((lead_1_obstacle - get_safe_obstacle_distance(self.x_sol[:,1], t_follow, self.stop_distance))- self.x_sol[:,0] < 0.0) and \
|
||||
(lead_1_obstacle[0] - lead_0_obstacle[0]):
|
||||
self.source = 'lead1'
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
#!/usr/bin/env python3
|
||||
import math
|
||||
import time
|
||||
import numpy as np
|
||||
|
||||
import cereal.messaging as messaging
|
||||
@@ -12,7 +13,7 @@ from openpilot.selfdrive.controls.lib.longcontrol import LongCtrlState
|
||||
from openpilot.selfdrive.controls.lib.longitudinal_mpc_lib.long_mpc import LongitudinalMpc
|
||||
from openpilot.selfdrive.controls.lib.longitudinal_mpc_lib.long_mpc import T_IDXS as T_IDXS_MPC
|
||||
from openpilot.selfdrive.controls.lib.drive_helpers import CONTROL_N, get_accel_from_plan
|
||||
from openpilot.selfdrive.car.cruise import V_CRUISE_MAX, V_CRUISE_UNSET
|
||||
from openpilot.selfdrive.car.cruise import V_CRUISE_UNSET
|
||||
from openpilot.common.swaglog import cloudlog
|
||||
|
||||
from openpilot.frogpilot.common.frogpilot_variables import MINIMUM_LATERAL_ACCELERATION
|
||||
@@ -24,6 +25,10 @@ CONTROL_N_T_IDX = ModelConstants.T_IDXS[:CONTROL_N]
|
||||
ALLOW_THROTTLE_THRESHOLD = 0.4
|
||||
MIN_ALLOW_THROTTLE_SPEED = 2.5
|
||||
|
||||
# Uncertainty-based filter disable thresholds
|
||||
UNCERT_SLOPE_TRIG = 0.12 # per second
|
||||
UNCERT_MAG_TRIG = 0.50
|
||||
|
||||
# Lookup table for turns
|
||||
_A_TOTAL_MAX_V = [1.7, 3.2]
|
||||
_A_TOTAL_MAX_BP = [20., 40.]
|
||||
@@ -58,11 +63,11 @@ class LongitudinalPlanner:
|
||||
def __init__(self, CP, init_v=0.0, init_a=0.0, dt=DT_MDL):
|
||||
self.CP = CP
|
||||
self.mpc = LongitudinalMpc(dt=dt)
|
||||
# TODO remove mpc modes when TR released
|
||||
self.mpc.mode = 'acc'
|
||||
self.fcw = False
|
||||
self.dt = dt
|
||||
self.allow_throttle = True
|
||||
self.mode = 'acc'
|
||||
self.generation = None
|
||||
|
||||
self.a_desired = init_a
|
||||
self.v_desired_filter = FirstOrderFilter(init_v, 2.0, self.dt)
|
||||
@@ -75,13 +80,32 @@ class LongitudinalPlanner:
|
||||
self.j_desired_trajectory = np.zeros(CONTROL_N)
|
||||
self.solverExecutionTime = 0.0
|
||||
|
||||
self.uncert_slow = FirstOrderFilter(0.0, 1.6, self.dt)
|
||||
self.uncert_fast = FirstOrderFilter(0.0, 0.9, self.dt)
|
||||
self.prev_lead_dist = None
|
||||
self.last_big_brake_t = 0.0
|
||||
self.stable_lead = False
|
||||
self.lead_dist_f = None
|
||||
self._uncert_last = 0.0
|
||||
self._uncert_last_t = None
|
||||
|
||||
@property
|
||||
def mlsim(self):
|
||||
return self.generation in ("v8", "v10", "v11", "v12")
|
||||
|
||||
@staticmethod
|
||||
def parse_model(model_msg, v_ego, frogpilot_toggles):
|
||||
def get_model_speed_error(model_msg, v_ego):
|
||||
if len(model_msg.velocity.x) == ModelConstants.IDX_N:
|
||||
return float(np.clip(model_msg.velocity.x[0] - v_ego, -5.0, 5.0))
|
||||
return 0.0
|
||||
|
||||
@staticmethod
|
||||
def parse_model(model_msg, model_error, v_ego, frogpilot_toggles):
|
||||
if (len(model_msg.position.x) == ModelConstants.IDX_N and
|
||||
len(model_msg.velocity.x) == ModelConstants.IDX_N and
|
||||
len(model_msg.acceleration.x) == ModelConstants.IDX_N):
|
||||
x = np.interp(T_IDXS_MPC, ModelConstants.T_IDXS, model_msg.position.x)
|
||||
v = np.interp(T_IDXS_MPC, ModelConstants.T_IDXS, model_msg.velocity.x)
|
||||
x = np.interp(T_IDXS_MPC, ModelConstants.T_IDXS, model_msg.position.x) - model_error * T_IDXS_MPC
|
||||
v = np.interp(T_IDXS_MPC, ModelConstants.T_IDXS, model_msg.velocity.x) - model_error
|
||||
a = np.interp(T_IDXS_MPC, ModelConstants.T_IDXS, model_msg.acceleration.x)
|
||||
j = np.zeros(len(T_IDXS_MPC))
|
||||
else:
|
||||
@@ -104,14 +128,18 @@ class LongitudinalPlanner:
|
||||
return x, v, a, j, throttle_prob
|
||||
|
||||
def update(self, sm, frogpilot_toggles):
|
||||
mode = 'blended' if sm['selfdriveState'].experimentalMode else 'acc'
|
||||
self.generation = getattr(frogpilot_toggles, "model_version", None)
|
||||
self.mode = 'blended' if sm['selfdriveState'].experimentalMode else 'acc'
|
||||
self.mpc.mode = 'acc'
|
||||
if not self.mlsim:
|
||||
self.mpc.mode = self.mode
|
||||
|
||||
if len(sm['carControl'].orientationNED) == 3:
|
||||
accel_coast = get_coast_accel(sm['carControl'].orientationNED[1])
|
||||
else:
|
||||
accel_coast = ACCEL_MAX
|
||||
|
||||
v_ego = sm['carState'].vEgo
|
||||
v_ego = max(sm['carState'].vEgo, sm['carState'].vEgoCluster)
|
||||
v_cruise = sm['frogpilotPlan'].vCruise
|
||||
v_cruise_initialized = sm['carState'].vCruise != V_CRUISE_UNSET
|
||||
|
||||
@@ -126,7 +154,7 @@ class LongitudinalPlanner:
|
||||
# No change cost when user is controlling the speed, or when standstill
|
||||
prev_accel_constraint = not (reset_state or sm['carState'].standstill)
|
||||
|
||||
if mode == 'acc':
|
||||
if self.mpc.mode == 'acc':
|
||||
accel_clip = [sm['frogpilotPlan'].minAcceleration, sm['frogpilotPlan'].maxAcceleration]
|
||||
steer_angle_without_offset = sm['carState'].steeringAngleDeg - sm['liveParameters'].angleOffsetDeg
|
||||
if not sm['frogpilotPlan'].cscControllingSpeed:
|
||||
@@ -141,9 +169,11 @@ class LongitudinalPlanner:
|
||||
|
||||
# Prevent divergence, smooth in current v_ego
|
||||
self.v_desired_filter.x = max(0.0, self.v_desired_filter.update(v_ego))
|
||||
x, v, a, j, throttle_prob = self.parse_model(sm['modelV2'], v_ego, frogpilot_toggles)
|
||||
model_error = self.get_model_speed_error(sm['modelV2'], v_ego)
|
||||
x, v, a, j, throttle_prob = self.parse_model(sm['modelV2'], model_error, v_ego, frogpilot_toggles)
|
||||
# Don't clip at low speeds since throttle_prob doesn't account for creep
|
||||
self.allow_throttle = throttle_prob > ALLOW_THROTTLE_THRESHOLD or v_ego <= MIN_ALLOW_THROTTLE_SPEED
|
||||
self.allow_throttle &= not sm['frogpilotPlan'].disableThrottle
|
||||
|
||||
if not self.allow_throttle:
|
||||
clipped_accel_coast = max(accel_coast, accel_clip[0])
|
||||
@@ -153,9 +183,99 @@ class LongitudinalPlanner:
|
||||
if force_slow_decel:
|
||||
v_cruise = 0.0
|
||||
|
||||
self.mpc.set_weights(sm['frogpilotPlan'].accelerationJerk, sm['frogpilotPlan'].dangerJerk, sm['frogpilotPlan'].speedJerk, prev_accel_constraint, personality=sm['selfdriveState'].personality)
|
||||
accel_clip[0] = min(accel_clip[0], self.a_desired + 0.05)
|
||||
accel_clip[1] = max(accel_clip[1], self.a_desired - 0.05)
|
||||
|
||||
lead_one = sm['radarState'].leadOne
|
||||
lead_dist = lead_one.dRel if lead_one.status else 50.0
|
||||
|
||||
alpha = max(0.02, min(0.15, 0.05 + 0.002 * v_ego))
|
||||
if self.lead_dist_f is None:
|
||||
self.lead_dist_f = float(lead_dist)
|
||||
else:
|
||||
self.lead_dist_f += alpha * (float(lead_dist) - self.lead_dist_f)
|
||||
|
||||
now_t = time.monotonic()
|
||||
v_rel = (v_ego - lead_one.vLead) if lead_one.status else 0.0
|
||||
if self.prev_lead_dist is None:
|
||||
d_rel_dot = 0.0
|
||||
else:
|
||||
d_rel_dot = (lead_dist - self.prev_lead_dist) / max(self.dt, 1e-3)
|
||||
self.prev_lead_dist = lead_dist
|
||||
|
||||
uncertainty = 0.0
|
||||
raw_brake_max = 0.0
|
||||
if hasattr(sm['modelV2'], 'meta'):
|
||||
desire_entropy = 0.0
|
||||
if hasattr(sm['modelV2'].meta, 'desirePrediction'):
|
||||
desire_probs = sm['modelV2'].meta.desirePrediction
|
||||
if len(desire_probs) > 1:
|
||||
probs = np.asarray(desire_probs, dtype=float)
|
||||
total = float(np.sum(probs))
|
||||
if total > 1e-6:
|
||||
p = probs / total
|
||||
entropy = -np.sum(p * np.log(p + 1e-10))
|
||||
max_entropy = np.log(len(p))
|
||||
desire_entropy = float(entropy / max(max_entropy, 1e-6))
|
||||
|
||||
disengage_risk = 0.0
|
||||
if hasattr(sm['modelV2'].meta, 'disengagePredictions'):
|
||||
brake_probs = sm['modelV2'].meta.disengagePredictions.brakePressProbs
|
||||
if len(brake_probs) > 0:
|
||||
probs = np.asarray(brake_probs, dtype=float)
|
||||
if float(np.max(probs)) < 0.015:
|
||||
probs = probs * 0.5
|
||||
raw_brake_max = float(np.max(probs))
|
||||
t = np.arange(len(probs), dtype=float) * DT_MDL
|
||||
lam = 0.6
|
||||
weights = np.exp(-lam * t)
|
||||
disengage_risk = float(np.max(probs * weights))
|
||||
|
||||
raw_uncertainty = desire_entropy + disengage_risk
|
||||
self.uncert_slow.update(raw_uncertainty)
|
||||
self.uncert_fast.update(raw_uncertainty)
|
||||
uncertainty = self.uncert_slow.x
|
||||
|
||||
if raw_brake_max > 0.02:
|
||||
self.last_big_brake_t = now_t
|
||||
|
||||
recently_braked = (now_t - self.last_big_brake_t) < 0.7
|
||||
self.stable_lead = (
|
||||
lead_one.status and
|
||||
abs(v_rel) < 0.5 and
|
||||
abs(d_rel_dot) < 0.5 and
|
||||
not recently_braked
|
||||
)
|
||||
|
||||
if self._uncert_last_t is None:
|
||||
uncert_slope = 0.0
|
||||
else:
|
||||
dt_u = max(1e-3, now_t - self._uncert_last_t)
|
||||
uncert_slope = (uncertainty - self._uncert_last) / dt_u
|
||||
self._uncert_last = uncertainty
|
||||
self._uncert_last_t = now_t
|
||||
|
||||
closing_fast = lead_one.status and (v_ego - lead_one.vLead) > 0.5
|
||||
panic_bypass = closing_fast and (uncert_slope > UNCERT_SLOPE_TRIG or uncertainty >= UNCERT_MAG_TRIG)
|
||||
if panic_bypass:
|
||||
cloudlog.error(f"LON_SLOPE slope={uncert_slope:.3f} uncertainty={uncertainty:.3f} v_ego={v_ego:.2f}")
|
||||
|
||||
self.mpc.set_weights(sm['frogpilotPlan'].accelerationJerk,
|
||||
sm['frogpilotPlan'].dangerJerk,
|
||||
sm['frogpilotPlan'].speedJerk,
|
||||
prev_accel_constraint,
|
||||
personality=sm['selfdriveState'].personality,
|
||||
v_ego=v_ego,
|
||||
lead_dist=self.lead_dist_f if self.lead_dist_f is not None else lead_dist,
|
||||
uncertainty=uncertainty,
|
||||
panic_bypass=panic_bypass,
|
||||
stop_distance=getattr(frogpilot_toggles, "stop_distance", 6.0))
|
||||
self.mpc.set_accel_limits(accel_clip[0], accel_clip[1])
|
||||
self.mpc.set_cur_state(self.v_desired_filter.x, self.a_desired)
|
||||
self.mpc.update(sm['radarState'], v_cruise, x, v, a, j, sm['frogpilotPlan'].dangerFactor, sm['frogpilotPlan'].tFollow, personality=sm['selfdriveState'].personality)
|
||||
tracking_lead = sm['frogpilotPlan'].desiredFollowDistance > 0
|
||||
self.mpc.update(sm['radarState'], v_cruise, x, v, a, j,
|
||||
sm['frogpilotPlan'].dangerFactor, sm['frogpilotPlan'].tFollow,
|
||||
personality=sm['selfdriveState'].personality, tracking_lead=tracking_lead)
|
||||
|
||||
self.v_desired_trajectory = np.interp(CONTROL_N_T_IDX, T_IDXS_MPC, self.mpc.v_solution)
|
||||
self.a_desired_trajectory = np.interp(CONTROL_N_T_IDX, T_IDXS_MPC, self.mpc.a_solution)
|
||||
@@ -171,13 +291,27 @@ class LongitudinalPlanner:
|
||||
self.a_desired = float(np.interp(self.dt, CONTROL_N_T_IDX, self.a_desired_trajectory))
|
||||
self.v_desired_filter.x = self.v_desired_filter.x + self.dt * (self.a_desired + a_prev) / 2.0
|
||||
|
||||
if lead_one.status:
|
||||
rel_v = max(0.0, v_ego - lead_one.vLead)
|
||||
base_th = 1.6
|
||||
th = base_th + 0.6 * max(0.0, uncertainty - 0.42)
|
||||
desired_gap = th * v_ego
|
||||
if self.lead_dist_f is not None and self.lead_dist_f < desired_gap and rel_v > 0.5:
|
||||
k_rel, k_unc = 0.04, 0.20
|
||||
pre_brake = k_rel * rel_v + k_unc * max(0.0, uncertainty - 0.42)
|
||||
self.a_desired = float(self.a_desired - min(pre_brake, 0.06))
|
||||
|
||||
if -0.05 < self.a_desired < 0.05:
|
||||
self.a_desired = 0.0
|
||||
|
||||
action_t = frogpilot_toggles.longitudinalActuatorDelay + DT_MDL
|
||||
output_a_target_mpc, output_should_stop_mpc = get_accel_from_plan(self.v_desired_trajectory, self.a_desired_trajectory, CONTROL_N_T_IDX,
|
||||
action_t=action_t, vEgoStopping=frogpilot_toggles.vEgoStopping)
|
||||
output_a_target_e2e = sm['modelV2'].action.desiredAcceleration
|
||||
output_should_stop_e2e = sm['modelV2'].action.shouldStop
|
||||
|
||||
if mode == 'acc':
|
||||
# Keep StarPilot behavior: for tinygrad v10/v11/v12 in experimental mode, blend with model action output.
|
||||
if self.mode == 'acc' or self.generation == 'v9':
|
||||
output_a_target = output_a_target_mpc
|
||||
self.output_should_stop = output_should_stop_mpc
|
||||
else:
|
||||
@@ -208,7 +342,7 @@ class LongitudinalPlanner:
|
||||
longitudinalPlan.fcw = self.fcw
|
||||
|
||||
longitudinalPlan.aTarget = float(self.output_a_target)
|
||||
longitudinalPlan.shouldStop = bool(self.output_should_stop)
|
||||
longitudinalPlan.shouldStop = bool(self.output_should_stop) or sm['frogpilotPlan'].forcingStopLength < 1
|
||||
longitudinalPlan.allowBrake = True
|
||||
longitudinalPlan.allowThrottle = bool(self.allow_throttle)
|
||||
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
from parameterized import parameterized
|
||||
from types import SimpleNamespace
|
||||
|
||||
from cereal import car, log
|
||||
from opendbc.car.car_helpers import interfaces
|
||||
@@ -16,7 +17,7 @@ from openpilot.selfdrive.controls.lib.latcontrol_angle import LatControlAngle
|
||||
class TestLatControl:
|
||||
|
||||
@parameterized.expand([(HONDA.HONDA_CIVIC, LatControlPID), (TOYOTA.TOYOTA_RAV4, LatControlTorque),
|
||||
(NISSAN.NISSAN_LEAF, LatControlAngle), (GM.CHEVROLET_BOLT_EUV, LatControlTorque)])
|
||||
(NISSAN.NISSAN_LEAF, LatControlAngle), (GM.CHEVROLET_BOLT_ACC_2022_2023, LatControlTorque)])
|
||||
def test_saturation(self, car_name, controller):
|
||||
CarInterface = interfaces[car_name]
|
||||
CP = CarInterface.get_non_essential_params(car_name)
|
||||
@@ -30,16 +31,17 @@ class TestLatControl:
|
||||
CS.steeringPressed = False
|
||||
|
||||
params = log.LiveParametersData.new_message()
|
||||
frogpilot_toggles = SimpleNamespace()
|
||||
|
||||
# Saturate for curvature limited and controller limited
|
||||
for _ in range(1000):
|
||||
_, _, lac_log = controller.update(True, CS, VM, params, False, 0, True, 0.2)
|
||||
_, _, lac_log = controller.update(True, CS, VM, params, False, 0, True, 0.2, None, None, frogpilot_toggles)
|
||||
assert lac_log.saturated
|
||||
|
||||
for _ in range(1000):
|
||||
_, _, lac_log = controller.update(True, CS, VM, params, False, 0, False, 0.2)
|
||||
_, _, lac_log = controller.update(True, CS, VM, params, False, 0, False, 0.2, None, None, frogpilot_toggles)
|
||||
assert not lac_log.saturated
|
||||
|
||||
for _ in range(1000):
|
||||
_, _, lac_log = controller.update(True, CS, VM, params, False, 1, False, 0.2)
|
||||
_, _, lac_log = controller.update(True, CS, VM, params, False, 1, False, 0.2, None, None, frogpilot_toggles)
|
||||
assert lac_log.saturated
|
||||
|
||||
@@ -0,0 +1,66 @@
|
||||
#!/usr/bin/env python3
|
||||
import argparse
|
||||
import time
|
||||
|
||||
from cereal import log, messaging
|
||||
from openpilot.system.hardware import HARDWARE
|
||||
|
||||
NETWORK_TYPES = {
|
||||
"none": log.DeviceState.NetworkType.none,
|
||||
"wifi": log.DeviceState.NetworkType.wifi,
|
||||
"ethernet": log.DeviceState.NetworkType.ethernet,
|
||||
"cell2G": log.DeviceState.NetworkType.cell2G,
|
||||
"cell3G": log.DeviceState.NetworkType.cell3G,
|
||||
"cell4G": log.DeviceState.NetworkType.cell4G,
|
||||
"cell5G": log.DeviceState.NetworkType.cell5G,
|
||||
}
|
||||
|
||||
NETWORK_STRENGTHS = {
|
||||
"unknown": log.DeviceState.NetworkStrength.unknown,
|
||||
"poor": log.DeviceState.NetworkStrength.poor,
|
||||
"moderate": log.DeviceState.NetworkStrength.moderate,
|
||||
"good": log.DeviceState.NetworkStrength.good,
|
||||
"great": log.DeviceState.NetworkStrength.great,
|
||||
}
|
||||
|
||||
|
||||
def parse_args() -> argparse.Namespace:
|
||||
parser = argparse.ArgumentParser(description="Publish fake deviceState network info for desktop UI demos.")
|
||||
parser.add_argument("--network", choices=NETWORK_TYPES.keys(), default="wifi", help="network type to publish")
|
||||
parser.add_argument("--strength", choices=NETWORK_STRENGTHS.keys(), default="great", help="network strength to publish")
|
||||
parser.add_argument("--started", action="store_true", help="publish started=true (onroad)")
|
||||
parser.add_argument("--metered", action="store_true", help="publish networkMetered=true")
|
||||
parser.add_argument("--interval", type=float, default=0.2, help="publish interval in seconds")
|
||||
return parser.parse_args()
|
||||
|
||||
|
||||
def main() -> None:
|
||||
args = parse_args()
|
||||
|
||||
pm = messaging.PubMaster(["deviceState"])
|
||||
interval = max(args.interval, 0.05)
|
||||
network_type = NETWORK_TYPES[args.network]
|
||||
network_strength = NETWORK_STRENGTHS[args.strength]
|
||||
|
||||
while True:
|
||||
msg = messaging.new_message("deviceState")
|
||||
ds = msg.deviceState
|
||||
|
||||
ds.deviceType = HARDWARE.get_device_type()
|
||||
ds.started = args.started
|
||||
ds.networkType = network_type
|
||||
ds.networkStrength = network_strength
|
||||
ds.networkMetered = args.metered
|
||||
ds.lastAthenaPingTime = time.monotonic_ns()
|
||||
|
||||
# Keep common sidebar values reasonable for desktop demos.
|
||||
ds.freeSpacePercent = 80
|
||||
ds.memoryUsagePercent = 35
|
||||
ds.maxTempC = 45
|
||||
|
||||
pm.send("deviceState", msg)
|
||||
time.sleep(interval)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -1 +1,9 @@
|
||||
generated/
|
||||
generated/*
|
||||
!generated/car.cpp
|
||||
!generated/car.h
|
||||
!generated/pose.cpp
|
||||
!generated/pose.h
|
||||
!generated/libcar.so
|
||||
!generated/libpose.so
|
||||
!generated/car.os
|
||||
!generated/pose.os
|
||||
|
||||
@@ -0,0 +1,661 @@
|
||||
#include "car.h"
|
||||
|
||||
namespace {
|
||||
#define DIM 9
|
||||
#define EDIM 9
|
||||
#define MEDIM 9
|
||||
typedef void (*Hfun)(double *, double *, double *);
|
||||
|
||||
double mass;
|
||||
|
||||
void set_mass(double x){ mass = x;}
|
||||
|
||||
double rotational_inertia;
|
||||
|
||||
void set_rotational_inertia(double x){ rotational_inertia = x;}
|
||||
|
||||
double center_to_front;
|
||||
|
||||
void set_center_to_front(double x){ center_to_front = x;}
|
||||
|
||||
double center_to_rear;
|
||||
|
||||
void set_center_to_rear(double x){ center_to_rear = x;}
|
||||
|
||||
double stiffness_front;
|
||||
|
||||
void set_stiffness_front(double x){ stiffness_front = x;}
|
||||
|
||||
double stiffness_rear;
|
||||
|
||||
void set_stiffness_rear(double x){ stiffness_rear = x;}
|
||||
const static double MAHA_THRESH_25 = 3.8414588206941227;
|
||||
const static double MAHA_THRESH_24 = 5.991464547107981;
|
||||
const static double MAHA_THRESH_30 = 3.8414588206941227;
|
||||
const static double MAHA_THRESH_26 = 3.8414588206941227;
|
||||
const static double MAHA_THRESH_27 = 3.8414588206941227;
|
||||
const static double MAHA_THRESH_29 = 3.8414588206941227;
|
||||
const static double MAHA_THRESH_28 = 3.8414588206941227;
|
||||
const static double MAHA_THRESH_31 = 3.8414588206941227;
|
||||
|
||||
/******************************************************************************
|
||||
* Code generated with SymPy 1.14.0 *
|
||||
* *
|
||||
* See http://www.sympy.org/ for more information. *
|
||||
* *
|
||||
* This file is part of 'ekf' *
|
||||
******************************************************************************/
|
||||
void err_fun(double *nom_x, double *delta_x, double *out_2689391300508487509) {
|
||||
out_2689391300508487509[0] = delta_x[0] + nom_x[0];
|
||||
out_2689391300508487509[1] = delta_x[1] + nom_x[1];
|
||||
out_2689391300508487509[2] = delta_x[2] + nom_x[2];
|
||||
out_2689391300508487509[3] = delta_x[3] + nom_x[3];
|
||||
out_2689391300508487509[4] = delta_x[4] + nom_x[4];
|
||||
out_2689391300508487509[5] = delta_x[5] + nom_x[5];
|
||||
out_2689391300508487509[6] = delta_x[6] + nom_x[6];
|
||||
out_2689391300508487509[7] = delta_x[7] + nom_x[7];
|
||||
out_2689391300508487509[8] = delta_x[8] + nom_x[8];
|
||||
}
|
||||
void inv_err_fun(double *nom_x, double *true_x, double *out_7177783815062799236) {
|
||||
out_7177783815062799236[0] = -nom_x[0] + true_x[0];
|
||||
out_7177783815062799236[1] = -nom_x[1] + true_x[1];
|
||||
out_7177783815062799236[2] = -nom_x[2] + true_x[2];
|
||||
out_7177783815062799236[3] = -nom_x[3] + true_x[3];
|
||||
out_7177783815062799236[4] = -nom_x[4] + true_x[4];
|
||||
out_7177783815062799236[5] = -nom_x[5] + true_x[5];
|
||||
out_7177783815062799236[6] = -nom_x[6] + true_x[6];
|
||||
out_7177783815062799236[7] = -nom_x[7] + true_x[7];
|
||||
out_7177783815062799236[8] = -nom_x[8] + true_x[8];
|
||||
}
|
||||
void H_mod_fun(double *state, double *out_8221602802744378233) {
|
||||
out_8221602802744378233[0] = 1.0;
|
||||
out_8221602802744378233[1] = 0.0;
|
||||
out_8221602802744378233[2] = 0.0;
|
||||
out_8221602802744378233[3] = 0.0;
|
||||
out_8221602802744378233[4] = 0.0;
|
||||
out_8221602802744378233[5] = 0.0;
|
||||
out_8221602802744378233[6] = 0.0;
|
||||
out_8221602802744378233[7] = 0.0;
|
||||
out_8221602802744378233[8] = 0.0;
|
||||
out_8221602802744378233[9] = 0.0;
|
||||
out_8221602802744378233[10] = 1.0;
|
||||
out_8221602802744378233[11] = 0.0;
|
||||
out_8221602802744378233[12] = 0.0;
|
||||
out_8221602802744378233[13] = 0.0;
|
||||
out_8221602802744378233[14] = 0.0;
|
||||
out_8221602802744378233[15] = 0.0;
|
||||
out_8221602802744378233[16] = 0.0;
|
||||
out_8221602802744378233[17] = 0.0;
|
||||
out_8221602802744378233[18] = 0.0;
|
||||
out_8221602802744378233[19] = 0.0;
|
||||
out_8221602802744378233[20] = 1.0;
|
||||
out_8221602802744378233[21] = 0.0;
|
||||
out_8221602802744378233[22] = 0.0;
|
||||
out_8221602802744378233[23] = 0.0;
|
||||
out_8221602802744378233[24] = 0.0;
|
||||
out_8221602802744378233[25] = 0.0;
|
||||
out_8221602802744378233[26] = 0.0;
|
||||
out_8221602802744378233[27] = 0.0;
|
||||
out_8221602802744378233[28] = 0.0;
|
||||
out_8221602802744378233[29] = 0.0;
|
||||
out_8221602802744378233[30] = 1.0;
|
||||
out_8221602802744378233[31] = 0.0;
|
||||
out_8221602802744378233[32] = 0.0;
|
||||
out_8221602802744378233[33] = 0.0;
|
||||
out_8221602802744378233[34] = 0.0;
|
||||
out_8221602802744378233[35] = 0.0;
|
||||
out_8221602802744378233[36] = 0.0;
|
||||
out_8221602802744378233[37] = 0.0;
|
||||
out_8221602802744378233[38] = 0.0;
|
||||
out_8221602802744378233[39] = 0.0;
|
||||
out_8221602802744378233[40] = 1.0;
|
||||
out_8221602802744378233[41] = 0.0;
|
||||
out_8221602802744378233[42] = 0.0;
|
||||
out_8221602802744378233[43] = 0.0;
|
||||
out_8221602802744378233[44] = 0.0;
|
||||
out_8221602802744378233[45] = 0.0;
|
||||
out_8221602802744378233[46] = 0.0;
|
||||
out_8221602802744378233[47] = 0.0;
|
||||
out_8221602802744378233[48] = 0.0;
|
||||
out_8221602802744378233[49] = 0.0;
|
||||
out_8221602802744378233[50] = 1.0;
|
||||
out_8221602802744378233[51] = 0.0;
|
||||
out_8221602802744378233[52] = 0.0;
|
||||
out_8221602802744378233[53] = 0.0;
|
||||
out_8221602802744378233[54] = 0.0;
|
||||
out_8221602802744378233[55] = 0.0;
|
||||
out_8221602802744378233[56] = 0.0;
|
||||
out_8221602802744378233[57] = 0.0;
|
||||
out_8221602802744378233[58] = 0.0;
|
||||
out_8221602802744378233[59] = 0.0;
|
||||
out_8221602802744378233[60] = 1.0;
|
||||
out_8221602802744378233[61] = 0.0;
|
||||
out_8221602802744378233[62] = 0.0;
|
||||
out_8221602802744378233[63] = 0.0;
|
||||
out_8221602802744378233[64] = 0.0;
|
||||
out_8221602802744378233[65] = 0.0;
|
||||
out_8221602802744378233[66] = 0.0;
|
||||
out_8221602802744378233[67] = 0.0;
|
||||
out_8221602802744378233[68] = 0.0;
|
||||
out_8221602802744378233[69] = 0.0;
|
||||
out_8221602802744378233[70] = 1.0;
|
||||
out_8221602802744378233[71] = 0.0;
|
||||
out_8221602802744378233[72] = 0.0;
|
||||
out_8221602802744378233[73] = 0.0;
|
||||
out_8221602802744378233[74] = 0.0;
|
||||
out_8221602802744378233[75] = 0.0;
|
||||
out_8221602802744378233[76] = 0.0;
|
||||
out_8221602802744378233[77] = 0.0;
|
||||
out_8221602802744378233[78] = 0.0;
|
||||
out_8221602802744378233[79] = 0.0;
|
||||
out_8221602802744378233[80] = 1.0;
|
||||
}
|
||||
void f_fun(double *state, double dt, double *out_7651705913211861388) {
|
||||
out_7651705913211861388[0] = state[0];
|
||||
out_7651705913211861388[1] = state[1];
|
||||
out_7651705913211861388[2] = state[2];
|
||||
out_7651705913211861388[3] = state[3];
|
||||
out_7651705913211861388[4] = state[4];
|
||||
out_7651705913211861388[5] = dt*((-state[4] + (-center_to_front*stiffness_front*state[0] + center_to_rear*stiffness_rear*state[0])/(mass*state[4]))*state[6] - 9.8100000000000005*state[8] + stiffness_front*(-state[2] - state[3] + state[7])*state[0]/(mass*state[1]) + (-stiffness_front*state[0] - stiffness_rear*state[0])*state[5]/(mass*state[4])) + state[5];
|
||||
out_7651705913211861388[6] = dt*(center_to_front*stiffness_front*(-state[2] - state[3] + state[7])*state[0]/(rotational_inertia*state[1]) + (-center_to_front*stiffness_front*state[0] + center_to_rear*stiffness_rear*state[0])*state[5]/(rotational_inertia*state[4]) + (-pow(center_to_front, 2)*stiffness_front*state[0] - pow(center_to_rear, 2)*stiffness_rear*state[0])*state[6]/(rotational_inertia*state[4])) + state[6];
|
||||
out_7651705913211861388[7] = state[7];
|
||||
out_7651705913211861388[8] = state[8];
|
||||
}
|
||||
void F_fun(double *state, double dt, double *out_97859790539154602) {
|
||||
out_97859790539154602[0] = 1;
|
||||
out_97859790539154602[1] = 0;
|
||||
out_97859790539154602[2] = 0;
|
||||
out_97859790539154602[3] = 0;
|
||||
out_97859790539154602[4] = 0;
|
||||
out_97859790539154602[5] = 0;
|
||||
out_97859790539154602[6] = 0;
|
||||
out_97859790539154602[7] = 0;
|
||||
out_97859790539154602[8] = 0;
|
||||
out_97859790539154602[9] = 0;
|
||||
out_97859790539154602[10] = 1;
|
||||
out_97859790539154602[11] = 0;
|
||||
out_97859790539154602[12] = 0;
|
||||
out_97859790539154602[13] = 0;
|
||||
out_97859790539154602[14] = 0;
|
||||
out_97859790539154602[15] = 0;
|
||||
out_97859790539154602[16] = 0;
|
||||
out_97859790539154602[17] = 0;
|
||||
out_97859790539154602[18] = 0;
|
||||
out_97859790539154602[19] = 0;
|
||||
out_97859790539154602[20] = 1;
|
||||
out_97859790539154602[21] = 0;
|
||||
out_97859790539154602[22] = 0;
|
||||
out_97859790539154602[23] = 0;
|
||||
out_97859790539154602[24] = 0;
|
||||
out_97859790539154602[25] = 0;
|
||||
out_97859790539154602[26] = 0;
|
||||
out_97859790539154602[27] = 0;
|
||||
out_97859790539154602[28] = 0;
|
||||
out_97859790539154602[29] = 0;
|
||||
out_97859790539154602[30] = 1;
|
||||
out_97859790539154602[31] = 0;
|
||||
out_97859790539154602[32] = 0;
|
||||
out_97859790539154602[33] = 0;
|
||||
out_97859790539154602[34] = 0;
|
||||
out_97859790539154602[35] = 0;
|
||||
out_97859790539154602[36] = 0;
|
||||
out_97859790539154602[37] = 0;
|
||||
out_97859790539154602[38] = 0;
|
||||
out_97859790539154602[39] = 0;
|
||||
out_97859790539154602[40] = 1;
|
||||
out_97859790539154602[41] = 0;
|
||||
out_97859790539154602[42] = 0;
|
||||
out_97859790539154602[43] = 0;
|
||||
out_97859790539154602[44] = 0;
|
||||
out_97859790539154602[45] = dt*(stiffness_front*(-state[2] - state[3] + state[7])/(mass*state[1]) + (-stiffness_front - stiffness_rear)*state[5]/(mass*state[4]) + (-center_to_front*stiffness_front + center_to_rear*stiffness_rear)*state[6]/(mass*state[4]));
|
||||
out_97859790539154602[46] = -dt*stiffness_front*(-state[2] - state[3] + state[7])*state[0]/(mass*pow(state[1], 2));
|
||||
out_97859790539154602[47] = -dt*stiffness_front*state[0]/(mass*state[1]);
|
||||
out_97859790539154602[48] = -dt*stiffness_front*state[0]/(mass*state[1]);
|
||||
out_97859790539154602[49] = dt*((-1 - (-center_to_front*stiffness_front*state[0] + center_to_rear*stiffness_rear*state[0])/(mass*pow(state[4], 2)))*state[6] - (-stiffness_front*state[0] - stiffness_rear*state[0])*state[5]/(mass*pow(state[4], 2)));
|
||||
out_97859790539154602[50] = dt*(-stiffness_front*state[0] - stiffness_rear*state[0])/(mass*state[4]) + 1;
|
||||
out_97859790539154602[51] = dt*(-state[4] + (-center_to_front*stiffness_front*state[0] + center_to_rear*stiffness_rear*state[0])/(mass*state[4]));
|
||||
out_97859790539154602[52] = dt*stiffness_front*state[0]/(mass*state[1]);
|
||||
out_97859790539154602[53] = -9.8100000000000005*dt;
|
||||
out_97859790539154602[54] = dt*(center_to_front*stiffness_front*(-state[2] - state[3] + state[7])/(rotational_inertia*state[1]) + (-center_to_front*stiffness_front + center_to_rear*stiffness_rear)*state[5]/(rotational_inertia*state[4]) + (-pow(center_to_front, 2)*stiffness_front - pow(center_to_rear, 2)*stiffness_rear)*state[6]/(rotational_inertia*state[4]));
|
||||
out_97859790539154602[55] = -center_to_front*dt*stiffness_front*(-state[2] - state[3] + state[7])*state[0]/(rotational_inertia*pow(state[1], 2));
|
||||
out_97859790539154602[56] = -center_to_front*dt*stiffness_front*state[0]/(rotational_inertia*state[1]);
|
||||
out_97859790539154602[57] = -center_to_front*dt*stiffness_front*state[0]/(rotational_inertia*state[1]);
|
||||
out_97859790539154602[58] = dt*(-(-center_to_front*stiffness_front*state[0] + center_to_rear*stiffness_rear*state[0])*state[5]/(rotational_inertia*pow(state[4], 2)) - (-pow(center_to_front, 2)*stiffness_front*state[0] - pow(center_to_rear, 2)*stiffness_rear*state[0])*state[6]/(rotational_inertia*pow(state[4], 2)));
|
||||
out_97859790539154602[59] = dt*(-center_to_front*stiffness_front*state[0] + center_to_rear*stiffness_rear*state[0])/(rotational_inertia*state[4]);
|
||||
out_97859790539154602[60] = dt*(-pow(center_to_front, 2)*stiffness_front*state[0] - pow(center_to_rear, 2)*stiffness_rear*state[0])/(rotational_inertia*state[4]) + 1;
|
||||
out_97859790539154602[61] = center_to_front*dt*stiffness_front*state[0]/(rotational_inertia*state[1]);
|
||||
out_97859790539154602[62] = 0;
|
||||
out_97859790539154602[63] = 0;
|
||||
out_97859790539154602[64] = 0;
|
||||
out_97859790539154602[65] = 0;
|
||||
out_97859790539154602[66] = 0;
|
||||
out_97859790539154602[67] = 0;
|
||||
out_97859790539154602[68] = 0;
|
||||
out_97859790539154602[69] = 0;
|
||||
out_97859790539154602[70] = 1;
|
||||
out_97859790539154602[71] = 0;
|
||||
out_97859790539154602[72] = 0;
|
||||
out_97859790539154602[73] = 0;
|
||||
out_97859790539154602[74] = 0;
|
||||
out_97859790539154602[75] = 0;
|
||||
out_97859790539154602[76] = 0;
|
||||
out_97859790539154602[77] = 0;
|
||||
out_97859790539154602[78] = 0;
|
||||
out_97859790539154602[79] = 0;
|
||||
out_97859790539154602[80] = 1;
|
||||
}
|
||||
void h_25(double *state, double *unused, double *out_1747255377487496462) {
|
||||
out_1747255377487496462[0] = state[6];
|
||||
}
|
||||
void H_25(double *state, double *unused, double *out_7588584345949747189) {
|
||||
out_7588584345949747189[0] = 0;
|
||||
out_7588584345949747189[1] = 0;
|
||||
out_7588584345949747189[2] = 0;
|
||||
out_7588584345949747189[3] = 0;
|
||||
out_7588584345949747189[4] = 0;
|
||||
out_7588584345949747189[5] = 0;
|
||||
out_7588584345949747189[6] = 1;
|
||||
out_7588584345949747189[7] = 0;
|
||||
out_7588584345949747189[8] = 0;
|
||||
}
|
||||
void h_24(double *state, double *unused, double *out_3070396560078381804) {
|
||||
out_3070396560078381804[0] = state[4];
|
||||
out_3070396560078381804[1] = state[5];
|
||||
}
|
||||
void H_24(double *state, double *unused, double *out_7300234128957481291) {
|
||||
out_7300234128957481291[0] = 0;
|
||||
out_7300234128957481291[1] = 0;
|
||||
out_7300234128957481291[2] = 0;
|
||||
out_7300234128957481291[3] = 0;
|
||||
out_7300234128957481291[4] = 1;
|
||||
out_7300234128957481291[5] = 0;
|
||||
out_7300234128957481291[6] = 0;
|
||||
out_7300234128957481291[7] = 0;
|
||||
out_7300234128957481291[8] = 0;
|
||||
out_7300234128957481291[9] = 0;
|
||||
out_7300234128957481291[10] = 0;
|
||||
out_7300234128957481291[11] = 0;
|
||||
out_7300234128957481291[12] = 0;
|
||||
out_7300234128957481291[13] = 0;
|
||||
out_7300234128957481291[14] = 1;
|
||||
out_7300234128957481291[15] = 0;
|
||||
out_7300234128957481291[16] = 0;
|
||||
out_7300234128957481291[17] = 0;
|
||||
}
|
||||
void h_30(double *state, double *unused, double *out_2594165583737162996) {
|
||||
out_2594165583737162996[0] = state[4];
|
||||
}
|
||||
void H_30(double *state, double *unused, double *out_8339826769252555800) {
|
||||
out_8339826769252555800[0] = 0;
|
||||
out_8339826769252555800[1] = 0;
|
||||
out_8339826769252555800[2] = 0;
|
||||
out_8339826769252555800[3] = 0;
|
||||
out_8339826769252555800[4] = 1;
|
||||
out_8339826769252555800[5] = 0;
|
||||
out_8339826769252555800[6] = 0;
|
||||
out_8339826769252555800[7] = 0;
|
||||
out_8339826769252555800[8] = 0;
|
||||
}
|
||||
void h_26(double *state, double *unused, double *out_1234538052727793168) {
|
||||
out_1234538052727793168[0] = state[7];
|
||||
}
|
||||
void H_26(double *state, double *unused, double *out_3847081027075690965) {
|
||||
out_3847081027075690965[0] = 0;
|
||||
out_3847081027075690965[1] = 0;
|
||||
out_3847081027075690965[2] = 0;
|
||||
out_3847081027075690965[3] = 0;
|
||||
out_3847081027075690965[4] = 0;
|
||||
out_3847081027075690965[5] = 0;
|
||||
out_3847081027075690965[6] = 0;
|
||||
out_3847081027075690965[7] = 1;
|
||||
out_3847081027075690965[8] = 0;
|
||||
}
|
||||
void h_27(double *state, double *unused, double *out_4784041428931829147) {
|
||||
out_4784041428931829147[0] = state[3];
|
||||
}
|
||||
void H_27(double *state, double *unused, double *out_886124704021714080) {
|
||||
out_886124704021714080[0] = 0;
|
||||
out_886124704021714080[1] = 0;
|
||||
out_886124704021714080[2] = 0;
|
||||
out_886124704021714080[3] = 1;
|
||||
out_886124704021714080[4] = 0;
|
||||
out_886124704021714080[5] = 0;
|
||||
out_886124704021714080[6] = 0;
|
||||
out_886124704021714080[7] = 0;
|
||||
out_886124704021714080[8] = 0;
|
||||
}
|
||||
void h_29(double *state, double *unused, double *out_2407507683923682649) {
|
||||
out_2407507683923682649[0] = state[1];
|
||||
}
|
||||
void H_29(double *state, double *unused, double *out_3571119360136531175) {
|
||||
out_3571119360136531175[0] = 0;
|
||||
out_3571119360136531175[1] = 1;
|
||||
out_3571119360136531175[2] = 0;
|
||||
out_3571119360136531175[3] = 0;
|
||||
out_3571119360136531175[4] = 0;
|
||||
out_3571119360136531175[5] = 0;
|
||||
out_3571119360136531175[6] = 0;
|
||||
out_3571119360136531175[7] = 0;
|
||||
out_3571119360136531175[8] = 0;
|
||||
}
|
||||
void h_28(double *state, double *unused, double *out_7679778805471580716) {
|
||||
out_7679778805471580716[0] = state[0];
|
||||
}
|
||||
void H_28(double *state, double *unused, double *out_5534749631701857426) {
|
||||
out_5534749631701857426[0] = 1;
|
||||
out_5534749631701857426[1] = 0;
|
||||
out_5534749631701857426[2] = 0;
|
||||
out_5534749631701857426[3] = 0;
|
||||
out_5534749631701857426[4] = 0;
|
||||
out_5534749631701857426[5] = 0;
|
||||
out_5534749631701857426[6] = 0;
|
||||
out_5534749631701857426[7] = 0;
|
||||
out_5534749631701857426[8] = 0;
|
||||
}
|
||||
void h_31(double *state, double *unused, double *out_5023579848862854474) {
|
||||
out_5023579848862854474[0] = state[8];
|
||||
}
|
||||
void H_31(double *state, double *unused, double *out_3220872924842339489) {
|
||||
out_3220872924842339489[0] = 0;
|
||||
out_3220872924842339489[1] = 0;
|
||||
out_3220872924842339489[2] = 0;
|
||||
out_3220872924842339489[3] = 0;
|
||||
out_3220872924842339489[4] = 0;
|
||||
out_3220872924842339489[5] = 0;
|
||||
out_3220872924842339489[6] = 0;
|
||||
out_3220872924842339489[7] = 0;
|
||||
out_3220872924842339489[8] = 1;
|
||||
}
|
||||
#include <eigen3/Eigen/Dense>
|
||||
#include <iostream>
|
||||
|
||||
typedef Eigen::Matrix<double, DIM, DIM, Eigen::RowMajor> DDM;
|
||||
typedef Eigen::Matrix<double, EDIM, EDIM, Eigen::RowMajor> EEM;
|
||||
typedef Eigen::Matrix<double, DIM, EDIM, Eigen::RowMajor> DEM;
|
||||
|
||||
void predict(double *in_x, double *in_P, double *in_Q, double dt) {
|
||||
typedef Eigen::Matrix<double, MEDIM, MEDIM, Eigen::RowMajor> RRM;
|
||||
|
||||
double nx[DIM] = {0};
|
||||
double in_F[EDIM*EDIM] = {0};
|
||||
|
||||
// functions from sympy
|
||||
f_fun(in_x, dt, nx);
|
||||
F_fun(in_x, dt, in_F);
|
||||
|
||||
|
||||
EEM F(in_F);
|
||||
EEM P(in_P);
|
||||
EEM Q(in_Q);
|
||||
|
||||
RRM F_main = F.topLeftCorner(MEDIM, MEDIM);
|
||||
P.topLeftCorner(MEDIM, MEDIM) = (F_main * P.topLeftCorner(MEDIM, MEDIM)) * F_main.transpose();
|
||||
P.topRightCorner(MEDIM, EDIM - MEDIM) = F_main * P.topRightCorner(MEDIM, EDIM - MEDIM);
|
||||
P.bottomLeftCorner(EDIM - MEDIM, MEDIM) = P.bottomLeftCorner(EDIM - MEDIM, MEDIM) * F_main.transpose();
|
||||
|
||||
P = P + dt*Q;
|
||||
|
||||
// copy out state
|
||||
memcpy(in_x, nx, DIM * sizeof(double));
|
||||
memcpy(in_P, P.data(), EDIM * EDIM * sizeof(double));
|
||||
}
|
||||
|
||||
// note: extra_args dim only correct when null space projecting
|
||||
// otherwise 1
|
||||
template <int ZDIM, int EADIM, bool MAHA_TEST>
|
||||
void update(double *in_x, double *in_P, Hfun h_fun, Hfun H_fun, Hfun Hea_fun, double *in_z, double *in_R, double *in_ea, double MAHA_THRESHOLD) {
|
||||
typedef Eigen::Matrix<double, ZDIM, ZDIM, Eigen::RowMajor> ZZM;
|
||||
typedef Eigen::Matrix<double, ZDIM, DIM, Eigen::RowMajor> ZDM;
|
||||
typedef Eigen::Matrix<double, Eigen::Dynamic, EDIM, Eigen::RowMajor> XEM;
|
||||
//typedef Eigen::Matrix<double, EDIM, ZDIM, Eigen::RowMajor> EZM;
|
||||
typedef Eigen::Matrix<double, Eigen::Dynamic, 1> X1M;
|
||||
typedef Eigen::Matrix<double, Eigen::Dynamic, Eigen::Dynamic, Eigen::RowMajor> XXM;
|
||||
|
||||
double in_hx[ZDIM] = {0};
|
||||
double in_H[ZDIM * DIM] = {0};
|
||||
double in_H_mod[EDIM * DIM] = {0};
|
||||
double delta_x[EDIM] = {0};
|
||||
double x_new[DIM] = {0};
|
||||
|
||||
|
||||
// state x, P
|
||||
Eigen::Matrix<double, ZDIM, 1> z(in_z);
|
||||
EEM P(in_P);
|
||||
ZZM pre_R(in_R);
|
||||
|
||||
// functions from sympy
|
||||
h_fun(in_x, in_ea, in_hx);
|
||||
H_fun(in_x, in_ea, in_H);
|
||||
ZDM pre_H(in_H);
|
||||
|
||||
// get y (y = z - hx)
|
||||
Eigen::Matrix<double, ZDIM, 1> pre_y(in_hx); pre_y = z - pre_y;
|
||||
X1M y; XXM H; XXM R;
|
||||
if (Hea_fun){
|
||||
typedef Eigen::Matrix<double, ZDIM, EADIM, Eigen::RowMajor> ZAM;
|
||||
double in_Hea[ZDIM * EADIM] = {0};
|
||||
Hea_fun(in_x, in_ea, in_Hea);
|
||||
ZAM Hea(in_Hea);
|
||||
XXM A = Hea.transpose().fullPivLu().kernel();
|
||||
|
||||
|
||||
y = A.transpose() * pre_y;
|
||||
H = A.transpose() * pre_H;
|
||||
R = A.transpose() * pre_R * A;
|
||||
} else {
|
||||
y = pre_y;
|
||||
H = pre_H;
|
||||
R = pre_R;
|
||||
}
|
||||
// get modified H
|
||||
H_mod_fun(in_x, in_H_mod);
|
||||
DEM H_mod(in_H_mod);
|
||||
XEM H_err = H * H_mod;
|
||||
|
||||
// Do mahalobis distance test
|
||||
if (MAHA_TEST){
|
||||
XXM a = (H_err * P * H_err.transpose() + R).inverse();
|
||||
double maha_dist = y.transpose() * a * y;
|
||||
if (maha_dist > MAHA_THRESHOLD){
|
||||
R = 1.0e16 * R;
|
||||
}
|
||||
}
|
||||
|
||||
// Outlier resilient weighting
|
||||
double weight = 1;//(1.5)/(1 + y.squaredNorm()/R.sum());
|
||||
|
||||
// kalman gains and I_KH
|
||||
XXM S = ((H_err * P) * H_err.transpose()) + R/weight;
|
||||
XEM KT = S.fullPivLu().solve(H_err * P.transpose());
|
||||
//EZM K = KT.transpose(); TODO: WHY DOES THIS NOT COMPILE?
|
||||
//EZM K = S.fullPivLu().solve(H_err * P.transpose()).transpose();
|
||||
//std::cout << "Here is the matrix rot:\n" << K << std::endl;
|
||||
EEM I_KH = Eigen::Matrix<double, EDIM, EDIM>::Identity() - (KT.transpose() * H_err);
|
||||
|
||||
// update state by injecting dx
|
||||
Eigen::Matrix<double, EDIM, 1> dx(delta_x);
|
||||
dx = (KT.transpose() * y);
|
||||
memcpy(delta_x, dx.data(), EDIM * sizeof(double));
|
||||
err_fun(in_x, delta_x, x_new);
|
||||
Eigen::Matrix<double, DIM, 1> x(x_new);
|
||||
|
||||
// update cov
|
||||
P = ((I_KH * P) * I_KH.transpose()) + ((KT.transpose() * R) * KT);
|
||||
|
||||
// copy out state
|
||||
memcpy(in_x, x.data(), DIM * sizeof(double));
|
||||
memcpy(in_P, P.data(), EDIM * EDIM * sizeof(double));
|
||||
memcpy(in_z, y.data(), y.rows() * sizeof(double));
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
extern "C" {
|
||||
|
||||
void car_update_25(double *in_x, double *in_P, double *in_z, double *in_R, double *in_ea) {
|
||||
update<1, 3, 0>(in_x, in_P, h_25, H_25, NULL, in_z, in_R, in_ea, MAHA_THRESH_25);
|
||||
}
|
||||
void car_update_24(double *in_x, double *in_P, double *in_z, double *in_R, double *in_ea) {
|
||||
update<2, 3, 0>(in_x, in_P, h_24, H_24, NULL, in_z, in_R, in_ea, MAHA_THRESH_24);
|
||||
}
|
||||
void car_update_30(double *in_x, double *in_P, double *in_z, double *in_R, double *in_ea) {
|
||||
update<1, 3, 0>(in_x, in_P, h_30, H_30, NULL, in_z, in_R, in_ea, MAHA_THRESH_30);
|
||||
}
|
||||
void car_update_26(double *in_x, double *in_P, double *in_z, double *in_R, double *in_ea) {
|
||||
update<1, 3, 0>(in_x, in_P, h_26, H_26, NULL, in_z, in_R, in_ea, MAHA_THRESH_26);
|
||||
}
|
||||
void car_update_27(double *in_x, double *in_P, double *in_z, double *in_R, double *in_ea) {
|
||||
update<1, 3, 0>(in_x, in_P, h_27, H_27, NULL, in_z, in_R, in_ea, MAHA_THRESH_27);
|
||||
}
|
||||
void car_update_29(double *in_x, double *in_P, double *in_z, double *in_R, double *in_ea) {
|
||||
update<1, 3, 0>(in_x, in_P, h_29, H_29, NULL, in_z, in_R, in_ea, MAHA_THRESH_29);
|
||||
}
|
||||
void car_update_28(double *in_x, double *in_P, double *in_z, double *in_R, double *in_ea) {
|
||||
update<1, 3, 0>(in_x, in_P, h_28, H_28, NULL, in_z, in_R, in_ea, MAHA_THRESH_28);
|
||||
}
|
||||
void car_update_31(double *in_x, double *in_P, double *in_z, double *in_R, double *in_ea) {
|
||||
update<1, 3, 0>(in_x, in_P, h_31, H_31, NULL, in_z, in_R, in_ea, MAHA_THRESH_31);
|
||||
}
|
||||
void car_err_fun(double *nom_x, double *delta_x, double *out_2689391300508487509) {
|
||||
err_fun(nom_x, delta_x, out_2689391300508487509);
|
||||
}
|
||||
void car_inv_err_fun(double *nom_x, double *true_x, double *out_7177783815062799236) {
|
||||
inv_err_fun(nom_x, true_x, out_7177783815062799236);
|
||||
}
|
||||
void car_H_mod_fun(double *state, double *out_8221602802744378233) {
|
||||
H_mod_fun(state, out_8221602802744378233);
|
||||
}
|
||||
void car_f_fun(double *state, double dt, double *out_7651705913211861388) {
|
||||
f_fun(state, dt, out_7651705913211861388);
|
||||
}
|
||||
void car_F_fun(double *state, double dt, double *out_97859790539154602) {
|
||||
F_fun(state, dt, out_97859790539154602);
|
||||
}
|
||||
void car_h_25(double *state, double *unused, double *out_1747255377487496462) {
|
||||
h_25(state, unused, out_1747255377487496462);
|
||||
}
|
||||
void car_H_25(double *state, double *unused, double *out_7588584345949747189) {
|
||||
H_25(state, unused, out_7588584345949747189);
|
||||
}
|
||||
void car_h_24(double *state, double *unused, double *out_3070396560078381804) {
|
||||
h_24(state, unused, out_3070396560078381804);
|
||||
}
|
||||
void car_H_24(double *state, double *unused, double *out_7300234128957481291) {
|
||||
H_24(state, unused, out_7300234128957481291);
|
||||
}
|
||||
void car_h_30(double *state, double *unused, double *out_2594165583737162996) {
|
||||
h_30(state, unused, out_2594165583737162996);
|
||||
}
|
||||
void car_H_30(double *state, double *unused, double *out_8339826769252555800) {
|
||||
H_30(state, unused, out_8339826769252555800);
|
||||
}
|
||||
void car_h_26(double *state, double *unused, double *out_1234538052727793168) {
|
||||
h_26(state, unused, out_1234538052727793168);
|
||||
}
|
||||
void car_H_26(double *state, double *unused, double *out_3847081027075690965) {
|
||||
H_26(state, unused, out_3847081027075690965);
|
||||
}
|
||||
void car_h_27(double *state, double *unused, double *out_4784041428931829147) {
|
||||
h_27(state, unused, out_4784041428931829147);
|
||||
}
|
||||
void car_H_27(double *state, double *unused, double *out_886124704021714080) {
|
||||
H_27(state, unused, out_886124704021714080);
|
||||
}
|
||||
void car_h_29(double *state, double *unused, double *out_2407507683923682649) {
|
||||
h_29(state, unused, out_2407507683923682649);
|
||||
}
|
||||
void car_H_29(double *state, double *unused, double *out_3571119360136531175) {
|
||||
H_29(state, unused, out_3571119360136531175);
|
||||
}
|
||||
void car_h_28(double *state, double *unused, double *out_7679778805471580716) {
|
||||
h_28(state, unused, out_7679778805471580716);
|
||||
}
|
||||
void car_H_28(double *state, double *unused, double *out_5534749631701857426) {
|
||||
H_28(state, unused, out_5534749631701857426);
|
||||
}
|
||||
void car_h_31(double *state, double *unused, double *out_5023579848862854474) {
|
||||
h_31(state, unused, out_5023579848862854474);
|
||||
}
|
||||
void car_H_31(double *state, double *unused, double *out_3220872924842339489) {
|
||||
H_31(state, unused, out_3220872924842339489);
|
||||
}
|
||||
void car_predict(double *in_x, double *in_P, double *in_Q, double dt) {
|
||||
predict(in_x, in_P, in_Q, dt);
|
||||
}
|
||||
void car_set_mass(double x) {
|
||||
set_mass(x);
|
||||
}
|
||||
void car_set_rotational_inertia(double x) {
|
||||
set_rotational_inertia(x);
|
||||
}
|
||||
void car_set_center_to_front(double x) {
|
||||
set_center_to_front(x);
|
||||
}
|
||||
void car_set_center_to_rear(double x) {
|
||||
set_center_to_rear(x);
|
||||
}
|
||||
void car_set_stiffness_front(double x) {
|
||||
set_stiffness_front(x);
|
||||
}
|
||||
void car_set_stiffness_rear(double x) {
|
||||
set_stiffness_rear(x);
|
||||
}
|
||||
}
|
||||
|
||||
const EKF car = {
|
||||
.name = "car",
|
||||
.kinds = { 25, 24, 30, 26, 27, 29, 28, 31 },
|
||||
.feature_kinds = { },
|
||||
.f_fun = car_f_fun,
|
||||
.F_fun = car_F_fun,
|
||||
.err_fun = car_err_fun,
|
||||
.inv_err_fun = car_inv_err_fun,
|
||||
.H_mod_fun = car_H_mod_fun,
|
||||
.predict = car_predict,
|
||||
.hs = {
|
||||
{ 25, car_h_25 },
|
||||
{ 24, car_h_24 },
|
||||
{ 30, car_h_30 },
|
||||
{ 26, car_h_26 },
|
||||
{ 27, car_h_27 },
|
||||
{ 29, car_h_29 },
|
||||
{ 28, car_h_28 },
|
||||
{ 31, car_h_31 },
|
||||
},
|
||||
.Hs = {
|
||||
{ 25, car_H_25 },
|
||||
{ 24, car_H_24 },
|
||||
{ 30, car_H_30 },
|
||||
{ 26, car_H_26 },
|
||||
{ 27, car_H_27 },
|
||||
{ 29, car_H_29 },
|
||||
{ 28, car_H_28 },
|
||||
{ 31, car_H_31 },
|
||||
},
|
||||
.updates = {
|
||||
{ 25, car_update_25 },
|
||||
{ 24, car_update_24 },
|
||||
{ 30, car_update_30 },
|
||||
{ 26, car_update_26 },
|
||||
{ 27, car_update_27 },
|
||||
{ 29, car_update_29 },
|
||||
{ 28, car_update_28 },
|
||||
{ 31, car_update_31 },
|
||||
},
|
||||
.Hes = {
|
||||
},
|
||||
.sets = {
|
||||
{ "mass", car_set_mass },
|
||||
{ "rotational_inertia", car_set_rotational_inertia },
|
||||
{ "center_to_front", car_set_center_to_front },
|
||||
{ "center_to_rear", car_set_center_to_rear },
|
||||
{ "stiffness_front", car_set_stiffness_front },
|
||||
{ "stiffness_rear", car_set_stiffness_rear },
|
||||
},
|
||||
.extra_routines = {
|
||||
},
|
||||
};
|
||||
|
||||
ekf_lib_init(car)
|
||||
@@ -0,0 +1,40 @@
|
||||
#pragma once
|
||||
#include "rednose/helpers/ekf.h"
|
||||
extern "C" {
|
||||
void car_update_25(double *in_x, double *in_P, double *in_z, double *in_R, double *in_ea);
|
||||
void car_update_24(double *in_x, double *in_P, double *in_z, double *in_R, double *in_ea);
|
||||
void car_update_30(double *in_x, double *in_P, double *in_z, double *in_R, double *in_ea);
|
||||
void car_update_26(double *in_x, double *in_P, double *in_z, double *in_R, double *in_ea);
|
||||
void car_update_27(double *in_x, double *in_P, double *in_z, double *in_R, double *in_ea);
|
||||
void car_update_29(double *in_x, double *in_P, double *in_z, double *in_R, double *in_ea);
|
||||
void car_update_28(double *in_x, double *in_P, double *in_z, double *in_R, double *in_ea);
|
||||
void car_update_31(double *in_x, double *in_P, double *in_z, double *in_R, double *in_ea);
|
||||
void car_err_fun(double *nom_x, double *delta_x, double *out_2689391300508487509);
|
||||
void car_inv_err_fun(double *nom_x, double *true_x, double *out_7177783815062799236);
|
||||
void car_H_mod_fun(double *state, double *out_8221602802744378233);
|
||||
void car_f_fun(double *state, double dt, double *out_7651705913211861388);
|
||||
void car_F_fun(double *state, double dt, double *out_97859790539154602);
|
||||
void car_h_25(double *state, double *unused, double *out_1747255377487496462);
|
||||
void car_H_25(double *state, double *unused, double *out_7588584345949747189);
|
||||
void car_h_24(double *state, double *unused, double *out_3070396560078381804);
|
||||
void car_H_24(double *state, double *unused, double *out_7300234128957481291);
|
||||
void car_h_30(double *state, double *unused, double *out_2594165583737162996);
|
||||
void car_H_30(double *state, double *unused, double *out_8339826769252555800);
|
||||
void car_h_26(double *state, double *unused, double *out_1234538052727793168);
|
||||
void car_H_26(double *state, double *unused, double *out_3847081027075690965);
|
||||
void car_h_27(double *state, double *unused, double *out_4784041428931829147);
|
||||
void car_H_27(double *state, double *unused, double *out_886124704021714080);
|
||||
void car_h_29(double *state, double *unused, double *out_2407507683923682649);
|
||||
void car_H_29(double *state, double *unused, double *out_3571119360136531175);
|
||||
void car_h_28(double *state, double *unused, double *out_7679778805471580716);
|
||||
void car_H_28(double *state, double *unused, double *out_5534749631701857426);
|
||||
void car_h_31(double *state, double *unused, double *out_5023579848862854474);
|
||||
void car_H_31(double *state, double *unused, double *out_3220872924842339489);
|
||||
void car_predict(double *in_x, double *in_P, double *in_Q, double dt);
|
||||
void car_set_mass(double x);
|
||||
void car_set_rotational_inertia(double x);
|
||||
void car_set_center_to_front(double x);
|
||||
void car_set_center_to_rear(double x);
|
||||
void car_set_stiffness_front(double x);
|
||||
void car_set_stiffness_rear(double x);
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
#pragma once
|
||||
#include "rednose/helpers/ekf.h"
|
||||
extern "C" {
|
||||
void pose_update_4(double *in_x, double *in_P, double *in_z, double *in_R, double *in_ea);
|
||||
void pose_update_10(double *in_x, double *in_P, double *in_z, double *in_R, double *in_ea);
|
||||
void pose_update_13(double *in_x, double *in_P, double *in_z, double *in_R, double *in_ea);
|
||||
void pose_update_14(double *in_x, double *in_P, double *in_z, double *in_R, double *in_ea);
|
||||
void pose_err_fun(double *nom_x, double *delta_x, double *out_2843212773844661672);
|
||||
void pose_inv_err_fun(double *nom_x, double *true_x, double *out_2943105406875713938);
|
||||
void pose_H_mod_fun(double *state, double *out_2147512711416992695);
|
||||
void pose_f_fun(double *state, double dt, double *out_9076294150866414245);
|
||||
void pose_F_fun(double *state, double dt, double *out_4995496673877862289);
|
||||
void pose_h_4(double *state, double *unused, double *out_7356118181286553510);
|
||||
void pose_H_4(double *state, double *unused, double *out_3967211437323289556);
|
||||
void pose_h_10(double *state, double *unused, double *out_320685202434969677);
|
||||
void pose_H_10(double *state, double *unused, double *out_156638784834038850);
|
||||
void pose_h_13(double *state, double *unused, double *out_4281107234856404284);
|
||||
void pose_H_13(double *state, double *unused, double *out_7800966900625813580);
|
||||
void pose_h_14(double *state, double *unused, double *out_3917712072833752253);
|
||||
void pose_H_14(double *state, double *unused, double *out_7049999869618661852);
|
||||
void pose_predict(double *in_x, double *in_P, double *in_Q, double dt);
|
||||
}
|
||||
@@ -1,6 +1,7 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
import sys
|
||||
import os
|
||||
import numpy as np
|
||||
|
||||
from openpilot.selfdrive.locationd.models.constants import ObservationKind
|
||||
@@ -102,7 +103,13 @@ class PoseKalman(KalmanFilter):
|
||||
|
||||
def __init__(self, generated_dir, max_rewind_age):
|
||||
dim_state, dim_state_err = PoseKalman.initial_x.shape[0], PoseKalman.initial_P.shape[0]
|
||||
self.filter = EKF_sym_pyx(generated_dir, self.name, PoseKalman.Q, PoseKalman.initial_x, PoseKalman.initial_P,
|
||||
ekf_name = self.name
|
||||
# Compatibility fallback for installs that still carry the older `live` EKF artifacts.
|
||||
if not os.path.exists(os.path.join(generated_dir, f"lib{ekf_name}.so")):
|
||||
live_fallback = os.path.join(generated_dir, "liblive.so")
|
||||
if os.path.exists(live_fallback):
|
||||
ekf_name = "live"
|
||||
self.filter = EKF_sym_pyx(generated_dir, ekf_name, PoseKalman.Q, PoseKalman.initial_x, PoseKalman.initial_P,
|
||||
dim_state, dim_state_err, max_rewind_age=max_rewind_age)
|
||||
|
||||
|
||||
|
||||
@@ -20,39 +20,104 @@ if arch == "Darwin":
|
||||
else:
|
||||
libs += ['OpenCL']
|
||||
|
||||
# Set path definitions
|
||||
for pathdef, fn in {'TRANSFORM': 'transforms/transform.cl', 'LOADYUV': 'transforms/loadyuv.cl'}.items():
|
||||
# Set path definitions to repository-relative paths so prebuilt artifacts stay relocatable.
|
||||
for pathdef, fn in {'TRANSFORM': 'selfdrive/modeld/transforms/transform.cl', 'LOADYUV': 'selfdrive/modeld/transforms/loadyuv.cl'}.items():
|
||||
for xenv in (lenv, lenvCython):
|
||||
xenv['CXXFLAGS'].append(f'-D{pathdef}_PATH=\\"{File(fn).abspath}\\"')
|
||||
xenv['CXXFLAGS'].append(f'-D{pathdef}_PATH=\\"{fn}\\"')
|
||||
|
||||
# Compile cython
|
||||
cython_libs = envCython["LIBS"] + libs
|
||||
commonmodel_lib = lenv.Library('commonmodel', common_src)
|
||||
lenvCython.Program('models/commonmodel_pyx.so', 'models/commonmodel_pyx.pyx', LIBS=[commonmodel_lib, *cython_libs], FRAMEWORKS=frameworks)
|
||||
tinygrad_files = ["#"+x for x in glob.glob(env.Dir("#tinygrad_repo").relpath + "/**", recursive=True, root_dir=env.Dir("#").abspath) if 'pycache' not in x]
|
||||
skip_dm_tinygrad_pkl = os.getenv("SP_SKIP_DM_TINYGRAD_PKL", "").lower() in {"1", "true", "yes", "on"}
|
||||
allow_host_tinygrad_pkl = os.getenv("SP_ALLOW_HOST_TINYGRAD_PKL", "").lower() in {"1", "true", "yes", "on"}
|
||||
build_model_tinygrad_pkl = os.getenv("SP_BUILD_MODEL_TINYGRAD_PKL", "").lower() in {"1", "true", "yes", "on"}
|
||||
has_qcom_gpu = os.path.exists('/dev/kgsl-3d0')
|
||||
|
||||
# Compile camera warp artifacts used by modeld/dmonitoringmodeld on C4.
|
||||
if arch == 'larch64':
|
||||
compile_warp_flags = 'DEV=QCOM FLOAT16=1 NOLOCALS=1 JIT_BATCH_SIZE=0' \
|
||||
if os.path.exists('/dev/kgsl-3d0') else 'DEV=CPU CPU=1 THREADS=0 NOLOCALS=1 DEBUG=0 HOME=/tmp'
|
||||
else:
|
||||
compile_warp_flags = {
|
||||
'Darwin': f'DEV=CPU THREADS=0 NOLOCALS=1 DEBUG=0 HOME={os.path.expanduser("~")}',
|
||||
}.get(arch, 'DEV=CPU CPU_LLVM=1 THREADS=0 NOLOCALS=1 DEBUG=0')
|
||||
|
||||
build_warp_artifacts = os.getenv("SP_BUILD_WARP_ARTIFACTS", "").lower() in {"1", "true", "yes", "on"}
|
||||
if build_warp_artifacts:
|
||||
default_warp_resolutions = [(1928, 1208), (1344, 760)]
|
||||
raw_warp_resolutions = os.getenv("SP_WARP_RESOLUTIONS", "").strip()
|
||||
if raw_warp_resolutions:
|
||||
selected_warp_resolutions = []
|
||||
seen_warp_resolutions = set()
|
||||
for token in raw_warp_resolutions.replace(";", ",").split(","):
|
||||
token = token.strip().lower()
|
||||
if not token:
|
||||
continue
|
||||
w_str, h_str = token.split("x", 1)
|
||||
wh = (int(w_str), int(h_str))
|
||||
if wh not in seen_warp_resolutions:
|
||||
seen_warp_resolutions.add(wh)
|
||||
selected_warp_resolutions.append(wh)
|
||||
if not selected_warp_resolutions:
|
||||
selected_warp_resolutions = default_warp_resolutions
|
||||
else:
|
||||
selected_warp_resolutions = default_warp_resolutions
|
||||
|
||||
compile_warp_script = "#selfdrive/modeld/compile_warp.py"
|
||||
warp_targets = []
|
||||
# Camera resolutions required by modeld compile_warp.py
|
||||
for w, h in selected_warp_resolutions:
|
||||
warp_targets += [File(f"models/warp_{w}x{h}_tinygrad.pkl").abspath, File(f"models/dm_warp_{w}x{h}_tinygrad.pkl").abspath]
|
||||
lenv.Command(
|
||||
warp_targets,
|
||||
tinygrad_files + [compile_warp_script],
|
||||
f'{compile_warp_flags} python3 selfdrive/modeld/compile_warp.py',
|
||||
)
|
||||
else:
|
||||
print("Skipping model warp precompile (set SP_BUILD_WARP_ARTIFACTS=1 to enable)")
|
||||
|
||||
# Get model metadata
|
||||
for model_name in ['driving_vision', 'driving_policy', 'dmonitoring_model']:
|
||||
fn = File(f"models/{model_name}").abspath
|
||||
script_files = [File(Dir("#selfdrive/modeld").File("get_model_metadata.py").abspath)]
|
||||
cmd = f'python3 {Dir("#selfdrive/modeld").abspath}/get_model_metadata.py {fn}.onnx'
|
||||
lenv.Command(fn + "_metadata.pkl", [fn + ".onnx"] + tinygrad_files + script_files, cmd)
|
||||
model_rel_path = f"selfdrive/modeld/models/{model_name}"
|
||||
model_node_path = f"#{model_rel_path}"
|
||||
metadata_script = "#selfdrive/modeld/get_model_metadata.py"
|
||||
cmd = f'python3 selfdrive/modeld/get_model_metadata.py {model_rel_path}.onnx'
|
||||
lenv.Command(model_node_path + "_metadata.pkl", [model_node_path + ".onnx"] + tinygrad_files + [metadata_script], cmd)
|
||||
|
||||
def tg_compile(flags, model_name):
|
||||
pythonpath_string = 'PYTHONPATH="${PYTHONPATH}:' + env.Dir("#tinygrad_repo").abspath + '"'
|
||||
fn = File(f"models/{model_name}").abspath
|
||||
tinygrad_root = "tinygrad_repo"
|
||||
compile_script = "#tinygrad_repo/examples/openpilot/compile3.py"
|
||||
pythonpath_string = 'PYTHONPATH="${PYTHONPATH}:' + tinygrad_root + '"'
|
||||
model_rel_path = f"selfdrive/modeld/models/{model_name}"
|
||||
model_node_path = f"#{model_rel_path}"
|
||||
model_input = f"./{model_rel_path}.onnx"
|
||||
return lenv.Command(
|
||||
fn + "_tinygrad.pkl",
|
||||
[fn + ".onnx"] + tinygrad_files,
|
||||
f'{pythonpath_string} {flags} python3 {Dir("#tinygrad_repo").abspath}/examples/openpilot/compile3.py {fn}.onnx {fn}_tinygrad.pkl'
|
||||
model_node_path + "_tinygrad.pkl",
|
||||
[model_node_path + ".onnx"] + tinygrad_files + [compile_script],
|
||||
f'{pythonpath_string} {flags} python3 tinygrad_repo/examples/openpilot/compile3.py {model_input} {model_rel_path}_tinygrad.pkl'
|
||||
)
|
||||
|
||||
compile_model_tinygrad_pkl = build_model_tinygrad_pkl or allow_host_tinygrad_pkl
|
||||
if not compile_model_tinygrad_pkl:
|
||||
print("Skipping tinygrad model PKL compile (set SP_BUILD_MODEL_TINYGRAD_PKL=1 or SP_ALLOW_HOST_TINYGRAD_PKL=1 to enable)")
|
||||
|
||||
# Compile small models
|
||||
for model_name in ['driving_vision', 'driving_policy', 'dmonitoring_model']:
|
||||
flags = {
|
||||
'larch64': 'DEV=QCOM FLOAT16=1 NOLOCALS=1 IMAGE=2 JIT_BATCH_SIZE=0',
|
||||
'Darwin': f'DEV=CPU HOME={os.path.expanduser("~")}', # tinygrad calls brew which needs a $HOME in the env
|
||||
}.get(arch, 'DEV=CPU CPU_LLVM=1')
|
||||
if model_name == 'dmonitoring_model' and skip_dm_tinygrad_pkl:
|
||||
print("Skipping dmonitoring_model_tinygrad.pkl compile (SP_SKIP_DM_TINYGRAD_PKL enabled)")
|
||||
continue
|
||||
if not compile_model_tinygrad_pkl:
|
||||
continue
|
||||
if arch == 'larch64':
|
||||
# On real devices keep QCOM codegen.
|
||||
flags = 'DEV=QCOM QCOM=1 FLOAT16=1 NOLOCALS=1 IMAGE=2 JIT_BATCH_SIZE=0 DEBUG=0'
|
||||
else:
|
||||
# Opt-in host compile (for debugging only).
|
||||
flags = {
|
||||
'Darwin': 'DEV=CPU CPU=1 IMAGE=0 NOLOCALS=1 DEBUG=0 HOME=/tmp',
|
||||
}.get(arch, 'DEV=CPU CPU=1 IMAGE=0 NOLOCALS=1 DEBUG=0')
|
||||
tg_compile(flags, model_name)
|
||||
|
||||
# Compile BIG model if USB GPU is available
|
||||
@@ -62,7 +127,7 @@ if "USBGPU" in os.environ:
|
||||
devs = subprocess.check_output('python3 -c "from tinygrad import Device; print(list(Device.get_available_devices()))"', shell=True, cwd=env.Dir('#').abspath)
|
||||
if b"AMD" in devs:
|
||||
print("USB GPU detected... building")
|
||||
flags = "DEV=AMD AMD_IFACE=USB AMD_LLVM=1 NOLOCALS=0 IMAGE=0"
|
||||
flags = "DEV=AMD AMD=1 AMD_IFACE=USB AMD_LLVM=1 NOLOCALS=0 IMAGE=0 DEBUG=0"
|
||||
bp = tg_compile(flags, "big_driving_policy")
|
||||
bv = tg_compile(flags, "big_driving_vision")
|
||||
lenv.SideEffect('lock', [bp, bv]) # tg doesn't support multi-process so build serially
|
||||
|
||||
@@ -0,0 +1,242 @@
|
||||
#!/usr/bin/env python3
|
||||
import os
|
||||
import time
|
||||
import pickle
|
||||
import numpy as np
|
||||
from pathlib import Path
|
||||
from tinygrad.tensor import Tensor
|
||||
from tinygrad.helpers import Context
|
||||
from tinygrad.device import Device
|
||||
from tinygrad.engine.jit import TinyJit
|
||||
|
||||
from openpilot.system.camerad.cameras.nv12_info import get_nv12_info
|
||||
|
||||
MODELS_DIR = Path(__file__).parent / 'models'
|
||||
WARP_COMPILE_ITERS = max(2, int(os.getenv("WARP_COMPILE_ITERS", "2")))
|
||||
|
||||
# Keep compile_warp standalone so it does not depend on common.transformations C extensions.
|
||||
MEDMODEL_INPUT_SIZE = (512, 256)
|
||||
DM_INPUT_SIZE = (1440, 960)
|
||||
|
||||
DEFAULT_CAMERA_CONFIGS = [
|
||||
(1928, 1208), # tici / tizi
|
||||
(1344, 760), # mici
|
||||
]
|
||||
|
||||
|
||||
def _parse_camera_configs_from_env() -> list[tuple[int, int]]:
|
||||
raw = os.getenv("SP_WARP_RESOLUTIONS", "").strip()
|
||||
if not raw:
|
||||
return DEFAULT_CAMERA_CONFIGS
|
||||
|
||||
parsed: list[tuple[int, int]] = []
|
||||
for token in raw.replace(";", ",").split(","):
|
||||
token = token.strip().lower()
|
||||
if not token:
|
||||
continue
|
||||
try:
|
||||
w_str, h_str = token.split("x", 1)
|
||||
parsed.append((int(w_str), int(h_str)))
|
||||
except Exception as e:
|
||||
raise ValueError(f"Invalid SP_WARP_RESOLUTIONS token: {token!r}") from e
|
||||
|
||||
# Keep first occurrence order, drop duplicates.
|
||||
deduped: list[tuple[int, int]] = []
|
||||
seen: set[tuple[int, int]] = set()
|
||||
for wh in parsed:
|
||||
if wh not in seen:
|
||||
seen.add(wh)
|
||||
deduped.append(wh)
|
||||
return deduped or DEFAULT_CAMERA_CONFIGS
|
||||
|
||||
|
||||
CAMERA_CONFIGS = _parse_camera_configs_from_env()
|
||||
|
||||
UV_SCALE_MATRIX = np.array([[0.5, 0, 0], [0, 0.5, 0], [0, 0, 1]], dtype=np.float32)
|
||||
UV_SCALE_MATRIX_INV = np.linalg.inv(UV_SCALE_MATRIX)
|
||||
|
||||
IMG_BUFFER_SHAPE = (30, MEDMODEL_INPUT_SIZE[1] // 2, MEDMODEL_INPUT_SIZE[0] // 2)
|
||||
|
||||
|
||||
def warp_pkl_path(w, h):
|
||||
return MODELS_DIR / f'warp_{w}x{h}_tinygrad.pkl'
|
||||
|
||||
|
||||
def dm_warp_pkl_path(w, h):
|
||||
return MODELS_DIR / f'dm_warp_{w}x{h}_tinygrad.pkl'
|
||||
|
||||
|
||||
def warp_perspective_tinygrad(src_flat, M_inv, dst_shape, src_shape, stride_pad):
|
||||
w_dst, h_dst = dst_shape
|
||||
h_src, w_src = src_shape
|
||||
|
||||
x = Tensor.arange(w_dst).reshape(1, w_dst).expand(h_dst, w_dst).reshape(-1)
|
||||
y = Tensor.arange(h_dst).reshape(h_dst, 1).expand(h_dst, w_dst).reshape(-1)
|
||||
|
||||
# inline 3x3 matmul as elementwise to avoid reduce op (enables fusion with gather)
|
||||
src_x = M_inv[0, 0] * x + M_inv[0, 1] * y + M_inv[0, 2]
|
||||
src_y = M_inv[1, 0] * x + M_inv[1, 1] * y + M_inv[1, 2]
|
||||
src_w = M_inv[2, 0] * x + M_inv[2, 1] * y + M_inv[2, 2]
|
||||
|
||||
src_x = src_x / src_w
|
||||
src_y = src_y / src_w
|
||||
|
||||
x_nn_clipped = Tensor.round(src_x).clip(0, w_src - 1).cast('int')
|
||||
y_nn_clipped = Tensor.round(src_y).clip(0, h_src - 1).cast('int')
|
||||
idx = y_nn_clipped * (w_src + stride_pad) + x_nn_clipped
|
||||
|
||||
return src_flat[idx]
|
||||
|
||||
|
||||
def frames_to_tensor(frames, model_w, model_h):
|
||||
H = (frames.shape[0] * 2) // 3
|
||||
W = frames.shape[1]
|
||||
in_img1 = Tensor.cat(frames[0:H:2, 0::2],
|
||||
frames[1:H:2, 0::2],
|
||||
frames[0:H:2, 1::2],
|
||||
frames[1:H:2, 1::2],
|
||||
frames[H:H+H//4].reshape((H//2, W//2)),
|
||||
frames[H+H//4:H+H//2].reshape((H//2, W//2)), dim=0).reshape((6, H//2, W//2))
|
||||
return in_img1
|
||||
|
||||
|
||||
def make_frame_prepare(cam_w, cam_h, model_w, model_h):
|
||||
stride, y_height, uv_height, _ = get_nv12_info(cam_w, cam_h)
|
||||
uv_offset = stride * y_height
|
||||
stride_pad = stride - cam_w
|
||||
|
||||
def frame_prepare_tinygrad(input_frame, M_inv):
|
||||
# UV_SCALE @ M_inv @ UV_SCALE_INV simplifies to elementwise scaling
|
||||
M_inv_uv = M_inv * Tensor([[1.0, 1.0, 0.5], [1.0, 1.0, 0.5], [2.0, 2.0, 1.0]])
|
||||
# deinterleave NV12 UV plane (UVUV... -> separate U, V)
|
||||
uv = input_frame[uv_offset:uv_offset + uv_height * stride].reshape(uv_height, stride)
|
||||
with Context(SPLIT_REDUCEOP=0):
|
||||
y = warp_perspective_tinygrad(input_frame[:cam_h*stride],
|
||||
M_inv, (model_w, model_h),
|
||||
(cam_h, cam_w), stride_pad).realize()
|
||||
u = warp_perspective_tinygrad(uv[:cam_h//2, :cam_w:2].flatten(),
|
||||
M_inv_uv, (model_w//2, model_h//2),
|
||||
(cam_h//2, cam_w//2), 0).realize()
|
||||
v = warp_perspective_tinygrad(uv[:cam_h//2, 1:cam_w:2].flatten(),
|
||||
M_inv_uv, (model_w//2, model_h//2),
|
||||
(cam_h//2, cam_w//2), 0).realize()
|
||||
yuv = y.cat(u).cat(v).reshape((model_h * 3 // 2, model_w))
|
||||
tensor = frames_to_tensor(yuv, model_w, model_h)
|
||||
return tensor
|
||||
return frame_prepare_tinygrad
|
||||
|
||||
|
||||
def make_update_img_input(frame_prepare, model_w, model_h):
|
||||
def update_img_input_tinygrad(tensor, frame, M_inv):
|
||||
M_inv = M_inv.to(Device.DEFAULT)
|
||||
new_img = frame_prepare(frame, M_inv)
|
||||
full_buffer = tensor[6:].cat(new_img, dim=0).contiguous()
|
||||
return full_buffer, Tensor.cat(full_buffer[:6], full_buffer[-6:], dim=0).contiguous().reshape(1, 12, model_h//2, model_w//2)
|
||||
return update_img_input_tinygrad
|
||||
|
||||
|
||||
def make_update_both_imgs(frame_prepare, model_w, model_h):
|
||||
update_img = make_update_img_input(frame_prepare, model_w, model_h)
|
||||
|
||||
def update_both_imgs_tinygrad(calib_img_buffer, new_img, M_inv,
|
||||
calib_big_img_buffer, new_big_img, M_inv_big):
|
||||
calib_img_buffer, calib_img_pair = update_img(calib_img_buffer, new_img, M_inv)
|
||||
calib_big_img_buffer, calib_big_img_pair = update_img(calib_big_img_buffer, new_big_img, M_inv_big)
|
||||
return calib_img_buffer, calib_img_pair, calib_big_img_buffer, calib_big_img_pair
|
||||
return update_both_imgs_tinygrad
|
||||
|
||||
|
||||
def make_warp_dm(cam_w, cam_h, dm_w, dm_h):
|
||||
stride, y_height, _, _ = get_nv12_info(cam_w, cam_h)
|
||||
stride_pad = stride - cam_w
|
||||
|
||||
def warp_dm(input_frame, M_inv):
|
||||
M_inv = M_inv.to(Device.DEFAULT)
|
||||
result = warp_perspective_tinygrad(input_frame[:cam_h*stride], M_inv, (dm_w, dm_h), (cam_h, cam_w), stride_pad).reshape(-1, dm_h * dm_w)
|
||||
return result
|
||||
return warp_dm
|
||||
|
||||
|
||||
def compile_modeld_warp(cam_w, cam_h):
|
||||
model_w, model_h = MEDMODEL_INPUT_SIZE
|
||||
_, _, _, yuv_size = get_nv12_info(cam_w, cam_h)
|
||||
|
||||
print(f"Compiling modeld warp for {cam_w}x{cam_h} ({WARP_COMPILE_ITERS} iters)...", flush=True)
|
||||
|
||||
frame_prepare = make_frame_prepare(cam_w, cam_h, model_w, model_h)
|
||||
update_both_imgs = make_update_both_imgs(frame_prepare, model_w, model_h)
|
||||
update_img_jit = TinyJit(update_both_imgs, prune=True)
|
||||
|
||||
full_buffer = Tensor.zeros(IMG_BUFFER_SHAPE, dtype='uint8').contiguous().realize()
|
||||
big_full_buffer = Tensor.zeros(IMG_BUFFER_SHAPE, dtype='uint8').contiguous().realize()
|
||||
full_buffer_np = np.zeros(IMG_BUFFER_SHAPE, dtype=np.uint8)
|
||||
big_full_buffer_np = np.zeros(IMG_BUFFER_SHAPE, dtype=np.uint8)
|
||||
|
||||
for i in range(WARP_COMPILE_ITERS):
|
||||
new_frame_np = (32 * np.random.randn(yuv_size).astype(np.float32) + 128).clip(0, 255).astype(np.uint8)
|
||||
img_inputs = [full_buffer,
|
||||
Tensor.from_blob(new_frame_np.ctypes.data, (yuv_size,), dtype='uint8').realize(),
|
||||
Tensor(Tensor.randn(3, 3).mul(8).realize().numpy(), device='NPY')]
|
||||
new_big_frame_np = (32 * np.random.randn(yuv_size).astype(np.float32) + 128).clip(0, 255).astype(np.uint8)
|
||||
big_img_inputs = [big_full_buffer,
|
||||
Tensor.from_blob(new_big_frame_np.ctypes.data, (yuv_size,), dtype='uint8').realize(),
|
||||
Tensor(Tensor.randn(3, 3).mul(8).realize().numpy(), device='NPY')]
|
||||
inputs = img_inputs + big_img_inputs
|
||||
Device.default.synchronize()
|
||||
|
||||
inputs_np = [x.numpy() for x in inputs]
|
||||
inputs_np[0] = full_buffer_np
|
||||
inputs_np[3] = big_full_buffer_np
|
||||
|
||||
st = time.perf_counter()
|
||||
out = update_img_jit(*inputs)
|
||||
full_buffer = out[0].contiguous().realize().clone()
|
||||
big_full_buffer = out[2].contiguous().realize().clone()
|
||||
mt = time.perf_counter()
|
||||
Device.default.synchronize()
|
||||
et = time.perf_counter()
|
||||
print(f" [{i+1}/{WARP_COMPILE_ITERS}] enqueue {(mt-st)*1e3:6.2f} ms -- total {(et-st)*1e3:6.2f} ms", flush=True)
|
||||
|
||||
pkl_path = warp_pkl_path(cam_w, cam_h)
|
||||
with open(pkl_path, "wb") as f:
|
||||
pickle.dump(update_img_jit, f)
|
||||
print(f" Saved to {pkl_path}", flush=True)
|
||||
|
||||
jit = pickle.load(open(pkl_path, "rb"))
|
||||
jit(*inputs)
|
||||
|
||||
|
||||
def compile_dm_warp(cam_w, cam_h):
|
||||
dm_w, dm_h = DM_INPUT_SIZE
|
||||
_, _, _, yuv_size = get_nv12_info(cam_w, cam_h)
|
||||
|
||||
print(f"Compiling DM warp for {cam_w}x{cam_h} ({WARP_COMPILE_ITERS} iters)...", flush=True)
|
||||
|
||||
warp_dm = make_warp_dm(cam_w, cam_h, dm_w, dm_h)
|
||||
warp_dm_jit = TinyJit(warp_dm, prune=True)
|
||||
|
||||
for i in range(WARP_COMPILE_ITERS):
|
||||
inputs = [Tensor.from_blob((32 * Tensor.randn(yuv_size,) + 128).cast(dtype='uint8').realize().numpy().ctypes.data, (yuv_size,), dtype='uint8'),
|
||||
Tensor(Tensor.randn(3, 3).mul(8).realize().numpy(), device='NPY')]
|
||||
Device.default.synchronize()
|
||||
st = time.perf_counter()
|
||||
warp_dm_jit(*inputs)
|
||||
mt = time.perf_counter()
|
||||
Device.default.synchronize()
|
||||
et = time.perf_counter()
|
||||
print(f" [{i+1}/{WARP_COMPILE_ITERS}] enqueue {(mt-st)*1e3:6.2f} ms -- total {(et-st)*1e3:6.2f} ms", flush=True)
|
||||
|
||||
pkl_path = dm_warp_pkl_path(cam_w, cam_h)
|
||||
with open(pkl_path, "wb") as f:
|
||||
pickle.dump(warp_dm_jit, f)
|
||||
print(f" Saved to {pkl_path}", flush=True)
|
||||
|
||||
|
||||
def run_and_save_pickle():
|
||||
for cam_w, cam_h in CAMERA_CONFIGS:
|
||||
compile_modeld_warp(cam_w, cam_h)
|
||||
compile_dm_warp(cam_w, cam_h)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
run_and_save_pickle()
|
||||
@@ -3,7 +3,6 @@ import os
|
||||
from openpilot.system.hardware import TICI
|
||||
os.environ['DEV'] = 'QCOM' if TICI else 'CPU'
|
||||
from tinygrad.tensor import Tensor
|
||||
from tinygrad.dtype import dtypes
|
||||
import time
|
||||
import pickle
|
||||
import numpy as np
|
||||
@@ -16,50 +15,109 @@ from openpilot.common.swaglog import cloudlog
|
||||
from openpilot.common.realtime import config_realtime_process
|
||||
from openpilot.common.transformations.model import dmonitoringmodel_intrinsics
|
||||
from openpilot.common.transformations.camera import _ar_ox_fisheye, _os_fisheye
|
||||
from openpilot.selfdrive.modeld.models.commonmodel_pyx import CLContext, MonitoringModelFrame
|
||||
from openpilot.system.camerad.cameras.nv12_info import get_nv12_info
|
||||
from openpilot.common.file_chunker import read_file_chunked
|
||||
from openpilot.selfdrive.modeld.parse_model_outputs import sigmoid, safe_exp
|
||||
from openpilot.selfdrive.modeld.runners.tinygrad_helpers import qcom_tensor_from_opencl_address
|
||||
|
||||
PROCESS_NAME = "selfdrive.modeld.dmonitoringmodeld"
|
||||
SEND_RAW_PRED = os.getenv('SEND_RAW_PRED')
|
||||
MODEL_PKL_PATH = Path(__file__).parent / 'models/dmonitoring_model_tinygrad.pkl'
|
||||
METADATA_PATH = Path(__file__).parent / 'models/dmonitoring_model_metadata.pkl'
|
||||
MODELS_DIR = Path(__file__).parent / 'models'
|
||||
|
||||
|
||||
class ModelState:
|
||||
inputs: dict[str, np.ndarray]
|
||||
output: np.ndarray
|
||||
|
||||
def __init__(self, cl_ctx):
|
||||
def __init__(self):
|
||||
with open(METADATA_PATH, 'rb') as f:
|
||||
model_metadata = pickle.load(f)
|
||||
self.input_shapes = model_metadata['input_shapes']
|
||||
self.output_slices = model_metadata['output_slices']
|
||||
|
||||
self.frame = MonitoringModelFrame(cl_ctx)
|
||||
self.numpy_inputs = {
|
||||
'calib': np.zeros(self.input_shapes['calib'], dtype=np.float32),
|
||||
}
|
||||
|
||||
self.warp_inputs_np = {'transform': np.zeros((3,3), dtype=np.float32)}
|
||||
self.warp_inputs = {k: Tensor(v, device='NPY') for k,v in self.warp_inputs_np.items()}
|
||||
self.frame_buf_params = None
|
||||
self.tensor_inputs = {k: Tensor(v, device='NPY').realize() for k,v in self.numpy_inputs.items()}
|
||||
with open(MODEL_PKL_PATH, "rb") as f:
|
||||
self.model_run = pickle.load(f)
|
||||
self._blob_cache : dict[int, Tensor] = {}
|
||||
self.image_warp = None
|
||||
self._warp_rebuild_attempted: set[tuple[int, int]] = set()
|
||||
self._warp_backend_rebuild_attempted: set[tuple[int, int]] = set()
|
||||
self.model_run = pickle.loads(read_file_chunked(str(MODEL_PKL_PATH)))
|
||||
|
||||
def _load_or_rebuild_dm_warp(self, width: int, height: int):
|
||||
warp_path = MODELS_DIR / f'dm_warp_{width}x{height}_tinygrad.pkl'
|
||||
resolution_key = (width, height)
|
||||
|
||||
def load_warp():
|
||||
with open(warp_path, "rb") as f:
|
||||
return pickle.load(f)
|
||||
|
||||
try:
|
||||
return load_warp()
|
||||
except Exception as error:
|
||||
if resolution_key in self._warp_rebuild_attempted:
|
||||
raise
|
||||
|
||||
self._warp_rebuild_attempted.add(resolution_key)
|
||||
cloudlog.exception(f"Failed to load DM warp artifact {warp_path}: {error}")
|
||||
cloudlog.warning(f"Rebuilding DM warp artifact for {width}x{height}")
|
||||
|
||||
try:
|
||||
warp_path.unlink(missing_ok=True)
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
from openpilot.selfdrive.modeld.compile_warp import compile_dm_warp
|
||||
compile_dm_warp(width, height)
|
||||
|
||||
try:
|
||||
return load_warp()
|
||||
except Exception as retry_error:
|
||||
cloudlog.exception(f"Reload failed after rebuilding {warp_path}: {retry_error}")
|
||||
raise
|
||||
|
||||
def run(self, buf: VisionBuf, calib: np.ndarray, transform: np.ndarray) -> tuple[np.ndarray, float]:
|
||||
self.numpy_inputs['calib'][0,:] = calib
|
||||
|
||||
t1 = time.perf_counter()
|
||||
|
||||
input_img_cl = self.frame.prepare(buf, transform.flatten())
|
||||
if TICI:
|
||||
# The imgs tensors are backed by opencl memory, only need init once
|
||||
if 'input_img' not in self.tensor_inputs:
|
||||
self.tensor_inputs['input_img'] = qcom_tensor_from_opencl_address(input_img_cl.mem_address, self.input_shapes['input_img'], dtype=dtypes.uint8)
|
||||
else:
|
||||
self.tensor_inputs['input_img'] = Tensor(self.frame.buffer_from_cl(input_img_cl).reshape(self.input_shapes['input_img']), dtype=dtypes.uint8).realize()
|
||||
if self.image_warp is None:
|
||||
self.frame_buf_params = get_nv12_info(buf.width, buf.height)
|
||||
self.image_warp = self._load_or_rebuild_dm_warp(buf.width, buf.height)
|
||||
ptr = buf.data.ctypes.data
|
||||
# There is a ringbuffer of imgs, just cache tensors pointing to all of them
|
||||
if ptr not in self._blob_cache:
|
||||
self._blob_cache[ptr] = Tensor.from_blob(ptr, (self.frame_buf_params[3],), dtype='uint8')
|
||||
|
||||
self.warp_inputs_np['transform'][:] = transform[:]
|
||||
resolution_key = (buf.width, buf.height)
|
||||
try:
|
||||
self.tensor_inputs['input_img'] = self.image_warp(self._blob_cache[ptr], self.warp_inputs['transform']).realize()
|
||||
except AssertionError as error:
|
||||
# Handle runtime backend mismatch (e.g. CPU-captured warp artifact on QCOM device).
|
||||
if "args mismatch in JIT" not in str(error) or resolution_key in self._warp_backend_rebuild_attempted:
|
||||
raise
|
||||
|
||||
output = self.model_run(**self.tensor_inputs).contiguous().realize().uop.base.buffer.numpy()
|
||||
self._warp_backend_rebuild_attempted.add(resolution_key)
|
||||
cloudlog.warning(f"DM warp JIT backend mismatch for {buf.width}x{buf.height}; rebuilding artifact for active backend")
|
||||
warp_path = MODELS_DIR / f'dm_warp_{buf.width}x{buf.height}_tinygrad.pkl'
|
||||
try:
|
||||
warp_path.unlink(missing_ok=True)
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
from openpilot.selfdrive.modeld.compile_warp import compile_dm_warp
|
||||
compile_dm_warp(buf.width, buf.height)
|
||||
self.image_warp = self._load_or_rebuild_dm_warp(buf.width, buf.height)
|
||||
self.tensor_inputs['input_img'] = self.image_warp(self._blob_cache[ptr], self.warp_inputs['transform']).realize()
|
||||
|
||||
output = self.model_run(**self.tensor_inputs).contiguous().realize().uop.base.buffer.numpy().flatten()
|
||||
|
||||
t2 = time.perf_counter()
|
||||
return output, t2 - t1
|
||||
@@ -107,12 +165,11 @@ def get_driverstate_packet(model_output, frame_id: int, location_ts: int, exec_t
|
||||
def main():
|
||||
config_realtime_process(7, 5)
|
||||
|
||||
cl_context = CLContext()
|
||||
model = ModelState(cl_context)
|
||||
model = ModelState()
|
||||
cloudlog.warning("models loaded, dmonitoringmodeld starting")
|
||||
|
||||
cloudlog.warning("connecting to driver stream")
|
||||
vipc_client = VisionIpcClient("camerad", VisionStreamType.VISION_STREAM_DRIVER, True, cl_context)
|
||||
vipc_client = VisionIpcClient("camerad", VisionStreamType.VISION_STREAM_DRIVER, True)
|
||||
while not vipc_client.connect(False):
|
||||
time.sleep(0.1)
|
||||
assert vipc_client.is_connected()
|
||||
|
||||
@@ -31,16 +31,47 @@ from openpilot.selfdrive.modeld.constants import ModelConstants, Plan
|
||||
from openpilot.selfdrive.modeld.models.commonmodel_pyx import DrivingModelFrame, CLContext
|
||||
from openpilot.selfdrive.modeld.runners.tinygrad_helpers import qcom_tensor_from_opencl_address
|
||||
|
||||
from openpilot.frogpilot.common.frogpilot_variables import get_frogpilot_toggles
|
||||
from openpilot.frogpilot.common.frogpilot_variables import MODELS_PATH, get_frogpilot_toggles
|
||||
|
||||
|
||||
PROCESS_NAME = "selfdrive.modeld.modeld"
|
||||
SEND_RAW_PRED = os.getenv('SEND_RAW_PRED')
|
||||
|
||||
VISION_PKL_PATH = Path(__file__).parent / 'models/driving_vision_tinygrad.pkl'
|
||||
POLICY_PKL_PATH = Path(__file__).parent / 'models/driving_policy_tinygrad.pkl'
|
||||
VISION_METADATA_PATH = Path(__file__).parent / 'models/driving_vision_metadata.pkl'
|
||||
POLICY_METADATA_PATH = Path(__file__).parent / 'models/driving_policy_metadata.pkl'
|
||||
BUILTIN_MODEL_DIR = Path(__file__).parent / "models"
|
||||
BUILTIN_VISION_PKL_PATH = BUILTIN_MODEL_DIR / "driving_vision_tinygrad.pkl"
|
||||
BUILTIN_POLICY_PKL_PATH = BUILTIN_MODEL_DIR / "driving_policy_tinygrad.pkl"
|
||||
BUILTIN_VISION_METADATA_PATH = BUILTIN_MODEL_DIR / "driving_vision_metadata.pkl"
|
||||
BUILTIN_POLICY_METADATA_PATH = BUILTIN_MODEL_DIR / "driving_policy_metadata.pkl"
|
||||
|
||||
|
||||
def _decode_param(value) -> str:
|
||||
if isinstance(value, bytes):
|
||||
return value.decode("utf-8")
|
||||
return str(value or "")
|
||||
|
||||
|
||||
def _resolve_model_paths(params: Params) -> tuple[str, dict[str, Path]]:
|
||||
model_key = _decode_param(params.get("DrivingModel")).strip()
|
||||
candidates = [model_key] if model_key else []
|
||||
if model_key.endswith("_default"):
|
||||
candidates.append(model_key[:-8])
|
||||
|
||||
for candidate in candidates:
|
||||
files = {
|
||||
"vision_pkl": MODELS_PATH / f"{candidate}_driving_vision_tinygrad.pkl",
|
||||
"policy_pkl": MODELS_PATH / f"{candidate}_driving_policy_tinygrad.pkl",
|
||||
"vision_meta": MODELS_PATH / f"{candidate}_driving_vision_metadata.pkl",
|
||||
"policy_meta": MODELS_PATH / f"{candidate}_driving_policy_metadata.pkl",
|
||||
}
|
||||
if all(path.is_file() for path in files.values()):
|
||||
return candidate, files
|
||||
|
||||
return "builtin", {
|
||||
"vision_pkl": BUILTIN_VISION_PKL_PATH,
|
||||
"policy_pkl": BUILTIN_POLICY_PKL_PATH,
|
||||
"vision_meta": BUILTIN_VISION_METADATA_PATH,
|
||||
"policy_meta": BUILTIN_POLICY_METADATA_PATH,
|
||||
}
|
||||
|
||||
LAT_SMOOTH_SECONDS = 0.0
|
||||
LONG_SMOOTH_SECONDS = 0.3
|
||||
@@ -143,15 +174,15 @@ class ModelState:
|
||||
output: np.ndarray
|
||||
prev_desire: np.ndarray # for tracking the rising edge of the pulse
|
||||
|
||||
def __init__(self, context: CLContext):
|
||||
with open(VISION_METADATA_PATH, 'rb') as f:
|
||||
def __init__(self, context: CLContext, model_paths: dict[str, Path]):
|
||||
with open(model_paths["vision_meta"], 'rb') as f:
|
||||
vision_metadata = pickle.load(f)
|
||||
self.vision_input_shapes = vision_metadata['input_shapes']
|
||||
self.vision_input_names = list(self.vision_input_shapes.keys())
|
||||
self.vision_output_slices = vision_metadata['output_slices']
|
||||
vision_output_size = vision_metadata['output_shapes']['outputs'][1]
|
||||
|
||||
with open(POLICY_METADATA_PATH, 'rb') as f:
|
||||
with open(model_paths["policy_meta"], 'rb') as f:
|
||||
policy_metadata = pickle.load(f)
|
||||
self.policy_input_shapes = policy_metadata['input_shapes']
|
||||
self.policy_output_slices = policy_metadata['output_slices']
|
||||
@@ -174,10 +205,10 @@ class ModelState:
|
||||
self.policy_output = np.zeros(policy_output_size, dtype=np.float32)
|
||||
self.parser = Parser()
|
||||
|
||||
with open(VISION_PKL_PATH, "rb") as f:
|
||||
with open(model_paths["vision_pkl"], "rb") as f:
|
||||
self.vision_run = pickle.load(f)
|
||||
|
||||
with open(POLICY_PKL_PATH, "rb") as f:
|
||||
with open(model_paths["policy_pkl"], "rb") as f:
|
||||
self.policy_run = pickle.load(f)
|
||||
|
||||
def slice_outputs(self, model_outputs: np.ndarray, output_slices: dict[str, slice]) -> dict[str, np.ndarray]:
|
||||
@@ -238,8 +269,11 @@ def main(demo=False):
|
||||
st = time.monotonic()
|
||||
cloudlog.warning("setting up CL context")
|
||||
cl_context = CLContext()
|
||||
params = Params()
|
||||
selected_model_key, model_paths = _resolve_model_paths(params)
|
||||
cloudlog.warning("CL context ready; loading model")
|
||||
model = ModelState(cl_context)
|
||||
model = ModelState(cl_context, model_paths)
|
||||
cloudlog.warning(f"selected model source: {selected_model_key}")
|
||||
cloudlog.warning(f"models loaded in {time.monotonic() - st:.1f}s, modeld starting")
|
||||
|
||||
# visionipc clients
|
||||
@@ -270,7 +304,6 @@ def main(demo=False):
|
||||
sm = SubMaster(["deviceState", "carState", "roadCameraState", "liveCalibration", "driverMonitoringState", "carControl", "liveDelay"])
|
||||
|
||||
publish_state = PublishState()
|
||||
params = Params()
|
||||
|
||||
# setup filter to track dropped frames
|
||||
frame_dropped_filter = FirstOrderFilter(0., 10., 1. / ModelConstants.MODEL_RUN_FREQ)
|
||||
|
||||
@@ -1,3 +1,2 @@
|
||||
pandad
|
||||
pandad_api_impl.cpp
|
||||
tests/test_pandad_usbprotocol
|
||||
|
||||
@@ -137,7 +137,7 @@ std::optional<std::string> Panda::get_serial() {
|
||||
|
||||
bool Panda::up_to_date() {
|
||||
if (auto fw_sig = get_firmware_version()) {
|
||||
for (auto fn : { "panda.bin.signed", "panda_h7.bin.signed" }) {
|
||||
for (auto fn : { "panda.bin.signed", "panda_h7.bin.signed", "panda_remote.bin.signed", "panda_h7_remote.bin.signed" }) {
|
||||
auto content = util::read_file(std::string("../../panda/board/obj/") + fn);
|
||||
if (content.size() >= fw_sig->size() &&
|
||||
memcmp(content.data() + content.size() - fw_sig->size(), fw_sig->data(), fw_sig->size()) == 0) {
|
||||
|
||||
@@ -8,20 +8,39 @@ import subprocess
|
||||
|
||||
from panda import Panda, PandaDFU, PandaProtocolMismatch, FW_PATH
|
||||
from openpilot.common.basedir import BASEDIR
|
||||
from openpilot.common.params import Params
|
||||
from openpilot.common.params import Params, UnknownKeyName
|
||||
from openpilot.system.hardware import HARDWARE
|
||||
from openpilot.common.swaglog import cloudlog
|
||||
|
||||
|
||||
def get_expected_signature(panda: Panda) -> bytes:
|
||||
def get_expected_firmware_path(panda: Panda, remote_start: bool) -> str:
|
||||
app_fn = panda.get_mcu_type().config.app_fn
|
||||
if remote_start:
|
||||
remote_fn = "panda_h7_remote.bin.signed" if app_fn == "panda_h7.bin.signed" else "panda_remote.bin.signed"
|
||||
remote_path = os.path.join(FW_PATH, remote_fn)
|
||||
if os.path.isfile(remote_path):
|
||||
return remote_path
|
||||
cloudlog.warning(f"Remote-start panda firmware not found: {remote_path}, falling back to default")
|
||||
return os.path.join(FW_PATH, app_fn)
|
||||
|
||||
|
||||
def get_expected_signature(panda: Panda, remote_start: bool) -> bytes:
|
||||
try:
|
||||
fn = os.path.join(FW_PATH, panda.get_mcu_type().config.app_fn)
|
||||
fn = get_expected_firmware_path(panda, remote_start)
|
||||
return Panda.get_signature_from_firmware(fn)
|
||||
except Exception:
|
||||
cloudlog.exception("Error computing expected signature")
|
||||
return b""
|
||||
|
||||
def flash_panda(panda_serial: str) -> Panda:
|
||||
|
||||
def get_remote_start_boots_comma(params: Params) -> bool:
|
||||
try:
|
||||
return params.get_bool("RemoteStartBootsComma")
|
||||
except UnknownKeyName:
|
||||
return False
|
||||
|
||||
|
||||
def flash_panda(panda_serial: str, remote_start: bool) -> Panda:
|
||||
try:
|
||||
panda = Panda(panda_serial)
|
||||
except PandaProtocolMismatch:
|
||||
@@ -29,7 +48,8 @@ def flash_panda(panda_serial: str) -> Panda:
|
||||
HARDWARE.recover_internal_panda()
|
||||
raise
|
||||
|
||||
fw_signature = get_expected_signature(panda)
|
||||
fw_path = get_expected_firmware_path(panda, remote_start)
|
||||
fw_signature = get_expected_signature(panda, remote_start)
|
||||
internal_panda = panda.is_internal()
|
||||
|
||||
panda_version = "bootstub" if panda.bootstub else panda.get_version()
|
||||
@@ -38,7 +58,7 @@ def flash_panda(panda_serial: str) -> Panda:
|
||||
|
||||
if panda.bootstub or panda_signature != fw_signature:
|
||||
cloudlog.info("Panda firmware out of date, update required")
|
||||
panda.flash()
|
||||
panda.flash(fn=fw_path)
|
||||
cloudlog.info("Done flashing")
|
||||
|
||||
if panda.bootstub:
|
||||
@@ -112,8 +132,9 @@ def main() -> None:
|
||||
|
||||
# Flash pandas
|
||||
pandas: list[Panda] = []
|
||||
remote_start = get_remote_start_boots_comma(params)
|
||||
for serial in panda_serials:
|
||||
pandas.append(flash_panda(serial))
|
||||
pandas.append(flash_panda(serial, remote_start))
|
||||
|
||||
# Ensure internal panda is present if expected
|
||||
internal_pandas = [panda for panda in pandas if panda.is_internal()]
|
||||
@@ -165,6 +186,10 @@ def main() -> None:
|
||||
first_run = False
|
||||
|
||||
# run pandad with all connected serials as arguments
|
||||
if get_remote_start_boots_comma(params):
|
||||
os.environ["BOARDD_SKIP_FW_CHECK"] = "1"
|
||||
else:
|
||||
os.environ.pop("BOARDD_SKIP_FW_CHECK", None)
|
||||
os.environ['MANAGER_DAEMON'] = 'pandad'
|
||||
process = subprocess.Popen(["./pandad", *panda_serials], cwd=os.path.join(BASEDIR, "selfdrive/pandad"))
|
||||
process.wait()
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
"""Compatibility alias for legacy/misspelled imports.
|
||||
|
||||
Prefer: openpilot.selfdrive.pandad.pandad_api_impl
|
||||
"""
|
||||
|
||||
from openpilot.selfdrive.pandad.pandad_api_impl import can_capnp_to_list, can_list_to_can_capnp
|
||||
|
||||
__all__ = ["can_list_to_can_capnp", "can_capnp_to_list"]
|
||||
@@ -0,0 +1,8 @@
|
||||
"""Compatibility alias for legacy imports.
|
||||
|
||||
Prefer: openpilot.selfdrive.pandad.pandad_api_impl
|
||||
"""
|
||||
|
||||
from openpilot.selfdrive.pandad.pandad_api_impl import can_capnp_to_list, can_list_to_can_capnp
|
||||
|
||||
__all__ = ["can_list_to_can_capnp", "can_capnp_to_list"]
|
||||
@@ -228,7 +228,7 @@ class SelfdriveD:
|
||||
# body always wants to enable
|
||||
self.events.add(EventName.pcmEnable)
|
||||
|
||||
# Disable on rising edge of accelerator or brake. Also disable on brake when speed > 0
|
||||
# Disable on rising edge of accelerator or brake. Also disable on brake when speed > 0.
|
||||
if (CS.gasPressed and not self.CS_prev.gasPressed and self.disengage_on_accelerator) or \
|
||||
(CS.brakePressed and (not self.CS_prev.brakePressed or not CS.standstill)) or \
|
||||
(CS.regenBraking and (not self.CS_prev.regenBraking or not CS.standstill)):
|
||||
@@ -299,15 +299,16 @@ class SelfdriveD:
|
||||
else:
|
||||
self.events.add(EventName.laneChangeBlocked)
|
||||
else:
|
||||
# Keep pre-lane-change prompts visible even when lane-width gate blocks the maneuver.
|
||||
# This preserves the onroad "Changing lanes" guidance while still surfacing
|
||||
# "No Lane Available" when the configured width threshold is not met.
|
||||
if direction == LaneChangeDirection.left:
|
||||
if self.sm['frogpilotPlan'].laneWidthLeft >= self.frogpilot_toggles.lane_detection_width:
|
||||
self.events.add(EventName.preLaneChangeLeft)
|
||||
else:
|
||||
self.events.add(EventName.preLaneChangeLeft)
|
||||
if self.sm['frogpilotPlan'].laneWidthLeft < self.frogpilot_toggles.lane_detection_width:
|
||||
self.frogpilot_events.add(FrogPilotEventName.noLaneAvailable)
|
||||
else:
|
||||
if self.sm['frogpilotPlan'].laneWidthRight >= self.frogpilot_toggles.lane_detection_width:
|
||||
self.events.add(EventName.preLaneChangeRight)
|
||||
else:
|
||||
elif direction == LaneChangeDirection.right:
|
||||
self.events.add(EventName.preLaneChangeRight)
|
||||
if self.sm['frogpilotPlan'].laneWidthRight < self.frogpilot_toggles.lane_detection_width:
|
||||
self.frogpilot_events.add(FrogPilotEventName.noLaneAvailable)
|
||||
elif self.sm['modelV2'].meta.laneChangeState in (LaneChangeState.laneChangeStarting,
|
||||
LaneChangeState.laneChangeFinishing):
|
||||
|
||||
@@ -5,12 +5,14 @@ base_libs = [common, messaging, visionipc, transformations,
|
||||
'm', 'OpenCL', 'ssl', 'crypto', 'pthread'] + qt_env["LIBS"]
|
||||
|
||||
# FrogPilot variables
|
||||
base_libs += ['avcodec', 'avformat', 'avutil', 'yuv', 'OmxCore']
|
||||
base_libs += ['avcodec', 'avformat', 'avutil', 'swresample', 'yuv', 'OmxCore']
|
||||
|
||||
if arch == 'larch64':
|
||||
base_libs.append('EGL')
|
||||
|
||||
if arch == "Darwin":
|
||||
if "OmxCore" in base_libs:
|
||||
base_libs.remove("OmxCore")
|
||||
del base_libs[base_libs.index('OpenCL')]
|
||||
qt_env['FRAMEWORKS'] += ['OpenCL']
|
||||
|
||||
@@ -44,6 +46,7 @@ qt_env['CPPPATH'] += ["../../frogpilot/ui/screenrecorder/openmax/include/"]
|
||||
|
||||
frogpilot_src = ["../../frogpilot/ui/frogpilot_ui.cc", "../../frogpilot/ui/qt/offroad/data_settings.cc",
|
||||
"../../frogpilot/ui/qt/offroad/device_settings.cc", "../../frogpilot/ui/qt/offroad/frogpilot_settings.cc",
|
||||
"../../frogpilot/ui/qt/offroad/expandable_multi_option_dialog.cc",
|
||||
"../../frogpilot/ui/qt/offroad/lateral_settings.cc", "../../frogpilot/ui/qt/offroad/longitudinal_settings.cc",
|
||||
"../../frogpilot/ui/qt/offroad/maps_settings.cc", "../../frogpilot/ui/qt/offroad/model_settings.cc",
|
||||
"../../frogpilot/ui/qt/offroad/navigation_settings.cc", "../../frogpilot/ui/qt/offroad/sounds_settings.cc",
|
||||
@@ -78,7 +81,8 @@ with open(File(translations_assets_src).abspath, 'w') as f:
|
||||
# build assets
|
||||
assets = "#selfdrive/assets/assets.cc"
|
||||
assets_src = "#selfdrive/assets/assets.qrc"
|
||||
qt_env.Command(assets, [assets_src, translations_assets_src], f"rcc $SOURCES -o $TARGET")
|
||||
rcc_bin = qt_env.get("SP_QT_RCC", "rcc")
|
||||
qt_env.Command(assets, [assets_src, translations_assets_src], f"{rcc_bin} $SOURCES -o $TARGET")
|
||||
qt_env.Depends(assets, Glob('#selfdrive/assets/*', exclude=[assets, assets_src, translations_assets_src, "#selfdrive/assets/assets.o"]) + [lrelease])
|
||||
asset_obj = qt_env.Object("assets", assets)
|
||||
|
||||
|
||||
@@ -144,6 +144,7 @@ int cachedFetch(const std::string &cache) {
|
||||
LOGD("Fetching with cache: %s", cache.c_str());
|
||||
|
||||
run(util::string_format("cp -rp %s %s", cache.c_str(), TMP_INSTALL_PATH).c_str());
|
||||
run(util::string_format("cd %s && git remote set-url origin %s", TMP_INSTALL_PATH, GIT_URL.c_str()).c_str());
|
||||
run(util::string_format("cd %s && git remote set-branches --add origin %s", TMP_INSTALL_PATH, migrated_branch.c_str()).c_str());
|
||||
|
||||
renderProgress(10);
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import pyray as rl
|
||||
from enum import IntEnum
|
||||
import cereal.messaging as messaging
|
||||
from openpilot.system.hardware import PC
|
||||
from openpilot.system.ui.lib.application import gui_app
|
||||
from openpilot.selfdrive.ui.layouts.sidebar import Sidebar, SIDEBAR_WIDTH
|
||||
from openpilot.selfdrive.ui.layouts.home import HomeLayout
|
||||
@@ -36,10 +37,12 @@ class MainLayout(Widget):
|
||||
# Set callbacks
|
||||
self._setup_callbacks()
|
||||
|
||||
# Start onboarding if terms or training not completed
|
||||
self._onboarding_window = OnboardingWindow()
|
||||
if not self._onboarding_window.completed:
|
||||
gui_app.set_modal_overlay(self._onboarding_window)
|
||||
# Skip onboarding on desktop; keep normal flow on device.
|
||||
self._onboarding_window = None
|
||||
if not PC:
|
||||
self._onboarding_window = OnboardingWindow()
|
||||
if not self._onboarding_window.completed:
|
||||
gui_app.set_modal_overlay(self._onboarding_window)
|
||||
|
||||
def _render(self, _):
|
||||
self._handle_onroad_transition()
|
||||
|
||||
@@ -25,6 +25,10 @@ DESCRIPTIONS = {
|
||||
"Enable this to switch to openpilot longitudinal control. Enabling Experimental mode is recommended when enabling openpilot longitudinal control alpha. " +
|
||||
"Changing this setting will restart openpilot if the car is powered on."
|
||||
),
|
||||
'use_prebuilt': tr_noop(
|
||||
"When enabled (default), openpilot uses prebuilt binaries at startup and skips source compilation. " +
|
||||
"Disable this if you intend to edit code and compile directly on-device."
|
||||
),
|
||||
}
|
||||
|
||||
|
||||
@@ -43,6 +47,14 @@ class DeveloperLayout(Widget):
|
||||
enabled=ui_state.is_offroad,
|
||||
)
|
||||
|
||||
self._use_prebuilt_toggle = toggle_item(
|
||||
lambda: tr("Use Prebuilt Binaries"),
|
||||
description=lambda: tr(DESCRIPTIONS["use_prebuilt"]),
|
||||
initial_state=self._params.get_bool("UsePrebuilt"),
|
||||
callback=self._on_use_prebuilt,
|
||||
enabled=ui_state.is_offroad,
|
||||
)
|
||||
|
||||
# SSH enable toggle + SSH key management
|
||||
self._ssh_toggle = toggle_item(
|
||||
lambda: tr("Enable SSH"),
|
||||
@@ -85,6 +97,7 @@ class DeveloperLayout(Widget):
|
||||
|
||||
self._scroller = Scroller([
|
||||
self._adb_toggle,
|
||||
self._use_prebuilt_toggle,
|
||||
self._ssh_toggle,
|
||||
self._ssh_keys,
|
||||
self._joystick_toggle,
|
||||
@@ -133,6 +146,7 @@ class DeveloperLayout(Widget):
|
||||
# refresh toggles from params to mirror external changes
|
||||
for key, item in (
|
||||
("AdbEnabled", self._adb_toggle),
|
||||
("UsePrebuilt", self._use_prebuilt_toggle),
|
||||
("SshEnabled", self._ssh_toggle),
|
||||
("JoystickDebugMode", self._joystick_toggle),
|
||||
("LongitudinalManeuverMode", self._long_maneuver_toggle),
|
||||
@@ -152,6 +166,9 @@ class DeveloperLayout(Widget):
|
||||
def _on_enable_ssh(self, state: bool):
|
||||
self._params.put_bool("SshEnabled", state)
|
||||
|
||||
def _on_use_prebuilt(self, state: bool):
|
||||
self._params.put_bool("UsePrebuilt", state)
|
||||
|
||||
def _on_joystick_debug_mode(self, state: bool):
|
||||
self._params.put_bool("JoystickDebugMode", state)
|
||||
self._params.put_bool("LongitudinalManeuverMode", False)
|
||||
|
||||
@@ -5,6 +5,7 @@ from collections.abc import Callable
|
||||
from openpilot.selfdrive.ui.layouts.settings.developer import DeveloperLayout
|
||||
from openpilot.selfdrive.ui.layouts.settings.device import DeviceLayout
|
||||
from openpilot.selfdrive.ui.layouts.settings.firehose import FirehoseLayout
|
||||
from openpilot.selfdrive.ui.layouts.settings.starpilot.main_panel import StarPilotLayout
|
||||
from openpilot.selfdrive.ui.layouts.settings.software import SoftwareLayout
|
||||
from openpilot.selfdrive.ui.layouts.settings.toggles import TogglesLayout
|
||||
from openpilot.system.ui.lib.application import gui_app, FontWeight, MousePos
|
||||
@@ -13,6 +14,7 @@ from openpilot.system.ui.lib.text_measure import measure_text_cached
|
||||
from openpilot.system.ui.lib.wifi_manager import WifiManager
|
||||
from openpilot.system.ui.widgets import Widget
|
||||
from openpilot.system.ui.widgets.network import NetworkUI
|
||||
from openpilot.common.params import Params
|
||||
|
||||
# Constants
|
||||
SIDEBAR_WIDTH = 500
|
||||
@@ -37,6 +39,7 @@ class PanelType(IntEnum):
|
||||
SOFTWARE = 3
|
||||
FIREHOSE = 4
|
||||
DEVELOPER = 5
|
||||
STARPILOT = 6
|
||||
|
||||
|
||||
@dataclass
|
||||
@@ -49,8 +52,13 @@ class PanelInfo:
|
||||
class SettingsLayout(Widget):
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
self._params = Params()
|
||||
self._current_panel = PanelType.DEVICE
|
||||
|
||||
# Panel depth tracking for hierarchical back navigation
|
||||
# 0 = top level (settings main), 1+ = sub-panels (FrogPilot categories)
|
||||
self._panel_depth = 0
|
||||
|
||||
# Panel configuration
|
||||
wifi_manager = WifiManager()
|
||||
wifi_manager.set_active(False)
|
||||
@@ -62,10 +70,15 @@ class SettingsLayout(Widget):
|
||||
PanelType.SOFTWARE: PanelInfo(tr_noop("Software"), SoftwareLayout()),
|
||||
PanelType.FIREHOSE: PanelInfo(tr_noop("Firehose"), FirehoseLayout()),
|
||||
PanelType.DEVELOPER: PanelInfo(tr_noop("Developer"), DeveloperLayout()),
|
||||
PanelType.STARPILOT: PanelInfo(tr_noop("StarPilot"), StarPilotLayout()),
|
||||
}
|
||||
|
||||
# Connect StarPilot depth callback for hierarchical back navigation
|
||||
self._panels[PanelType.STARPILOT].instance.set_depth_callback(self.set_panel_depth)
|
||||
self._panels[PanelType.STARPILOT].instance.set_settings_layout(self)
|
||||
|
||||
self._font_medium = gui_app.font(FontWeight.MEDIUM)
|
||||
self._close_icon = gui_app.texture("icons/close2.png", CLOSE_ICON_SIZE, CLOSE_ICON_SIZE)
|
||||
self._close_icon = gui_app.texture("icons/backspace.png", CLOSE_ICON_SIZE, CLOSE_ICON_SIZE)
|
||||
|
||||
# Callbacks
|
||||
self._close_callback: Callable | None = None
|
||||
@@ -73,6 +86,24 @@ class SettingsLayout(Widget):
|
||||
def set_callbacks(self, on_close: Callable):
|
||||
self._close_callback = on_close
|
||||
|
||||
def set_panel_depth(self, depth: int):
|
||||
self._panel_depth = depth
|
||||
|
||||
def refresh_developer_visibility(self):
|
||||
pass
|
||||
|
||||
def get_panel_depth(self) -> int:
|
||||
return self._panel_depth
|
||||
|
||||
def _handle_back_navigation(self):
|
||||
if self._panel_depth > 0:
|
||||
self._panel_depth -= 1
|
||||
if hasattr(self._panels[PanelType.STARPILOT].instance, 'navigate_back'):
|
||||
self._panels[PanelType.STARPILOT].instance.navigate_back()
|
||||
else:
|
||||
if self._close_callback:
|
||||
self._close_callback()
|
||||
|
||||
def _render(self, rect: rl.Rectangle):
|
||||
# Calculate layout
|
||||
sidebar_rect = rl.Rectangle(rect.x, rect.y, SIDEBAR_WIDTH, rect.height)
|
||||
@@ -85,20 +116,17 @@ class SettingsLayout(Widget):
|
||||
def _draw_sidebar(self, rect: rl.Rectangle):
|
||||
rl.draw_rectangle_rec(rect, SIDEBAR_COLOR)
|
||||
|
||||
# Close button
|
||||
close_btn_rect = rl.Rectangle(
|
||||
rect.x + (rect.width - CLOSE_BTN_SIZE) / 2, rect.y + 60, CLOSE_BTN_SIZE, CLOSE_BTN_SIZE
|
||||
)
|
||||
# Back/Close button - hierarchical navigation
|
||||
back_btn_rect = rl.Rectangle(rect.x + (rect.width - CLOSE_BTN_SIZE) / 2, rect.y + 60, CLOSE_BTN_SIZE, CLOSE_BTN_SIZE)
|
||||
|
||||
pressed = (rl.is_mouse_button_down(rl.MouseButton.MOUSE_BUTTON_LEFT) and
|
||||
rl.check_collision_point_rec(rl.get_mouse_position(), close_btn_rect))
|
||||
pressed = rl.is_mouse_button_down(rl.MouseButton.MOUSE_BUTTON_LEFT) and rl.check_collision_point_rec(rl.get_mouse_position(), back_btn_rect)
|
||||
close_color = CLOSE_BTN_PRESSED if pressed else CLOSE_BTN_COLOR
|
||||
rl.draw_rectangle_rounded(close_btn_rect, 1.0, 20, close_color)
|
||||
rl.draw_rectangle_rounded(back_btn_rect, 1.0, 20, close_color)
|
||||
|
||||
icon_color = rl.Color(255, 255, 255, 255) if not pressed else rl.Color(220, 220, 220, 255)
|
||||
icon_dest = rl.Rectangle(
|
||||
close_btn_rect.x + (close_btn_rect.width - self._close_icon.width) / 2,
|
||||
close_btn_rect.y + (close_btn_rect.height - self._close_icon.height) / 2,
|
||||
back_btn_rect.x + (back_btn_rect.width - self._close_icon.width) / 2,
|
||||
back_btn_rect.y + (back_btn_rect.height - self._close_icon.height) / 2,
|
||||
self._close_icon.width,
|
||||
self._close_icon.height,
|
||||
)
|
||||
@@ -111,12 +139,20 @@ class SettingsLayout(Widget):
|
||||
icon_color,
|
||||
)
|
||||
|
||||
# Store close button rect for click detection
|
||||
self._close_btn_rect = close_btn_rect
|
||||
# Store back button rect for click detection
|
||||
self._back_btn_rect = back_btn_rect
|
||||
|
||||
# Get tuning level to control Developer panel visibility
|
||||
tuning_level_str = self._params.get("TuningLevel", return_default=True, default="1")
|
||||
tuning_level = int(tuning_level_str) if tuning_level_str else 1
|
||||
|
||||
# Navigation buttons
|
||||
y = rect.y + 300
|
||||
for panel_type, panel_info in self._panels.items():
|
||||
# Hide Developer panel unless tuning level >= 3
|
||||
if panel_type == PanelType.DEVELOPER and tuning_level < 3:
|
||||
continue
|
||||
|
||||
button_rect = rl.Rectangle(rect.x + 50, y, rect.width - 150, NAV_BTN_HEIGHT)
|
||||
|
||||
# Button styling
|
||||
@@ -125,9 +161,7 @@ class SettingsLayout(Widget):
|
||||
# Draw button text (right-aligned)
|
||||
panel_name = tr(panel_info.name)
|
||||
text_size = measure_text_cached(self._font_medium, panel_name, 65)
|
||||
text_pos = rl.Vector2(
|
||||
button_rect.x + button_rect.width - text_size.x, button_rect.y + (button_rect.height - text_size.y) / 2
|
||||
)
|
||||
text_pos = rl.Vector2(button_rect.x + button_rect.width - text_size.x, button_rect.y + (button_rect.height - text_size.y) / 2)
|
||||
rl.draw_text_ex(self._font_medium, panel_name, text_pos, 65, 0, text_color)
|
||||
|
||||
# Store button rect for click detection
|
||||
@@ -136,9 +170,7 @@ class SettingsLayout(Widget):
|
||||
y += NAV_BTN_HEIGHT
|
||||
|
||||
def _draw_current_panel(self, rect: rl.Rectangle):
|
||||
rl.draw_rectangle_rounded(
|
||||
rl.Rectangle(rect.x + 10, rect.y + 10, rect.width - 20, rect.height - 20), 0.04, 30, PANEL_COLOR
|
||||
)
|
||||
rl.draw_rectangle_rounded(rl.Rectangle(rect.x + 10, rect.y + 10, rect.width - 20, rect.height - 20), 0.04, 30, PANEL_COLOR)
|
||||
content_rect = rl.Rectangle(rect.x + PANEL_MARGIN, rect.y + 25, rect.width - (PANEL_MARGIN * 2), rect.height - 50)
|
||||
# rl.draw_rectangle_rounded(content_rect, 0.03, 30, PANEL_COLOR)
|
||||
panel = self._panels[self._current_panel]
|
||||
@@ -146,10 +178,9 @@ class SettingsLayout(Widget):
|
||||
panel.instance.render(content_rect)
|
||||
|
||||
def _handle_mouse_release(self, mouse_pos: MousePos) -> None:
|
||||
# Check close button
|
||||
if rl.check_collision_point_rec(mouse_pos, self._close_btn_rect):
|
||||
if self._close_callback:
|
||||
self._close_callback()
|
||||
# Check back/close button - hierarchical navigation
|
||||
if rl.check_collision_point_rec(mouse_pos, self._back_btn_rect):
|
||||
self._handle_back_navigation()
|
||||
return
|
||||
|
||||
# Check navigation buttons
|
||||
|
||||
@@ -7,7 +7,7 @@ from openpilot.system.ui.lib.application import gui_app
|
||||
from openpilot.system.ui.lib.multilang import tr, trn
|
||||
from openpilot.system.ui.widgets import Widget, DialogResult
|
||||
from openpilot.system.ui.widgets.confirm_dialog import ConfirmDialog
|
||||
from openpilot.system.ui.widgets.list_view import button_item, text_item, ListItem
|
||||
from openpilot.system.ui.widgets.list_view import button_item, text_item, toggle_item, ListItem
|
||||
from openpilot.system.ui.widgets.option_dialog import MultiOptionDialog
|
||||
from openpilot.system.ui.widgets.scroller_tici import Scroller
|
||||
|
||||
@@ -54,6 +54,12 @@ class SoftwareLayout(Widget):
|
||||
|
||||
self._onroad_label = ListItem(lambda: tr("Updates are only downloaded while the car is off."))
|
||||
self._version_item = text_item(lambda: tr("Current Version"), ui_state.params.get("UpdaterCurrentDescription") or "")
|
||||
self._auto_updates_toggle = toggle_item(
|
||||
lambda: tr("Automatically Install Updates"),
|
||||
lambda: tr("Automatically install updates when parked with an active internet connection."),
|
||||
initial_state=ui_state.params.get_bool("AutomaticUpdates"),
|
||||
callback=self._on_auto_updates_toggle,
|
||||
)
|
||||
self._download_btn = button_item(lambda: tr("Download"), lambda: tr("CHECK"), callback=self._on_download_update)
|
||||
|
||||
# Install button is initially hidden
|
||||
@@ -73,6 +79,7 @@ class SoftwareLayout(Widget):
|
||||
self._scroller = Scroller([
|
||||
self._onroad_label,
|
||||
self._version_item,
|
||||
self._auto_updates_toggle,
|
||||
self._download_btn,
|
||||
self._install_btn,
|
||||
self._branch_btn,
|
||||
@@ -94,6 +101,7 @@ class SoftwareLayout(Widget):
|
||||
current_release_notes = (ui_state.params.get("UpdaterCurrentReleaseNotes") or b"").decode("utf-8", "replace")
|
||||
self._version_item.action_item.set_text(current_desc)
|
||||
self._version_item.set_description(current_release_notes)
|
||||
self._auto_updates_toggle.action_item.set_state(ui_state.params.get_bool("AutomaticUpdates"))
|
||||
|
||||
# Update download button visibility and state
|
||||
self._download_btn.set_visible(ui_state.is_offroad())
|
||||
@@ -164,6 +172,9 @@ class SoftwareLayout(Widget):
|
||||
self._waiting_start_ts = time.monotonic()
|
||||
os.system("pkill -SIGHUP -f system.updated.updated")
|
||||
|
||||
def _on_auto_updates_toggle(self, enabled: bool):
|
||||
ui_state.params.put_bool("AutomaticUpdates", enabled)
|
||||
|
||||
def _on_uninstall(self):
|
||||
def handle_uninstall_confirmation(result):
|
||||
if result == DialogResult.CONFIRM:
|
||||
|
||||
@@ -0,0 +1,25 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from openpilot.system.ui.lib.multilang import tr, tr_noop
|
||||
from openpilot.system.ui.widgets.list_view import button_item
|
||||
from openpilot.system.ui.widgets.scroller_tici import Scroller
|
||||
from openpilot.selfdrive.ui.layouts.settings.starpilot.panel import StarPilotPanel
|
||||
|
||||
class StarPilotDataLayout(StarPilotPanel):
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
|
||||
items = [
|
||||
button_item(
|
||||
tr_noop("Manage Backups"),
|
||||
lambda: tr("MANAGE"),
|
||||
tr_noop("<b>Create, restore, or delete backups</b> of your StarPilot settings."),
|
||||
),
|
||||
button_item(
|
||||
tr_noop("Manage Storage"),
|
||||
lambda: tr("MANAGE"),
|
||||
tr_noop("<b>View and manage storage usage</b> for models, maps, and other data."),
|
||||
),
|
||||
]
|
||||
|
||||
self._scroller = Scroller(items, line_separator=True, spacing=0)
|
||||
@@ -0,0 +1,25 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from openpilot.system.ui.lib.multilang import tr, tr_noop
|
||||
from openpilot.system.ui.widgets.list_view import button_item
|
||||
from openpilot.system.ui.widgets.scroller_tici import Scroller
|
||||
from openpilot.selfdrive.ui.layouts.settings.starpilot.panel import StarPilotPanel
|
||||
|
||||
class StarPilotDeviceLayout(StarPilotPanel):
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
|
||||
items = [
|
||||
button_item(
|
||||
tr_noop("Screen Controls"),
|
||||
lambda: tr("MANAGE"),
|
||||
tr_noop("<b>Adjust screen brightness, timeout,</b> and other display settings."),
|
||||
),
|
||||
button_item(
|
||||
tr_noop("Device Settings"),
|
||||
lambda: tr("MANAGE"),
|
||||
tr_noop("<b>Configure device-specific options</b> like orientation and controls."),
|
||||
),
|
||||
]
|
||||
|
||||
self._scroller = Scroller(items, line_separator=True, spacing=0)
|
||||
@@ -0,0 +1,429 @@
|
||||
from __future__ import annotations
|
||||
import json
|
||||
import os
|
||||
import threading
|
||||
from pathlib import Path
|
||||
|
||||
from openpilot.selfdrive.ui.ui_state import ui_state
|
||||
from openpilot.frogpilot.assets.model_manager import ModelManager
|
||||
from openpilot.system.hardware import HARDWARE, PC
|
||||
from openpilot.system.ui.lib.application import gui_app
|
||||
from openpilot.system.ui.lib.multilang import tr, tr_noop
|
||||
from openpilot.system.ui.widgets import DialogResult
|
||||
from openpilot.system.ui.widgets.confirm_dialog import ConfirmDialog, alert_dialog
|
||||
from openpilot.system.ui.widgets.selection_dialog import SelectionDialog
|
||||
from openpilot.system.ui.widgets.list_view import toggle_item, button_item
|
||||
from openpilot.system.ui.widgets.scroller_tici import Scroller
|
||||
from openpilot.selfdrive.ui.layouts.settings.starpilot.panel import StarPilotPanel
|
||||
|
||||
class StarPilotDrivingModelLayout(StarPilotPanel):
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
self._toggle_items = {}
|
||||
|
||||
if PC:
|
||||
self._model_dir = Path(os.path.expanduser("~/.comma/frogpilot/data/models"))
|
||||
else:
|
||||
self._model_dir = Path("/data/frogpilot/models")
|
||||
self._model_dir.mkdir(parents=True, exist_ok=True)
|
||||
|
||||
self._available_models = []
|
||||
self._available_model_names = []
|
||||
self._available_model_series = []
|
||||
self._available_model_versions = []
|
||||
self._model_released_dates = {}
|
||||
self._model_file_to_name = {}
|
||||
self._model_series_map = {}
|
||||
self._model_version_map = {}
|
||||
self._current_model_name = ""
|
||||
|
||||
self._toggle_items["ModelRandomizer"] = toggle_item(
|
||||
tr_noop("Model Randomizer"),
|
||||
tr_noop("<b>Driving models are chosen at random each drive</b> and feedback prompts are used to find the model that best suits your needs."),
|
||||
self._params.get_bool("ModelRandomizer"),
|
||||
callback=self._on_model_randomizer_toggled,
|
||||
)
|
||||
|
||||
self._toggle_items["AutomaticallyDownloadModels"] = toggle_item(
|
||||
tr_noop("Automatically Download New Models"),
|
||||
tr_noop("<b>Automatically download new driving models</b> as they become available."),
|
||||
self._params.get_bool("AutomaticallyDownloadModels"),
|
||||
callback=self._on_auto_download_toggled,
|
||||
)
|
||||
|
||||
self._select_model_btn = button_item(
|
||||
tr_noop("Select Driving Model"),
|
||||
lambda: tr("SELECT"),
|
||||
tr_noop("<b>Select the active driving model.</b>"),
|
||||
callback=self._on_select_model_clicked,
|
||||
)
|
||||
self._download_model_btn = button_item(
|
||||
tr_noop("Download Driving Models"),
|
||||
lambda: tr("DOWNLOAD"),
|
||||
tr_noop("<b>Download driving models to the device.</b>"),
|
||||
callback=self._on_download_clicked,
|
||||
)
|
||||
self._delete_model_btn = button_item(
|
||||
tr_noop("Delete Driving Models"),
|
||||
lambda: tr("DELETE"),
|
||||
tr_noop("<b>Delete driving models from the device.</b>"),
|
||||
callback=self._on_delete_clicked,
|
||||
)
|
||||
|
||||
items = [
|
||||
self._select_model_btn,
|
||||
self._download_model_btn,
|
||||
self._delete_model_btn,
|
||||
self._toggle_items["ModelRandomizer"],
|
||||
self._toggle_items["AutomaticallyDownloadModels"],
|
||||
button_item(
|
||||
tr_noop("Manage Model Blacklist"),
|
||||
lambda: tr("MANAGE"),
|
||||
tr_noop("<b>Add or remove models</b> from the Model Randomizer's blacklist."),
|
||||
callback=self._on_blacklist_clicked,
|
||||
),
|
||||
button_item(
|
||||
tr_noop("Manage Model Ratings"),
|
||||
lambda: tr("MANAGE"),
|
||||
tr_noop("<b>Reset or view the saved ratings</b> for the driving models."),
|
||||
callback=self._on_scores_clicked,
|
||||
),
|
||||
]
|
||||
|
||||
self._model_manager = ModelManager(self._params, self._params_memory)
|
||||
self._download_thread = None
|
||||
|
||||
self._scroller = Scroller(items, line_separator=True, spacing=0)
|
||||
self._update_model_metadata()
|
||||
|
||||
def _render(self, rect: rl.Rectangle):
|
||||
self._update_state()
|
||||
super()._render(rect)
|
||||
|
||||
def show_event(self):
|
||||
super().show_event()
|
||||
self._update_model_metadata()
|
||||
|
||||
def refresh_visibility(self):
|
||||
current_level = int(self._params.get("TuningLevel", return_default=True, default="1") or "1")
|
||||
for key, item in self._toggle_items.items():
|
||||
min_level = self._tuning_levels.get(key, 0)
|
||||
item.set_visible(current_level >= min_level)
|
||||
|
||||
def _on_auto_download_toggled(self, state: bool):
|
||||
self._params.put_bool("AutomaticallyDownloadModels", state)
|
||||
|
||||
def _is_model_installed(self, key: str) -> bool:
|
||||
if not key:
|
||||
return False
|
||||
|
||||
has_thneed = False
|
||||
has_policy_meta = False
|
||||
has_policy_tg = False
|
||||
has_vision_meta = False
|
||||
has_vision_tg = False
|
||||
found_any = False
|
||||
|
||||
for file in self._model_dir.iterdir():
|
||||
if not (file.name.startswith(key) or file.name.startswith(key + "_")):
|
||||
continue
|
||||
|
||||
found_any = True
|
||||
ext = file.suffix.lower()
|
||||
base = file.stem
|
||||
|
||||
if ext == ".thneed":
|
||||
has_thneed = True
|
||||
elif ext == ".pkl":
|
||||
if "_driving_policy_metadata" in base:
|
||||
has_policy_meta = True
|
||||
elif "_driving_policy_tinygrad" in base:
|
||||
has_policy_tg = True
|
||||
elif "_driving_vision_metadata" in base:
|
||||
has_vision_meta = True
|
||||
elif "_driving_vision_tinygrad" in base:
|
||||
has_vision_tg = True
|
||||
|
||||
if has_thneed:
|
||||
return True
|
||||
|
||||
return has_policy_meta and has_policy_tg and has_vision_meta and has_vision_tg
|
||||
|
||||
def _update_model_metadata(self):
|
||||
available_models_raw = self._params.get("AvailableModels", encoding='utf-8')
|
||||
if not available_models_raw:
|
||||
manager = ModelManager(self._params, self._params_memory)
|
||||
manager.update_models()
|
||||
available_models_raw = self._params.get("AvailableModels", encoding='utf-8')
|
||||
|
||||
self._available_models = [m.strip() for m in (available_models_raw or "").split(",") if m.strip()]
|
||||
self._available_model_names = [m.strip() for m in (self._params.get("AvailableModelNames", encoding='utf-8') or "").split(",") if m.strip()]
|
||||
self._available_model_series = [m.strip() for m in (self._params.get("AvailableModelSeries", encoding='utf-8') or "").split(",") if m.strip()]
|
||||
self._available_model_versions = [m.strip() for m in (self._params.get("ModelVersions", encoding='utf-8') or "").split(",")]
|
||||
released_dates = (self._params.get("ModelReleasedDates", encoding='utf-8') or "").split(",")
|
||||
|
||||
self._model_file_to_name.clear()
|
||||
self._model_series_map.clear()
|
||||
self._model_version_map.clear()
|
||||
self._model_released_dates.clear()
|
||||
|
||||
size = min(len(self._available_models), len(self._available_model_names))
|
||||
for i in range(size):
|
||||
key = self._available_models[i].strip()
|
||||
name = self._available_model_names[i].strip()
|
||||
if not key or not name:
|
||||
continue
|
||||
|
||||
series = self._available_model_series[i].strip() if i < len(self._available_model_series) else tr("Custom Series")
|
||||
self._model_file_to_name[key] = name
|
||||
self._model_series_map[key] = series
|
||||
if i < len(self._available_model_versions):
|
||||
version = self._available_model_versions[i].strip()
|
||||
if version:
|
||||
self._model_version_map[key] = version
|
||||
if i < len(released_dates):
|
||||
date = released_dates[i].strip()
|
||||
if date:
|
||||
self._model_released_dates[key] = date
|
||||
|
||||
model_key = self._params.get("Model") or self._params.get("DrivingModel")
|
||||
if model_key:
|
||||
if isinstance(model_key, bytes):
|
||||
model_key = model_key.decode()
|
||||
if not model_key or not self._is_model_installed(model_key):
|
||||
model_key = self._params.get_default_value("Model") or self._params.get_default_value("DrivingModel") or ""
|
||||
if isinstance(model_key, bytes):
|
||||
model_key = model_key.decode()
|
||||
|
||||
self._current_model_name = self._model_file_to_name.get(model_key, "Default")
|
||||
self._select_model_btn.action_item._text_source = self._current_model_name
|
||||
|
||||
def _show_selection_dialog(self, title: str, options: dict[str, str] | list[str], current_val: str, on_confirm: Callable):
|
||||
if not options:
|
||||
gui_app.set_modal_overlay(alert_dialog(tr("No options available.")))
|
||||
return
|
||||
|
||||
if isinstance(options, list):
|
||||
def _on_close_list(res, val):
|
||||
if res == DialogResult.CONFIRM:
|
||||
on_confirm(val)
|
||||
dialog = SelectionDialog(title, options, current_val, on_close=_on_close_list)
|
||||
gui_app.set_modal_overlay(dialog)
|
||||
return
|
||||
|
||||
grouped = {}
|
||||
name_to_key = {}
|
||||
for key, name in options.items():
|
||||
series = self._model_series_map.get(key, tr("Custom Series"))
|
||||
if series not in grouped:
|
||||
grouped[series] = []
|
||||
grouped[series].append(name)
|
||||
name_to_key[name] = key
|
||||
|
||||
for series in grouped:
|
||||
grouped[series].sort()
|
||||
|
||||
sorted_series = sorted(grouped.keys())
|
||||
if "StarPilot" in sorted_series:
|
||||
sorted_series.remove("StarPilot")
|
||||
sorted_series.insert(0, "StarPilot")
|
||||
|
||||
final_grouped = {s: grouped[s] for s in sorted_series}
|
||||
|
||||
def _on_close_grouped(res, val):
|
||||
if res == DialogResult.CONFIRM:
|
||||
key = name_to_key.get(val, val)
|
||||
on_confirm(key)
|
||||
|
||||
def _on_favorite_toggled(key):
|
||||
favs = [f.strip() for f in (self._params.get("UserFavorites", encoding='utf-8') or "").split(",") if f.strip()]
|
||||
if key in favs:
|
||||
favs.remove(key)
|
||||
else:
|
||||
favs.append(key)
|
||||
self._params.put("UserFavorites", ",".join(favs))
|
||||
|
||||
user_favs = [f.strip() for f in (self._params.get("UserFavorites", encoding='utf-8') or "").split(",") if f.strip()]
|
||||
comm_favs = [f.strip() for f in (self._params.get("CommunityFavorites", encoding='utf-8') or "").split(",") if f.strip()]
|
||||
|
||||
dialog = SelectionDialog(
|
||||
title,
|
||||
final_grouped,
|
||||
current_val,
|
||||
on_close=_on_close_grouped,
|
||||
model_released_dates=self._model_released_dates,
|
||||
model_file_to_name=self._model_file_to_name,
|
||||
user_favorites=user_favs,
|
||||
community_favorites=comm_favs,
|
||||
on_favorite_toggled=_on_favorite_toggled
|
||||
)
|
||||
gui_app.set_modal_overlay(dialog)
|
||||
|
||||
def _on_select_model_clicked(self):
|
||||
installed_models = {k: v for k, v in self._model_file_to_name.items() if self._is_model_installed(k)}
|
||||
if not installed_models:
|
||||
return
|
||||
|
||||
def _on_confirm(model_key):
|
||||
self._params.put("Model", model_key)
|
||||
self._params.put("DrivingModel", model_key)
|
||||
self._params.put("DrivingModelName", installed_models[model_key])
|
||||
model_version = self._model_version_map.get(model_key, "")
|
||||
if model_version:
|
||||
self._params.put("ModelVersion", model_version)
|
||||
self._params.put("DrivingModelVersion", model_version)
|
||||
self._update_model_metadata()
|
||||
|
||||
if ui_state.started:
|
||||
reboot_dialog = ConfirmDialog(
|
||||
tr("Reboot required to take effect. Reboot now?"),
|
||||
tr("Reboot"),
|
||||
tr("Cancel"),
|
||||
on_close=lambda res: HARDWARE.reboot() if res == DialogResult.CONFIRM else None
|
||||
)
|
||||
gui_app.set_modal_overlay(reboot_dialog)
|
||||
|
||||
self._show_selection_dialog(tr("Select Driving Model"), installed_models, self._current_model_name, _on_confirm)
|
||||
|
||||
def _on_download_clicked(self):
|
||||
is_downloading = self._params_memory.get("ModelToDownload") or self._params_memory.get_bool("DownloadAllModels")
|
||||
if is_downloading:
|
||||
self._params_memory.remove("ModelToDownload")
|
||||
self._params_memory.put_bool("DownloadAllModels", False)
|
||||
self._params_memory.remove("ModelDownloadProgress")
|
||||
return
|
||||
|
||||
not_installed = {k: v for k, v in self._model_file_to_name.items() if not self._is_model_installed(k)}
|
||||
def _on_confirm(model_key):
|
||||
self._params_memory.put("ModelToDownload", model_key)
|
||||
self._params_memory.put("ModelDownloadProgress", "Downloading...")
|
||||
|
||||
self._show_selection_dialog(tr("Select Model to Download"), not_installed, "", _on_confirm)
|
||||
|
||||
def _on_delete_clicked(self):
|
||||
installed = {k: v for k, v in self._model_file_to_name.items() if self._is_model_installed(k)}
|
||||
default_key = self._params.get_default_value("Model") or ""
|
||||
if isinstance(default_key, bytes):
|
||||
default_key = default_key.decode()
|
||||
current_key = self._params.get("Model", encoding='utf-8') or ""
|
||||
deletable = {k: v for k, v in installed.items() if k != default_key and k != current_key}
|
||||
|
||||
def _on_confirm(model_key):
|
||||
def _execute_delete(confirm_res):
|
||||
if confirm_res == DialogResult.CONFIRM:
|
||||
for file in self._model_dir.iterdir():
|
||||
if file.name.startswith(model_key):
|
||||
file.unlink()
|
||||
self._update_model_metadata()
|
||||
|
||||
confirm = ConfirmDialog(
|
||||
tr(f"Are you sure you want to delete the '{deletable[model_key]}' model?"),
|
||||
tr("Delete"),
|
||||
on_close=_execute_delete
|
||||
)
|
||||
gui_app.set_modal_overlay(confirm)
|
||||
|
||||
self._show_selection_dialog(tr("Select Model to Delete"), deletable, "", _on_confirm)
|
||||
|
||||
def _on_blacklist_clicked(self):
|
||||
blacklisted = [m.strip() for m in (self._params.get("BlacklistedModels", encoding='utf-8') or "").split(",") if m.strip()]
|
||||
blacklisted = [b for b in blacklisted if b]
|
||||
|
||||
def _on_action_selected(res, val):
|
||||
if res == DialogResult.CONFIRM:
|
||||
if val == tr("ADD"):
|
||||
blacklistable = {k: v for k, v in self._model_file_to_name.items() if k not in blacklisted}
|
||||
self._show_selection_dialog(tr("Add to Blacklist"), blacklistable, "", lambda k: self._params.put("BlacklistedModels", ",".join(blacklisted + [k])))
|
||||
elif val == tr("REMOVE"):
|
||||
options = {k: self._model_file_to_name.get(k, k) for k in blacklisted}
|
||||
def _remove(k):
|
||||
blacklisted.remove(k)
|
||||
self._params.put("BlacklistedModels", ",".join(blacklisted))
|
||||
self._show_selection_dialog(tr("Remove from Blacklist"), options, "", _remove)
|
||||
elif val == tr("RESET ALL"):
|
||||
self._params.remove("BlacklistedModels")
|
||||
|
||||
dialog = SelectionDialog(
|
||||
tr("Manage Blacklist"),
|
||||
[tr("ADD"), tr("REMOVE"), tr("RESET ALL")],
|
||||
on_close=_on_action_selected
|
||||
)
|
||||
gui_app.set_modal_overlay(dialog)
|
||||
|
||||
def _on_scores_clicked(self):
|
||||
scores_raw = self._params.get("ModelDrivesAndScores", encoding='utf-8') or ""
|
||||
if not scores_raw:
|
||||
gui_app.set_modal_overlay(alert_dialog(tr("No model ratings found.")))
|
||||
return
|
||||
|
||||
try:
|
||||
scores = json.loads(scores_raw)
|
||||
lines = [f"{k}: {v.get('Score', 0)}% ({v.get('Drives', 0)} drives)" for k, v in scores.items()]
|
||||
confirm = ConfirmDialog("\n".join(lines), tr("Close"), rich=True)
|
||||
gui_app.set_modal_overlay(confirm)
|
||||
except:
|
||||
pass
|
||||
|
||||
def _on_model_randomizer_toggled(self, state: bool):
|
||||
self._params.put_bool("ModelRandomizer", state)
|
||||
if state:
|
||||
not_installed = [k for k in self._model_file_to_name if not self._is_model_installed(k)]
|
||||
if not_installed:
|
||||
def _on_download_confirm(res):
|
||||
if res == DialogResult.CONFIRM:
|
||||
self._params_memory.put_bool("DownloadAllModels", True)
|
||||
self._params_memory.put("ModelDownloadProgress", "Downloading...")
|
||||
|
||||
confirm = ConfirmDialog(
|
||||
tr("Model Randomizer works best with all models. Download all now?"),
|
||||
tr("Download All"),
|
||||
on_close=_on_download_confirm
|
||||
)
|
||||
gui_app.set_modal_overlay(confirm)
|
||||
|
||||
def _update_state(self):
|
||||
if not self.is_visible:
|
||||
return
|
||||
|
||||
model_to_download = self._params_memory.get("ModelToDownload", encoding='utf-8') or ""
|
||||
download_all = self._params_memory.get_bool("DownloadAllModels")
|
||||
progress = self._params_memory.get("ModelDownloadProgress", encoding='utf-8') or ""
|
||||
is_downloading = bool(model_to_download or download_all)
|
||||
|
||||
if is_downloading and (self._download_thread is None or not self._download_thread.is_alive()):
|
||||
def _download_task():
|
||||
try:
|
||||
if download_all:
|
||||
print("Starting [All Models] download thread...")
|
||||
self._model_manager.download_all_models()
|
||||
else:
|
||||
print(f"Starting [{model_to_download}] download thread...")
|
||||
self._model_manager.download_model(model_to_download)
|
||||
print("Download thread finished successfully.")
|
||||
except Exception as e:
|
||||
print(f"Download thread CRASHED: {e}")
|
||||
import traceback
|
||||
traceback.print_exc()
|
||||
finally:
|
||||
self._download_thread = None
|
||||
|
||||
self._download_thread = threading.Thread(target=_download_task, daemon=True)
|
||||
self._download_thread.start()
|
||||
|
||||
if is_downloading:
|
||||
self._download_model_btn.action_item._text_source = tr("CANCEL")
|
||||
self._download_model_btn.action_item._value_source = progress if progress else tr("Downloading...")
|
||||
else:
|
||||
self._download_model_btn.action_item._text_source = tr("DOWNLOAD")
|
||||
parked = not ui_state.started
|
||||
online = ui_state.sm["deviceState"].networkType != 0 if ui_state.sm.valid.get("deviceState", False) else True
|
||||
if not online:
|
||||
self._download_model_btn.action_item._value_source = tr("Offline...")
|
||||
elif not parked:
|
||||
self._download_model_btn.action_item._value_source = tr("Not parked")
|
||||
else:
|
||||
self._download_model_btn.action_item._value_source = ""
|
||||
|
||||
all_installed = all(self._is_model_installed(k) for k in self._model_file_to_name)
|
||||
if all_installed:
|
||||
self._download_model_btn.action_item._value_source = tr("All Downloaded!")
|
||||
@@ -0,0 +1,380 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from openpilot.selfdrive.ui.lib.starpilot_state import starpilot_state
|
||||
from openpilot.system.ui.lib.multilang import tr, tr_noop
|
||||
from openpilot.system.ui.widgets import DialogResult
|
||||
from openpilot.system.ui.widgets.confirm_dialog import ConfirmDialog
|
||||
from openpilot.system.ui.widgets.list_view import button_item, value_button_item, button_toggle_item, toggle_item, value_item
|
||||
from openpilot.system.ui.widgets.scroller_tici import Scroller
|
||||
from openpilot.selfdrive.ui.layouts.settings.starpilot.panel import StarPilotPanel
|
||||
|
||||
class StarPilotAdvancedLateralLayout(StarPilotPanel):
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
|
||||
items = [
|
||||
value_button_item(
|
||||
lambda: (
|
||||
tr("Actuator Delay")
|
||||
+ (f" (Default: {starpilot_state.car_state.steerActuatorDelay:.2f})" if starpilot_state.car_state.steerActuatorDelay != 0 else "")
|
||||
),
|
||||
"SteerDelay",
|
||||
min_val=0.01,
|
||||
max_val=1.0,
|
||||
step=0.01,
|
||||
button_text="Reset",
|
||||
button_callback=lambda: self._params.put_float("SteerDelay", starpilot_state.car_state.steerActuatorDelay),
|
||||
description=tr_noop(
|
||||
"<b>The time between openpilot's steering command and the vehicle's response.</b> Increase if the vehicle reacts late; decrease if it feels jumpy. Auto-learned by default."
|
||||
),
|
||||
enabled=lambda: starpilot_state.car_state.steerActuatorDelay != 0,
|
||||
),
|
||||
value_button_item(
|
||||
lambda: tr("Friction") + (f" (Default: {starpilot_state.car_state.friction:.2f})" if starpilot_state.car_state.friction != 0 else ""),
|
||||
"SteerFriction",
|
||||
min_val=0.0,
|
||||
max_val=1.0,
|
||||
step=0.01,
|
||||
button_text="Reset",
|
||||
button_callback=lambda: self._params.put_float("SteerFriction", starpilot_state.car_state.friction),
|
||||
description=tr_noop(
|
||||
"<b>Compensates for steering friction.</b> Increase if the wheel sticks near center; decrease if it jitters. Auto-learned by default."
|
||||
),
|
||||
enabled=lambda: (
|
||||
starpilot_state.car_state.friction != 0
|
||||
and (not starpilot_state.car_state.hasAutoTune or (starpilot_state.car_state.hasAutoTune and self._params.get_bool("ForceAutoTuneOff")))
|
||||
),
|
||||
),
|
||||
value_button_item(
|
||||
lambda: tr("Kp Factor") + (f" (Default: {starpilot_state.car_state.steerKp:.2f})" if starpilot_state.car_state.steerKp != 0 else ""),
|
||||
"SteerKP",
|
||||
min_val=lambda: starpilot_state.car_state.steerKp * 0.5,
|
||||
max_val=lambda: starpilot_state.car_state.steerKp * 1.5,
|
||||
step=0.01,
|
||||
button_text="Reset",
|
||||
button_callback=lambda: self._params.put_float("SteerKP", starpilot_state.car_state.steerKp),
|
||||
description=tr_noop(
|
||||
"<b>How strongly openpilot corrects lane position.</b> Higher is tighter but twitchier; lower is smoother but slower. Auto-learned by default."
|
||||
),
|
||||
enabled=lambda: starpilot_state.car_state.steerKp != 0 and not starpilot_state.car_state.isAngleCar,
|
||||
),
|
||||
value_button_item(
|
||||
lambda: (
|
||||
tr("Lateral Acceleration") + (f" (Default: {starpilot_state.car_state.latAccelFactor:.2f})" if starpilot_state.car_state.latAccelFactor != 0 else "")
|
||||
),
|
||||
"SteerLatAccel",
|
||||
min_val=lambda: starpilot_state.car_state.latAccelFactor * 0.5,
|
||||
max_val=lambda: starpilot_state.car_state.latAccelFactor * 1.5,
|
||||
step=0.01,
|
||||
button_text="Reset",
|
||||
button_callback=lambda: self._params.put_float("SteerLatAccel", starpilot_state.car_state.latAccelFactor),
|
||||
description=tr_noop(
|
||||
"<b>Maps steering torque to turning response.</b> Increase for sharper turns; decrease for gentler steering. Auto-learned by default."
|
||||
),
|
||||
enabled=lambda: (
|
||||
starpilot_state.car_state.latAccelFactor != 0
|
||||
and (not starpilot_state.car_state.hasAutoTune or (starpilot_state.car_state.hasAutoTune and self._params.get_bool("ForceAutoTuneOff")))
|
||||
),
|
||||
),
|
||||
value_button_item(
|
||||
lambda: tr("Steer Ratio") + (f" (Default: {starpilot_state.car_state.steerRatio:.2f})" if starpilot_state.car_state.steerRatio != 0 else ""),
|
||||
"SteerRatio",
|
||||
min_val=lambda: starpilot_state.car_state.steerRatio * 0.5,
|
||||
max_val=lambda: starpilot_state.car_state.steerRatio * 1.5,
|
||||
step=0.01,
|
||||
button_text="Reset",
|
||||
button_callback=lambda: self._params.put_float("SteerRatio", starpilot_state.car_state.steerRatio),
|
||||
description=tr_noop(
|
||||
"<b>The relationship between steering wheel rotation and road wheel angle.</b> Increase if steering feels too quick or twitchy; decrease if it feels too slow or weak. Auto-learned by default."
|
||||
),
|
||||
enabled=lambda: (
|
||||
starpilot_state.car_state.steerRatio != 0
|
||||
and (not starpilot_state.car_state.hasAutoTune or (starpilot_state.car_state.hasAutoTune and self._params.get_bool("ForceAutoTuneOff")))
|
||||
),
|
||||
),
|
||||
toggle_item(
|
||||
tr_noop("Force Auto-Tune On"),
|
||||
tr_noop("<b>Force-enable openpilot's live auto-tuning for \"Friction\" and \"Lateral Acceleration\".</b>"),
|
||||
self._params.get_bool("ForceAutoTune"),
|
||||
callback=lambda x: self._params.put_bool("ForceAutoTune", x),
|
||||
enabled=lambda: not starpilot_state.car_state.hasAutoTune and not starpilot_state.car_state.isAngleCar,
|
||||
),
|
||||
toggle_item(
|
||||
tr_noop("Force Auto-Tune Off"),
|
||||
tr_noop("<b>Force-disable openpilot's live auto-tuning for \"Friction\" and \"Lateral Acceleration\" and use the set value instead.</b>"),
|
||||
self._params.get_bool("ForceAutoTuneOff"),
|
||||
callback=lambda x: self._params.put_bool("ForceAutoTuneOff", x),
|
||||
enabled=lambda: starpilot_state.car_state.hasAutoTune,
|
||||
),
|
||||
toggle_item(
|
||||
tr_noop("Force Torque Controller"),
|
||||
tr_noop("<b>Use torque-based steering control instead of angle-based control for smoother lane keeping, especially in curves.</b>"),
|
||||
self._params.get_bool("ForceTorqueController"),
|
||||
callback=lambda x: self._on_reboot_toggle("ForceTorqueController", x),
|
||||
enabled=lambda: not starpilot_state.car_state.isAngleCar and not starpilot_state.car_state.isTorqueCar,
|
||||
),
|
||||
]
|
||||
self._scroller = Scroller(items, line_separator=True, spacing=0)
|
||||
|
||||
def _on_reboot_toggle(self, key, state):
|
||||
self._params.put_bool(key, state)
|
||||
from openpilot.selfdrive.ui.ui_state import ui_state
|
||||
|
||||
if ui_state.started:
|
||||
from openpilot.system.ui.lib.application import gui_app
|
||||
|
||||
def _confirm_reboot(res):
|
||||
gui_app.set_modal_overlay(None)
|
||||
if res == DialogResult.CONFIRM:
|
||||
from openpilot.system.hardware import HARDWARE
|
||||
HARDWARE.reboot()
|
||||
|
||||
dialog = ConfirmDialog("Reboot required to take effect. Reboot now?", "Reboot", "Cancel", on_close=_confirm_reboot)
|
||||
gui_app.set_modal_overlay(dialog)
|
||||
|
||||
class StarPilotAlwaysOnLateralLayout(StarPilotPanel):
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
|
||||
items = [
|
||||
toggle_item(
|
||||
tr_noop("Always On Lateral"),
|
||||
tr_noop("<b>openpilot's steering remains active even when the accelerator or brake pedals are pressed.</b>"),
|
||||
self._params.get_bool("AlwaysOnLateral"),
|
||||
callback=lambda x: self._on_reboot_toggle("AlwaysOnLateral", x),
|
||||
icon="toggle_icons/icon_always_on_lateral.png",
|
||||
starpilot_icon=True,
|
||||
),
|
||||
toggle_item(
|
||||
tr_noop("Enable With LKAS"),
|
||||
tr_noop("<b>Enable \"Always On Lateral\" whenever \"LKAS\" is on, even when openpilot is not engaged.</b>"),
|
||||
self._params.get_bool("AlwaysOnLateralLKAS"),
|
||||
callback=lambda x: self._params.put_bool("AlwaysOnLateralLKAS", x),
|
||||
enabled=lambda: starpilot_state.car_state.lkasAllowedForAOL,
|
||||
),
|
||||
value_item(
|
||||
tr_noop("Pause on Brake Press Below"),
|
||||
"PauseAOLOnBrake",
|
||||
min_val=0,
|
||||
max_val=99,
|
||||
step=1,
|
||||
unit="mph",
|
||||
description=tr_noop("<b>Pause \"Always On Lateral\" below the set speed while the brake pedal is pressed.</b>"),
|
||||
is_metric=True,
|
||||
),
|
||||
]
|
||||
self._scroller = Scroller(items, line_separator=True, spacing=0)
|
||||
|
||||
def _on_reboot_toggle(self, key, state):
|
||||
self._params.put_bool(key, state)
|
||||
from openpilot.selfdrive.ui.ui_state import ui_state
|
||||
|
||||
if ui_state.started:
|
||||
from openpilot.system.ui.lib.application import gui_app
|
||||
|
||||
def _confirm_reboot(res):
|
||||
gui_app.set_modal_overlay(None)
|
||||
if res == DialogResult.CONFIRM:
|
||||
from openpilot.system.hardware import HARDWARE
|
||||
HARDWARE.reboot()
|
||||
|
||||
dialog = ConfirmDialog("Reboot required to take effect. Reboot now?", "Reboot", "Cancel", on_close=_confirm_reboot)
|
||||
gui_app.set_modal_overlay(dialog)
|
||||
|
||||
class StarPilotLaneChangesLayout(StarPilotPanel):
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
|
||||
def _get_lane_change_labels():
|
||||
labels = {0.0: tr("Instant")}
|
||||
for i in range(1, 51):
|
||||
val = i / 10.0
|
||||
labels[val] = f"{val:.1f} seconds" if val != 1.0 else "1.0 second"
|
||||
return labels
|
||||
|
||||
items = [
|
||||
toggle_item(
|
||||
tr_noop("Lane Changes"),
|
||||
tr_noop("<b>Allow openpilot to change lanes.</b>"),
|
||||
self._params.get_bool("LaneChanges"),
|
||||
callback=lambda x: self._params.put_bool("LaneChanges", x),
|
||||
icon="toggle_icons/icon_lane.png",
|
||||
starpilot_icon=True,
|
||||
),
|
||||
toggle_item(
|
||||
tr_noop("Automatic Lane Changes"),
|
||||
tr_noop("<b>When the turn signal is on, openpilot will automatically change lanes.</b> No steering-wheel nudge required!"),
|
||||
self._params.get_bool("NudgelessLaneChange"),
|
||||
callback=lambda x: self._params.put_bool("NudgelessLaneChange", x),
|
||||
),
|
||||
value_item(
|
||||
tr_noop("Lane Change Delay"),
|
||||
"LaneChangeTime",
|
||||
min_val=0.0,
|
||||
max_val=5.0,
|
||||
step=0.1,
|
||||
description=tr_noop("<b>Delay between turn signal activation and the start of an automatic lane change.</b>"),
|
||||
labels=_get_lane_change_labels(),
|
||||
enabled=lambda: self._params.get_bool("LaneChanges") and self._params.get_bool("NudgelessLaneChange"),
|
||||
),
|
||||
value_item(
|
||||
tr_noop("Minimum Lane Change Speed"),
|
||||
"MinimumLaneChangeSpeed",
|
||||
min_val=0,
|
||||
max_val=99,
|
||||
step=1,
|
||||
unit="mph",
|
||||
description=tr_noop("<b>Lowest speed at which openpilot will change lanes.</b>"),
|
||||
is_metric=True,
|
||||
),
|
||||
value_item(
|
||||
tr_noop("Minimum Lane Width"),
|
||||
"LaneDetectionWidth",
|
||||
min_val=0.0,
|
||||
max_val=15.0,
|
||||
step=0.1,
|
||||
unit="feet",
|
||||
description=tr_noop("<b>Prevent automatic lane changes into lanes narrower than the set width.</b>"),
|
||||
enabled=lambda: self._params.get_bool("LaneChanges") and self._params.get_bool("NudgelessLaneChange"),
|
||||
is_metric=True,
|
||||
),
|
||||
toggle_item(
|
||||
tr_noop("One Lane Change Per Signal"),
|
||||
tr_noop("<b>Limit automatic lane changes to one per turn-signal activation.</b>"),
|
||||
self._params.get_bool("OneLaneChange"),
|
||||
callback=lambda x: self._params.put_bool("OneLaneChange", x),
|
||||
),
|
||||
]
|
||||
self._scroller = Scroller(items, line_separator=True, spacing=0)
|
||||
|
||||
class StarPilotLateralTuneLayout(StarPilotPanel):
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
|
||||
items = [
|
||||
toggle_item(
|
||||
tr_noop("Force Turn Desires Below Lane Change Speed"),
|
||||
tr_noop("<b>While driving below the minimum lane change speed with an active turn signal, instruct openpilot to turn left/right.</b>"),
|
||||
self._params.get_bool("TurnDesires"),
|
||||
callback=lambda x: self._params.put_bool("TurnDesires", x),
|
||||
),
|
||||
toggle_item(
|
||||
tr_noop("Neural Network Feedforward (NNFF)"),
|
||||
tr_noop(
|
||||
"<b>Twilsonco's \"Neural Network FeedForward\" controller.</b> Uses a trained neural network model to predict steering torque based on vehicle speed, roll, and past/future planned path data for smoother, model-based steering."
|
||||
),
|
||||
self._params.get_bool("NNFF"),
|
||||
callback=lambda x: self._on_reboot_toggle("NNFF", x),
|
||||
enabled=lambda: starpilot_state.car_state.hasNNFFLog and not starpilot_state.car_state.isAngleCar,
|
||||
),
|
||||
toggle_item(
|
||||
tr_noop("Neural Network Feedforward (NNFF) Lite"),
|
||||
tr_noop(
|
||||
"<b>A lightweight version of Twilsonco's \"Neural Network FeedForward\" controller.</b> Uses the \"look-ahead\" planned lateral jerk logic from the full model to help smoothen steering adjustments in curves, but does not use the full neural network for torque calculation."
|
||||
),
|
||||
self._params.get_bool("NNFFLite"),
|
||||
callback=lambda x: self._on_reboot_toggle("NNFFLite", x),
|
||||
enabled=lambda: not self._params.get_bool("NNFF") and not starpilot_state.car_state.isAngleCar,
|
||||
),
|
||||
]
|
||||
self._scroller = Scroller(items, line_separator=True, spacing=0)
|
||||
|
||||
def _on_reboot_toggle(self, key, state):
|
||||
self._params.put_bool(key, state)
|
||||
from openpilot.selfdrive.ui.ui_state import ui_state
|
||||
|
||||
if ui_state.started:
|
||||
from openpilot.system.ui.lib.application import gui_app
|
||||
|
||||
def _confirm_reboot(res):
|
||||
gui_app.set_modal_overlay(None)
|
||||
if res == DialogResult.CONFIRM:
|
||||
from openpilot.system.hardware import HARDWARE
|
||||
HARDWARE.reboot()
|
||||
|
||||
dialog = ConfirmDialog("Reboot required to take effect. Reboot now?", "Reboot", "Cancel", on_close=_confirm_reboot)
|
||||
gui_app.set_modal_overlay(dialog)
|
||||
|
||||
class StarPilotLateralQOLLayout(StarPilotPanel):
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
|
||||
items = [
|
||||
value_button_item(
|
||||
tr_noop("Pause Steering Below"),
|
||||
"PauseLateralSpeed",
|
||||
min_val=0,
|
||||
max_val=99,
|
||||
step=1,
|
||||
unit="mph",
|
||||
description=tr_noop("<b>Pause steering below the set speed.</b>"),
|
||||
sub_toggles=[("PauseLateralOnSignal", True)],
|
||||
labels={0: tr_noop("Off")},
|
||||
is_metric=True,
|
||||
)
|
||||
]
|
||||
|
||||
self._scroller = Scroller(items, line_separator=True, spacing=0)
|
||||
|
||||
class StarPilotLateralLayout(StarPilotPanel):
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
|
||||
self._sub_panels = {
|
||||
"advanced_lateral": StarPilotAdvancedLateralLayout(),
|
||||
"always_on_lateral": StarPilotAlwaysOnLateralLayout(),
|
||||
"lane_changes": StarPilotLaneChangesLayout(),
|
||||
"lateral_tune": StarPilotLateralTuneLayout(),
|
||||
"qol": StarPilotLateralQOLLayout(),
|
||||
}
|
||||
|
||||
for name, panel in self._sub_panels.items():
|
||||
if hasattr(panel, 'set_navigate_callback'):
|
||||
panel.set_navigate_callback(self._navigate_to)
|
||||
if hasattr(panel, 'set_back_callback'):
|
||||
panel.set_back_callback(self._go_back)
|
||||
|
||||
items = [
|
||||
button_item(
|
||||
tr_noop("Advanced Lateral Tuning"),
|
||||
lambda: tr("MANAGE"),
|
||||
tr_noop("<b>Advanced steering control changes to fine-tune how openpilot drives.</b>"),
|
||||
callback=lambda: self._navigate_to("advanced_lateral"),
|
||||
icon="toggle_icons/icon_advanced_lateral_tune.png",
|
||||
starpilot_icon=True,
|
||||
),
|
||||
button_item(
|
||||
tr_noop("Always On Lateral"),
|
||||
lambda: tr("MANAGE"),
|
||||
tr_noop("<b>openpilot's steering remains active even when the accelerator or brake pedals are pressed.</b>"),
|
||||
callback=lambda: self._navigate_to("always_on_lateral"),
|
||||
icon="toggle_icons/icon_always_on_lateral.png",
|
||||
starpilot_icon=True,
|
||||
),
|
||||
button_item(
|
||||
tr_noop("Lane Changes"),
|
||||
lambda: tr("MANAGE"),
|
||||
tr_noop("<b>Allow openpilot to change lanes.</b>"),
|
||||
callback=lambda: self._navigate_to("lane_changes"),
|
||||
icon="toggle_icons/icon_lane.png",
|
||||
starpilot_icon=True,
|
||||
),
|
||||
button_item(
|
||||
tr_noop("Lateral Tuning"),
|
||||
lambda: tr("MANAGE"),
|
||||
tr_noop("<b>Miscellaneous steering control changes</b> to fine-tune how openpilot drives."),
|
||||
callback=lambda: self._navigate_to("lateral_tune"),
|
||||
icon="toggle_icons/icon_lateral_tune.png",
|
||||
starpilot_icon=True,
|
||||
),
|
||||
button_item(
|
||||
tr_noop("Quality of Life"),
|
||||
lambda: tr("MANAGE"),
|
||||
tr_noop("<b>Steering control changes to fine-tune how openpilot drives.</b>"),
|
||||
callback=lambda: self._navigate_to("qol"),
|
||||
icon="toggle_icons/icon_quality_of_life.png",
|
||||
starpilot_icon=True,
|
||||
),
|
||||
]
|
||||
|
||||
self._scroller = Scroller(items, line_separator=True, spacing=0)
|
||||
@@ -0,0 +1,368 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from openpilot.system.ui.lib.multilang import tr, tr_noop
|
||||
from openpilot.system.ui.widgets.list_view import button_item, value_item
|
||||
from openpilot.system.ui.widgets.scroller_tici import Scroller
|
||||
from openpilot.selfdrive.ui.layouts.settings.starpilot.panel import StarPilotPanel
|
||||
|
||||
class StarPilotLongitudinalLayout(StarPilotPanel):
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
|
||||
# Main panel items
|
||||
items = [
|
||||
button_item(
|
||||
tr_noop("Advanced Longitudinal Tuning"),
|
||||
lambda: tr("MANAGE"),
|
||||
tr_noop("<b>Advanced acceleration and braking control changes</b> to fine-tune how openpilot drives."),
|
||||
),
|
||||
button_item(
|
||||
tr_noop("Conditional Experimental Mode"),
|
||||
lambda: tr("MANAGE"),
|
||||
tr_noop("<b>Automatically switch to Experimental Mode</b> when set conditions are met."),
|
||||
),
|
||||
button_item(
|
||||
tr_noop("Curve Speed Controller"),
|
||||
lambda: tr("MANAGE"),
|
||||
tr_noop("<b>Automatically slow down for upcoming curves</b> using data learned from your driving style."),
|
||||
),
|
||||
button_item(
|
||||
tr_noop("Driving Personalities"),
|
||||
lambda: tr("MANAGE"),
|
||||
tr_noop("<b>Customize the Driving Personalities</b> to better match your driving style."),
|
||||
),
|
||||
button_item(
|
||||
tr_noop("Longitudinal Tuning"),
|
||||
lambda: tr("MANAGE"),
|
||||
tr_noop("<b>Acceleration and braking control changes</b> to fine-tune how openpilot drives."),
|
||||
),
|
||||
button_item(
|
||||
tr_noop("Quality of Life"),
|
||||
lambda: tr("MANAGE"),
|
||||
tr_noop("<b>Miscellaneous acceleration and braking control changes</b> to fine-tune how openpilot drives."),
|
||||
),
|
||||
button_item(
|
||||
tr_noop("Weather"),
|
||||
lambda: tr("MANAGE"),
|
||||
tr_noop("<b>Adjust driving behavior</b> based on weather conditions."),
|
||||
callback=lambda: self._navigate_to("weather"),
|
||||
),
|
||||
]
|
||||
|
||||
self._scroller = Scroller(items, line_separator=True, spacing=0)
|
||||
|
||||
# Sub-panels
|
||||
self._sub_panels = {
|
||||
"weather": StarPilotWeatherLayout(),
|
||||
"low_visibility": StarPilotLowVisibilityLayout(),
|
||||
"rain": StarPilotRainLayout(),
|
||||
"rainstorm": StarPilotRainStormLayout(),
|
||||
"snow": StarPilotSnowLayout(),
|
||||
}
|
||||
|
||||
# Wire up navigation callbacks for sub-panels
|
||||
for name, panel in self._sub_panels.items():
|
||||
if hasattr(panel, 'set_navigate_callback'):
|
||||
panel.set_navigate_callback(self._navigate_to)
|
||||
if hasattr(panel, 'set_back_callback'):
|
||||
panel.set_back_callback(self._go_back)
|
||||
|
||||
class StarPilotWeatherLayout(StarPilotPanel):
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
|
||||
items = [
|
||||
button_item(
|
||||
tr_noop("Low Visibility"),
|
||||
lambda: tr("MANAGE"),
|
||||
tr_noop("<b>Driving adjustments for fog, haze, or other low-visibility conditions.</b>"),
|
||||
callback=lambda: self._navigate("low_visibility"),
|
||||
),
|
||||
button_item(
|
||||
tr_noop("Rain"),
|
||||
lambda: tr("MANAGE"),
|
||||
tr_noop("<b>Driving adjustments for rainy conditions.</b>"),
|
||||
callback=lambda: self._navigate("rain"),
|
||||
),
|
||||
button_item(
|
||||
tr_noop("Rainstorms"),
|
||||
lambda: tr("MANAGE"),
|
||||
tr_noop("<b>Driving adjustments for rainstorms.</b>"),
|
||||
callback=lambda: self._navigate("rainstorm"),
|
||||
),
|
||||
button_item(
|
||||
tr_noop("Snow"),
|
||||
lambda: tr("MANAGE"),
|
||||
tr_noop("<b>Driving adjustments for snowy conditions.</b>"),
|
||||
callback=lambda: self._navigate("snow"),
|
||||
),
|
||||
]
|
||||
|
||||
self._scroller = Scroller(items, line_separator=True, spacing=0)
|
||||
|
||||
def _navigate(self, sub_panel: str):
|
||||
if self._navigate_callback:
|
||||
self._navigate_callback(sub_panel)
|
||||
|
||||
class StarPilotLowVisibilityLayout(StarPilotPanel):
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
|
||||
def save_following(value: float):
|
||||
self._params.put_int("IncreaseFollowingLowVisibility", int(value))
|
||||
|
||||
def save_stopped_distance(value: float):
|
||||
self._params.put_int("IncreasedStoppedDistanceLowVisibility", int(value))
|
||||
|
||||
def save_reduce_accel(value: float):
|
||||
self._params.put_int("ReduceAccelerationLowVisibility", int(value))
|
||||
|
||||
def save_reduce_lateral(value: float):
|
||||
self._params.put_int("ReduceLateralAccelerationLowVisibility", int(value))
|
||||
|
||||
items = [
|
||||
value_item(
|
||||
tr_noop("Increase Following Distance by:"),
|
||||
lambda: self._params.get_int("IncreaseFollowingLowVisibility", return_default=True, default="0"),
|
||||
min_val=0,
|
||||
max_val=3,
|
||||
step=0.5,
|
||||
unit=" seconds",
|
||||
description=tr_noop("<b>Add extra space behind lead vehicles in low visibility.</b> Increase for more space; decrease for tighter gaps."),
|
||||
callback=save_following,
|
||||
),
|
||||
value_item(
|
||||
tr_noop("Increase Stopped Distance by:"),
|
||||
lambda: self._params.get_int("IncreasedStoppedDistanceLowVisibility", return_default=True, default="0"),
|
||||
min_val=0,
|
||||
max_val=10,
|
||||
step=1,
|
||||
unit=" feet",
|
||||
description=tr_noop("<b>Add extra buffer when stopped behind vehicles in low visibility.</b> Increase for more room; decrease for shorter gaps."),
|
||||
callback=save_stopped_distance,
|
||||
),
|
||||
value_item(
|
||||
tr_noop("Reduce Acceleration by:"),
|
||||
lambda: self._params.get_int("ReduceAccelerationLowVisibility", return_default=True, default="0"),
|
||||
min_val=0,
|
||||
max_val=50,
|
||||
step=5,
|
||||
unit="%",
|
||||
description=tr_noop(
|
||||
"<b>Lower the maximum acceleration in low visibility.</b> Increase for softer takeoffs; decrease for quicker but less stable takeoffs."
|
||||
),
|
||||
callback=save_reduce_accel,
|
||||
),
|
||||
value_item(
|
||||
tr_noop("Reduce Speed in Curves by:"),
|
||||
lambda: self._params.get_int("ReduceLateralAccelerationLowVisibility", return_default=True, default="0"),
|
||||
min_val=0,
|
||||
max_val=50,
|
||||
step=5,
|
||||
unit="%",
|
||||
description=tr_noop(
|
||||
"<b>Lower the desired speed while driving through curves in low visibility.</b> Increase for safer, gentler turns; decrease for more aggressive driving in curves."
|
||||
),
|
||||
callback=save_reduce_lateral,
|
||||
),
|
||||
]
|
||||
|
||||
self._scroller = Scroller(items, line_separator=True, spacing=0)
|
||||
|
||||
class StarPilotRainLayout(StarPilotPanel):
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
|
||||
def save_following(value: float):
|
||||
self._params.put_int("IncreaseFollowingRain", int(value))
|
||||
|
||||
def save_stopped_distance(value: float):
|
||||
self._params.put_int("IncreasedStoppedDistanceRain", int(value))
|
||||
|
||||
def save_reduce_accel(value: float):
|
||||
self._params.put_int("ReduceAccelerationRain", int(value))
|
||||
|
||||
def save_reduce_lateral(value: float):
|
||||
self._params.put_int("ReduceLateralAccelerationRain", int(value))
|
||||
|
||||
items = [
|
||||
value_item(
|
||||
tr_noop("Increase Following Distance by:"),
|
||||
lambda: self._params.get_int("IncreaseFollowingRain", return_default=True, default="0"),
|
||||
min_val=0,
|
||||
max_val=3,
|
||||
step=0.5,
|
||||
unit=" seconds",
|
||||
description=tr_noop("<b>Add extra space behind lead vehicles in rain.</b> Increase for more space; decrease for tighter gaps."),
|
||||
callback=save_following,
|
||||
),
|
||||
value_item(
|
||||
tr_noop("Increase Stopped Distance by:"),
|
||||
lambda: self._params.get_int("IncreasedStoppedDistanceRain", return_default=True, default="0"),
|
||||
min_val=0,
|
||||
max_val=10,
|
||||
step=1,
|
||||
unit=" feet",
|
||||
description=tr_noop("<b>Add extra buffer when stopped behind vehicles in rain.</b> Increase for more room; decrease for shorter gaps."),
|
||||
callback=save_stopped_distance,
|
||||
),
|
||||
value_item(
|
||||
tr_noop("Reduce Acceleration by:"),
|
||||
lambda: self._params.get_int("ReduceAccelerationRain", return_default=True, default="0"),
|
||||
min_val=0,
|
||||
max_val=50,
|
||||
step=5,
|
||||
unit="%",
|
||||
description=tr_noop("<b>Lower the maximum acceleration in rain.</b> Increase for softer takeoffs; decrease for quicker but less stable takeoffs."),
|
||||
callback=save_reduce_accel,
|
||||
),
|
||||
value_item(
|
||||
tr_noop("Reduce Speed in Curves by:"),
|
||||
lambda: self._params.get_int("ReduceLateralAccelerationRain", return_default=True, default="0"),
|
||||
min_val=0,
|
||||
max_val=50,
|
||||
step=5,
|
||||
unit="%",
|
||||
description=tr_noop(
|
||||
"<b>Lower the desired speed while driving through curves in rain.</b> Increase for safer, gentler turns; decrease for more aggressive driving in curves."
|
||||
),
|
||||
callback=save_reduce_lateral,
|
||||
),
|
||||
]
|
||||
|
||||
self._scroller = Scroller(items, line_separator=True, spacing=0)
|
||||
|
||||
class StarPilotRainStormLayout(StarPilotPanel):
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
|
||||
def save_following(value: float):
|
||||
self._params.put_int("IncreaseFollowingRainStorm", int(value))
|
||||
|
||||
def save_stopped_distance(value: float):
|
||||
self._params.put_int("IncreasedStoppedDistanceRainStorm", int(value))
|
||||
|
||||
def save_reduce_accel(value: float):
|
||||
self._params.put_int("ReduceAccelerationRainStorm", int(value))
|
||||
|
||||
def save_reduce_lateral(value: float):
|
||||
self._params.put_int("ReduceLateralAccelerationRainStorm", int(value))
|
||||
|
||||
items = [
|
||||
value_item(
|
||||
tr_noop("Increase Following Distance by:"),
|
||||
lambda: self._params.get_int("IncreaseFollowingRainStorm", return_default=True, default="0"),
|
||||
min_val=0,
|
||||
max_val=3,
|
||||
step=0.5,
|
||||
unit=" seconds",
|
||||
description=tr_noop("<b>Add extra space behind lead vehicles in a rainstorm.</b> Increase for more space; decrease for tighter gaps."),
|
||||
callback=save_following,
|
||||
),
|
||||
value_item(
|
||||
tr_noop("Increase Stopped Distance by:"),
|
||||
lambda: self._params.get_int("IncreasedStoppedDistanceRainStorm", return_default=True, default="0"),
|
||||
min_val=0,
|
||||
max_val=10,
|
||||
step=1,
|
||||
unit=" feet",
|
||||
description=tr_noop("<b>Add extra buffer when stopped behind vehicles in a rainstorm.</b> Increase for more room; decrease for shorter gaps."),
|
||||
callback=save_stopped_distance,
|
||||
),
|
||||
value_item(
|
||||
tr_noop("Reduce Acceleration by:"),
|
||||
lambda: self._params.get_int("ReduceAccelerationRainStorm", return_default=True, default="0"),
|
||||
min_val=0,
|
||||
max_val=10,
|
||||
step=1,
|
||||
unit=" feet",
|
||||
description=tr_noop("<b>Add extra buffer when stopped behind vehicles in rain.</b> Increase for more room; decrease for shorter gaps."),
|
||||
callback=save_reduce_accel,
|
||||
),
|
||||
value_item(
|
||||
tr_noop("Reduce Acceleration by:"),
|
||||
lambda: self._params.get_int("ReduceAccelerationRain", return_default=True, default="0"),
|
||||
min_val=0,
|
||||
max_val=50,
|
||||
step=5,
|
||||
unit="%",
|
||||
description=tr_noop("<b>Lower the maximum acceleration in rain.</b> Increase for softer takeoffs; decrease for quicker but less stable takeoffs."),
|
||||
),
|
||||
value_item(
|
||||
tr_noop("Reduce Speed in Curves by:"),
|
||||
lambda: self._params.get_int("ReduceLateralAccelerationRain", return_default=True, default="0"),
|
||||
min_val=0,
|
||||
max_val=50,
|
||||
step=5,
|
||||
unit="%",
|
||||
description=tr_noop(
|
||||
"<b>Lower the desired speed while driving through curves in rain.</b> Increase for safer, gentler turns; decrease for more aggressive driving in curves."
|
||||
),
|
||||
callback=save_reduce_lateral,
|
||||
),
|
||||
]
|
||||
|
||||
self._scroller = Scroller(items, line_separator=True, spacing=0)
|
||||
|
||||
class StarPilotSnowLayout(StarPilotPanel):
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
|
||||
def save_following(value: float):
|
||||
self._params.put_int("IncreaseFollowingSnow", int(value))
|
||||
|
||||
def save_stopped_distance(value: float):
|
||||
self._params.put_int("IncreasedStoppedDistanceSnow", int(value))
|
||||
|
||||
def save_reduce_accel(value: float):
|
||||
self._params.put_int("ReduceAccelerationSnow", int(value))
|
||||
|
||||
def save_reduce_lateral(value: float):
|
||||
self._params.put_int("ReduceLateralAccelerationSnow", int(value))
|
||||
|
||||
items = [
|
||||
value_item(
|
||||
tr_noop("Increase Following Distance by:"),
|
||||
lambda: self._params.get_int("IncreaseFollowingSnow", return_default=True, default="0"),
|
||||
min_val=0,
|
||||
max_val=3,
|
||||
step=0.5,
|
||||
unit=" seconds",
|
||||
description=tr_noop("<b>Add extra space behind lead vehicles in snow.</b> Increase for more space; decrease for tighter gaps."),
|
||||
callback=save_following,
|
||||
),
|
||||
value_item(
|
||||
tr_noop("Increase Stopped Distance by:"),
|
||||
lambda: self._params.get_int("IncreasedStoppedDistanceSnow", return_default=True, default="0"),
|
||||
min_val=0,
|
||||
max_val=10,
|
||||
step=1,
|
||||
unit=" feet",
|
||||
description=tr_noop("<b>Add extra buffer when stopped behind vehicles in snow.</b> Increase for more room; decrease for shorter gaps."),
|
||||
callback=save_stopped_distance,
|
||||
),
|
||||
value_item(
|
||||
tr_noop("Reduce Acceleration by:"),
|
||||
lambda: self._params.get_int("ReduceAccelerationSnow", return_default=True, default="0"),
|
||||
min_val=0,
|
||||
max_val=50,
|
||||
step=5,
|
||||
unit="%",
|
||||
description=tr_noop("<b>Lower the maximum acceleration in snow.</b> Increase for softer takeoffs; decrease for quicker but less stable takeoffs."),
|
||||
callback=save_reduce_accel,
|
||||
),
|
||||
value_item(
|
||||
tr_noop("Reduce Speed in Curves by:"),
|
||||
lambda: self._params.get_int("ReduceLateralAccelerationSnow", return_default=True, default="0"),
|
||||
min_val=0,
|
||||
max_val=50,
|
||||
step=5,
|
||||
unit="%",
|
||||
description=tr_noop(
|
||||
"<b>Lower the desired speed while driving through curves in snow.</b> Increase for safer, gentler turns; decrease for more aggressive driving in curves."
|
||||
),
|
||||
callback=save_reduce_lateral,
|
||||
),
|
||||
]
|
||||
|
||||
self._scroller = Scroller(items, line_separator=True, spacing=0)
|
||||
@@ -0,0 +1,324 @@
|
||||
from __future__ import annotations
|
||||
from collections.abc import Callable
|
||||
import pyray as rl
|
||||
|
||||
from openpilot.common.params import Params
|
||||
from openpilot.system.ui.widgets import Widget
|
||||
from openpilot.system.ui.lib.multilang import tr, tr_noop
|
||||
from openpilot.system.ui.widgets.scroller_tici import Scroller
|
||||
from openpilot.system.ui.widgets.list_view import multiple_button_item, category_buttons_item
|
||||
|
||||
from openpilot.selfdrive.ui.layouts.settings.starpilot.panel import StarPilotPanelType, StarPilotPanelInfo
|
||||
from openpilot.selfdrive.ui.layouts.settings.starpilot.sounds import StarPilotSoundsLayout
|
||||
from openpilot.selfdrive.ui.layouts.settings.starpilot.driving_model import StarPilotDrivingModelLayout
|
||||
from openpilot.selfdrive.ui.layouts.settings.starpilot.longitudinal import StarPilotLongitudinalLayout
|
||||
from openpilot.selfdrive.ui.layouts.settings.starpilot.lateral import StarPilotLateralLayout
|
||||
from openpilot.selfdrive.ui.layouts.settings.starpilot.maps import StarPilotMapsLayout
|
||||
from openpilot.selfdrive.ui.layouts.settings.starpilot.navigation import StarPilotNavigationLayout
|
||||
from openpilot.selfdrive.ui.layouts.settings.starpilot.data import StarPilotDataLayout
|
||||
from openpilot.selfdrive.ui.layouts.settings.starpilot.device import StarPilotDeviceLayout
|
||||
from openpilot.selfdrive.ui.layouts.settings.starpilot.utilities import StarPilotUtilitiesLayout
|
||||
from openpilot.selfdrive.ui.layouts.settings.starpilot.visuals import StarPilotVisualsLayout
|
||||
from openpilot.selfdrive.ui.layouts.settings.starpilot.themes import StarPilotThemesLayout
|
||||
from openpilot.selfdrive.ui.layouts.settings.starpilot.vehicle import StarPilotVehicleSettingsLayout
|
||||
from openpilot.selfdrive.ui.layouts.settings.starpilot.wheel import StarPilotWheelLayout
|
||||
|
||||
STARPILOT_ICONS_DIR = "toggle_icons"
|
||||
|
||||
class StarPilotLayout(Widget):
|
||||
CATEGORIES = [
|
||||
{
|
||||
"title": "Alerts and Sounds",
|
||||
"icon": "icon_sound.png",
|
||||
"desc": "<b>Adjust alert volumes and enable custom notifications.</b>",
|
||||
"buttons": [("MANAGE", "SOUNDS", 0)],
|
||||
},
|
||||
{
|
||||
"title": "Driving Controls",
|
||||
"icon": "icon_steering.png",
|
||||
"desc": "<b>Fine-tune custom StarPilot acceleration, braking, and steering controls.</b>",
|
||||
"buttons": [("DRIVING MODEL", "DRIVING_MODEL", 0), ("GAS / BRAKE", "LONGITUDINAL", 0), ("STEERING", "LATERAL", 0)],
|
||||
},
|
||||
{
|
||||
"title": "Navigation",
|
||||
"icon": "icon_navigate.png",
|
||||
"desc": "<b>Download map data for the Speed Limit Controller.</b>",
|
||||
"buttons": [("MAP DATA", "MAPS", 0), ("NAVIGATION", "NAVIGATION", 1)],
|
||||
},
|
||||
{
|
||||
"title": "System Settings",
|
||||
"icon": "icon_system.png",
|
||||
"desc": "<b>Manage backups, device settings, screen options, storage, and tools to keep StarPilot running smoothly.</b>",
|
||||
"buttons": [("DATA", "DATA", 0), ("DEVICE CONTROLS", "DEVICE", 2), ("UTILITIES", "UTILITIES", 0)],
|
||||
},
|
||||
{
|
||||
"title": "Theme and Appearance",
|
||||
"icon": "icon_display.png",
|
||||
"desc": "<b>Customize the look of the driving screen and interface, including themes!</b>",
|
||||
"buttons": [("APPEARANCE", "VISUALS", 0), ("THEME", "THEMES", 0)],
|
||||
},
|
||||
{
|
||||
"title": "Vehicle Settings",
|
||||
"icon": "icon_vehicle.png",
|
||||
"desc": "<b>Configure car-specific options and steering wheel button mappings.</b>",
|
||||
"buttons": [("VEHICLE SETTINGS", "VEHICLE", 0), ("WHEEL CONTROLS", "WHEEL", 1)],
|
||||
},
|
||||
]
|
||||
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
self._params = Params()
|
||||
|
||||
self._current_panel = StarPilotPanelType.MAIN
|
||||
self._depth_callback: Callable | None = None
|
||||
self._settings_layout = None
|
||||
|
||||
self._panel_stack: list[tuple[StarPilotPanelType, str]] = []
|
||||
self._sub_panel_callbacks: dict[str, Callable] = {}
|
||||
|
||||
self._toggle_tuning_levels: dict[str, int] = {}
|
||||
all_keys = self._params.all_keys()
|
||||
for key in all_keys:
|
||||
level = self._params.get_tuning_level(key)
|
||||
if level is not None:
|
||||
self._toggle_tuning_levels[key] = level
|
||||
|
||||
self._panels = {
|
||||
StarPilotPanelType.MAIN: StarPilotPanelInfo("", None),
|
||||
StarPilotPanelType.SOUNDS: StarPilotPanelInfo(tr_noop("Sounds"), StarPilotSoundsLayout()),
|
||||
StarPilotPanelType.DRIVING_MODEL: StarPilotPanelInfo(tr_noop("Driving Model"), StarPilotDrivingModelLayout()),
|
||||
StarPilotPanelType.LONGITUDINAL: StarPilotPanelInfo(tr_noop("Gas / Brake"), StarPilotLongitudinalLayout()),
|
||||
StarPilotPanelType.LATERAL: StarPilotPanelInfo(tr_noop("Steering"), StarPilotLateralLayout()),
|
||||
StarPilotPanelType.MAPS: StarPilotPanelInfo(tr_noop("Map Data"), StarPilotMapsLayout()),
|
||||
StarPilotPanelType.NAVIGATION: StarPilotPanelInfo(tr_noop("Navigation"), StarPilotNavigationLayout()),
|
||||
StarPilotPanelType.DATA: StarPilotPanelInfo(tr_noop("Data Management"), StarPilotDataLayout()),
|
||||
StarPilotPanelType.DEVICE: StarPilotPanelInfo(tr_noop("Device Controls"), StarPilotDeviceLayout()),
|
||||
StarPilotPanelType.UTILITIES: StarPilotPanelInfo(tr_noop("Utilities"), StarPilotUtilitiesLayout()),
|
||||
StarPilotPanelType.VISUALS: StarPilotPanelInfo(tr_noop("Appearance"), StarPilotVisualsLayout()),
|
||||
StarPilotPanelType.THEMES: StarPilotPanelInfo(tr_noop("Themes"), StarPilotThemesLayout()),
|
||||
StarPilotPanelType.VEHICLE: StarPilotPanelInfo(tr_noop("Vehicle Settings"), StarPilotVehicleSettingsLayout()),
|
||||
StarPilotPanelType.WHEEL: StarPilotPanelInfo(tr_noop("Wheel Controls"), StarPilotWheelLayout()),
|
||||
}
|
||||
|
||||
self._setup_longitudinal_sub_panels()
|
||||
self._setup_sounds_sub_panels()
|
||||
self._setup_lateral_sub_panels()
|
||||
|
||||
for panel_type in [
|
||||
StarPilotPanelType.SOUNDS,
|
||||
StarPilotPanelType.DRIVING_MODEL,
|
||||
]:
|
||||
panel = self._panels[panel_type].instance
|
||||
if panel and hasattr(panel, 'set_tuning_levels'):
|
||||
panel.set_tuning_levels(self._toggle_tuning_levels)
|
||||
|
||||
tuning_levels = [tr("Minimal"), tr("Standard"), tr("Advanced"), tr("Developer")]
|
||||
tuning_level_str = self._params.get("TuningLevel", return_default=True, default="1")
|
||||
current_tuning_level = int(tuning_level_str) if tuning_level_str else 1
|
||||
|
||||
items = [
|
||||
multiple_button_item(
|
||||
tr_noop("Tuning Level"),
|
||||
tr_noop(
|
||||
"Choose your tuning level. Lower levels keep it simple; higher levels unlock more toggles for finer control.\n\n"
|
||||
"Minimal - Ideal for those who prefer simplicity or ease of use\n"
|
||||
"Standard - Recommended for most users for a balanced experience\n"
|
||||
"Advanced - Fine-tuning for experienced users\n"
|
||||
"Developer - Highly customizable settings for seasoned enthusiasts"
|
||||
),
|
||||
tuning_levels,
|
||||
current_tuning_level,
|
||||
callback=self._on_tuning_level_changed,
|
||||
icon=f"{STARPILOT_ICONS_DIR}/icon_tuning.png",
|
||||
starpilot_icon=True,
|
||||
),
|
||||
]
|
||||
|
||||
panel_type_map = {
|
||||
"SOUNDS": StarPilotPanelType.SOUNDS,
|
||||
"DRIVING_MODEL": StarPilotPanelType.DRIVING_MODEL,
|
||||
"LONGITUDINAL": StarPilotPanelType.LONGITUDINAL,
|
||||
"LATERAL": StarPilotPanelType.LATERAL,
|
||||
"MAPS": StarPilotPanelType.MAPS,
|
||||
"NAVIGATION": StarPilotPanelType.NAVIGATION,
|
||||
"DATA": StarPilotPanelType.DATA,
|
||||
"DEVICE": StarPilotPanelType.DEVICE,
|
||||
"UTILITIES": StarPilotPanelType.UTILITIES,
|
||||
"VISUALS": StarPilotPanelType.VISUALS,
|
||||
"THEMES": StarPilotPanelType.THEMES,
|
||||
"VEHICLE": StarPilotPanelType.VEHICLE,
|
||||
"WHEEL": StarPilotPanelType.WHEEL,
|
||||
}
|
||||
|
||||
for cat in self.CATEGORIES:
|
||||
filtered_buttons = []
|
||||
for btn_label, panel_key, min_level in cat["buttons"]:
|
||||
if current_tuning_level >= min_level:
|
||||
panel_type = panel_type_map[panel_key]
|
||||
callback = lambda p=panel_type: self._set_current_panel(p)
|
||||
filtered_buttons.append((tr(btn_label), callback))
|
||||
|
||||
if filtered_buttons:
|
||||
full_icon_path = f"{STARPILOT_ICONS_DIR}/{cat['icon']}"
|
||||
item = category_buttons_item(
|
||||
title=tr(cat["title"]),
|
||||
buttons=filtered_buttons,
|
||||
description=tr(cat["desc"]),
|
||||
icon=full_icon_path,
|
||||
starpilot_icon=True,
|
||||
)
|
||||
items.append(item)
|
||||
|
||||
self._main_scroller = Scroller(items, line_separator=True, spacing=0)
|
||||
|
||||
def set_depth_callback(self, callback: Callable):
|
||||
self._depth_callback = callback
|
||||
|
||||
def set_settings_layout(self, settings_layout):
|
||||
self._settings_layout = settings_layout
|
||||
|
||||
def navigate_back(self):
|
||||
if self._panel_stack:
|
||||
self._panel_stack.pop()
|
||||
if self._panel_stack:
|
||||
self._update_sub_panel_visibility()
|
||||
else:
|
||||
self._set_current_panel(StarPilotPanelType.MAIN)
|
||||
|
||||
def _push_sub_panel(self, sub_panel_name: str):
|
||||
self._panel_stack.append((self._current_panel, sub_panel_name))
|
||||
self._update_sub_panel_visibility()
|
||||
|
||||
def _update_sub_panel_visibility(self):
|
||||
if self._current_panel == StarPilotPanelType.LONGITUDINAL:
|
||||
longitudinal = self._panels[StarPilotPanelType.LONGITUDINAL].instance
|
||||
if longitudinal:
|
||||
current_sub = self._get_current_sub_panel()
|
||||
if hasattr(longitudinal, 'set_current_sub_panel'):
|
||||
longitudinal.set_current_sub_panel(current_sub)
|
||||
elif self._current_panel == StarPilotPanelType.SOUNDS:
|
||||
sounds = self._panels[StarPilotPanelType.SOUNDS].instance
|
||||
if sounds:
|
||||
current_sub = self._get_current_sub_panel()
|
||||
if hasattr(sounds, '_navigate_to'):
|
||||
sounds._current_sub_panel = current_sub
|
||||
|
||||
def _get_current_sub_panel(self) -> str:
|
||||
if self._panel_stack and self._panel_stack[-1][0] == self._current_panel:
|
||||
return self._panel_stack[-1][1]
|
||||
return ""
|
||||
|
||||
def _setup_longitudinal_sub_panels(self):
|
||||
longitudinal = self._panels[StarPilotPanelType.LONGITUDINAL].instance
|
||||
if longitudinal and hasattr(longitudinal, 'set_navigate_callback'):
|
||||
longitudinal.set_navigate_callback(self._push_sub_panel)
|
||||
|
||||
def _setup_sounds_sub_panels(self):
|
||||
sounds = self._panels[StarPilotPanelType.SOUNDS].instance
|
||||
if sounds and hasattr(sounds, 'set_navigate_callback'):
|
||||
sounds.set_navigate_callback(self._push_sub_panel)
|
||||
|
||||
def _setup_lateral_sub_panels(self):
|
||||
lateral = self._panels[StarPilotPanelType.LATERAL].instance
|
||||
if lateral and hasattr(lateral, 'set_navigate_callback'):
|
||||
lateral.set_navigate_callback(self._push_sub_panel)
|
||||
|
||||
def _on_tuning_level_changed(self, index: int):
|
||||
self._params.put_nonblocking("TuningLevel", index)
|
||||
if self._settings_layout:
|
||||
self._settings_layout.refresh_developer_visibility()
|
||||
for panel_info in self._panels.values():
|
||||
panel = panel_info.instance
|
||||
if panel and hasattr(panel, 'refresh_visibility'):
|
||||
panel.refresh_visibility()
|
||||
self._rebuild_main_scroller(index)
|
||||
|
||||
def _rebuild_main_scroller(self, tuning_level: int):
|
||||
tuning_levels = [tr("Minimal"), tr("Standard"), tr("Advanced"), tr("Developer")]
|
||||
|
||||
items = [
|
||||
multiple_button_item(
|
||||
tr_noop("Tuning Level"),
|
||||
tr_noop(
|
||||
"Choose your tuning level. Lower levels keep it simple; higher levels unlock more toggles for finer control.\n\n"
|
||||
"Minimal - Ideal for those who prefer simplicity or ease of use\n"
|
||||
"Standard - Recommended for most users for a balanced experience\n"
|
||||
"Advanced - Fine-tuning for experienced users\n"
|
||||
"Developer - Highly customizable settings for seasoned enthusiasts"
|
||||
),
|
||||
tuning_levels,
|
||||
tuning_level,
|
||||
callback=self._on_tuning_level_changed,
|
||||
icon=f"{STARPILOT_ICONS_DIR}/icon_tuning.png",
|
||||
starpilot_icon=True,
|
||||
),
|
||||
]
|
||||
|
||||
panel_type_map = {
|
||||
"SOUNDS": StarPilotPanelType.SOUNDS,
|
||||
"DRIVING_MODEL": StarPilotPanelType.DRIVING_MODEL,
|
||||
"LONGITUDINAL": StarPilotPanelType.LONGITUDINAL,
|
||||
"LATERAL": StarPilotPanelType.LATERAL,
|
||||
"MAPS": StarPilotPanelType.MAPS,
|
||||
"NAVIGATION": StarPilotPanelType.NAVIGATION,
|
||||
"DATA": StarPilotPanelType.DATA,
|
||||
"DEVICE": StarPilotPanelType.DEVICE,
|
||||
"UTILITIES": StarPilotPanelType.UTILITIES,
|
||||
"VISUALS": StarPilotPanelType.VISUALS,
|
||||
"THEMES": StarPilotPanelType.THEMES,
|
||||
"VEHICLE": StarPilotPanelType.VEHICLE,
|
||||
"WHEEL": StarPilotPanelType.WHEEL,
|
||||
}
|
||||
|
||||
for cat in self.CATEGORIES:
|
||||
filtered_buttons = []
|
||||
for btn_label, panel_key, min_level in cat["buttons"]:
|
||||
if tuning_level >= min_level:
|
||||
panel_type = panel_type_map[panel_key]
|
||||
callback = lambda p=panel_type: self._set_current_panel(p)
|
||||
filtered_buttons.append((tr(btn_label), callback))
|
||||
|
||||
if filtered_buttons:
|
||||
full_icon_path = f"{STARPILOT_ICONS_DIR}/{cat['icon']}"
|
||||
item = category_buttons_item(
|
||||
title=tr(cat["title"]),
|
||||
buttons=filtered_buttons,
|
||||
description=tr(cat["desc"]),
|
||||
icon=full_icon_path,
|
||||
starpilot_icon=True,
|
||||
)
|
||||
items.append(item)
|
||||
|
||||
self._main_scroller = Scroller(items, line_separator=True, spacing=0)
|
||||
|
||||
def _set_current_panel(self, panel_type: StarPilotPanelType):
|
||||
if panel_type != self._current_panel:
|
||||
if self._current_panel != StarPilotPanelType.MAIN:
|
||||
self._panels[self._current_panel].instance.hide_event()
|
||||
self._current_panel = panel_type
|
||||
if panel_type != StarPilotPanelType.MAIN:
|
||||
self._panels[panel_type].instance.show_event()
|
||||
|
||||
depth = 1 if panel_type != StarPilotPanelType.MAIN else 0
|
||||
if self._depth_callback:
|
||||
self._depth_callback(depth)
|
||||
|
||||
def _render(self, rect: rl.Rectangle):
|
||||
if self._current_panel == StarPilotPanelType.MAIN:
|
||||
self._main_scroller.render(rect)
|
||||
else:
|
||||
panel = self._panels[self._current_panel]
|
||||
if panel.instance:
|
||||
panel.instance.render(rect)
|
||||
|
||||
def show_event(self):
|
||||
super().show_event()
|
||||
if self._current_panel == StarPilotPanelType.MAIN:
|
||||
self._main_scroller.show_event()
|
||||
else:
|
||||
self._panels[self._current_panel].instance.show_event()
|
||||
|
||||
def hide_event(self):
|
||||
super().hide_event()
|
||||
if self._current_panel != StarPilotPanelType.MAIN:
|
||||
self._panels[self._current_panel].instance.hide_event()
|
||||
@@ -0,0 +1,25 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from openpilot.system.ui.lib.multilang import tr, tr_noop
|
||||
from openpilot.system.ui.widgets.list_view import button_item
|
||||
from openpilot.system.ui.widgets.scroller_tici import Scroller
|
||||
from openpilot.selfdrive.ui.layouts.settings.starpilot.panel import StarPilotPanel
|
||||
|
||||
class StarPilotMapsLayout(StarPilotPanel):
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
|
||||
items = [
|
||||
button_item(
|
||||
tr_noop("Download Map Data"),
|
||||
lambda: tr("DOWNLOAD"),
|
||||
tr_noop("<b>Download map data</b> for the Speed Limit Controller."),
|
||||
),
|
||||
button_item(
|
||||
tr_noop("Manage Map Data"),
|
||||
lambda: tr("MANAGE"),
|
||||
tr_noop("<b>View or delete downloaded map data.</b>"),
|
||||
),
|
||||
]
|
||||
|
||||
self._scroller = Scroller(items, line_separator=True, spacing=0)
|
||||
@@ -0,0 +1,25 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from openpilot.system.ui.lib.multilang import tr, tr_noop
|
||||
from openpilot.system.ui.widgets.list_view import toggle_item, button_item
|
||||
from openpilot.system.ui.widgets.scroller_tici import Scroller
|
||||
from openpilot.selfdrive.ui.layouts.settings.starpilot.panel import StarPilotPanel
|
||||
|
||||
class StarPilotNavigationLayout(StarPilotPanel):
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
|
||||
items = [
|
||||
toggle_item(
|
||||
tr_noop("Use Live Map Data"),
|
||||
tr_noop("<b>Use live map data</b> for real-time navigation updates."),
|
||||
False,
|
||||
),
|
||||
button_item(
|
||||
tr_noop("Navigation Settings"),
|
||||
lambda: tr("MANAGE"),
|
||||
tr_noop("<b>Configure navigation-specific options</b> like route preferences."),
|
||||
),
|
||||
]
|
||||
|
||||
self._scroller = Scroller(items, line_separator=True, spacing=0)
|
||||
@@ -0,0 +1,74 @@
|
||||
from __future__ import annotations
|
||||
from collections.abc import Callable
|
||||
from dataclasses import dataclass
|
||||
from enum import IntEnum
|
||||
|
||||
import pyray as rl
|
||||
|
||||
from openpilot.common.params import Params
|
||||
from openpilot.system.ui.widgets import Widget
|
||||
|
||||
class StarPilotPanelType(IntEnum):
|
||||
MAIN = 0
|
||||
SOUNDS = 1
|
||||
DRIVING_MODEL = 2
|
||||
LONGITUDINAL = 3
|
||||
LATERAL = 4
|
||||
MAPS = 5
|
||||
NAVIGATION = 6
|
||||
DATA = 7
|
||||
DEVICE = 8
|
||||
UTILITIES = 9
|
||||
VISUALS = 10
|
||||
THEMES = 11
|
||||
VEHICLE = 12
|
||||
WHEEL = 13
|
||||
|
||||
@dataclass
|
||||
class StarPilotPanelInfo:
|
||||
name: str
|
||||
instance: Widget
|
||||
|
||||
class StarPilotPanel(Widget):
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
self._params = Params()
|
||||
self._params_memory = Params(memory=True)
|
||||
self._tuning_levels: dict[str, int] = {}
|
||||
self._navigate_callback: Callable | None = None
|
||||
self._back_callback: Callable | None = None
|
||||
self._current_sub_panel = ""
|
||||
self._sub_panels: dict[str, Widget] = {}
|
||||
self._scroller = None
|
||||
|
||||
def set_navigate_callback(self, callback: Callable):
|
||||
self._navigate_callback = callback
|
||||
|
||||
def set_back_callback(self, callback: Callable):
|
||||
self._back_callback = callback
|
||||
|
||||
def set_tuning_levels(self, levels: dict[str, int]):
|
||||
self._tuning_levels = levels
|
||||
|
||||
def _navigate_to(self, sub_panel: str):
|
||||
self._current_sub_panel = sub_panel
|
||||
if self._navigate_callback:
|
||||
self._navigate_callback(sub_panel)
|
||||
|
||||
def _go_back(self):
|
||||
self._current_sub_panel = ""
|
||||
if self._back_callback:
|
||||
self._back_callback()
|
||||
|
||||
def _render(self, rect: rl.Rectangle):
|
||||
if self._current_sub_panel and self._current_sub_panel in self._sub_panels:
|
||||
self._sub_panels[self._current_sub_panel].render(rect)
|
||||
elif self._scroller:
|
||||
self._scroller.render(rect)
|
||||
|
||||
def show_event(self):
|
||||
super().show_event()
|
||||
if self._current_sub_panel and self._current_sub_panel in self._sub_panels:
|
||||
self._sub_panels[self._current_sub_panel].show_event()
|
||||
elif self._scroller:
|
||||
self._scroller.show_event()
|
||||
@@ -0,0 +1,271 @@
|
||||
from __future__ import annotations
|
||||
import subprocess
|
||||
from pathlib import Path
|
||||
|
||||
from openpilot.common.basedir import BASEDIR
|
||||
from openpilot.frogpilot.common.frogpilot_variables import ACTIVE_THEME_PATH
|
||||
from openpilot.system.ui.lib.multilang import tr, tr_noop
|
||||
from openpilot.system.ui.widgets.list_view import button_item, toggle_item, value_button_item
|
||||
from openpilot.system.ui.widgets.scroller_tici import Scroller
|
||||
from openpilot.selfdrive.ui.ui_state import ui_state
|
||||
from openpilot.selfdrive.ui.lib.starpilot_state import starpilot_state
|
||||
from openpilot.selfdrive.ui.layouts.settings.starpilot.panel import StarPilotPanel
|
||||
|
||||
class StarPilotSoundsLayout(StarPilotPanel):
|
||||
VOLUME_KEYS = [
|
||||
"DisengageVolume",
|
||||
"EngageVolume",
|
||||
"PromptVolume",
|
||||
"PromptDistractedVolume",
|
||||
"RefuseVolume",
|
||||
"WarningSoftVolume",
|
||||
"WarningImmediateVolume",
|
||||
]
|
||||
CUSTOM_ALERTS_KEYS = [
|
||||
"GoatScream",
|
||||
"GreenLightAlert",
|
||||
"LeadDepartingAlert",
|
||||
"LoudBlindspotAlert",
|
||||
"SpeedLimitChangedAlert",
|
||||
]
|
||||
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
|
||||
items = [
|
||||
button_item(
|
||||
tr_noop("Alert Volume Controller"),
|
||||
lambda: tr("MANAGE"),
|
||||
tr_noop("<b>Set how loud each type of openpilot alert is</b> to keep routine prompts from becoming distracting."),
|
||||
callback=lambda: self._navigate_to("volume_control"),
|
||||
icon="toggle_icons/icon_mute.png",
|
||||
starpilot_icon=True,
|
||||
),
|
||||
button_item(
|
||||
tr_noop("StarPilot Alerts"),
|
||||
lambda: tr("MANAGE"),
|
||||
tr_noop("<b>Optional StarPilot alerts</b> that highlight driving events in a more noticeable way."),
|
||||
callback=lambda: self._navigate_to("custom_alerts"),
|
||||
icon="toggle_icons/icon_green_light.png",
|
||||
starpilot_icon=True,
|
||||
),
|
||||
]
|
||||
|
||||
self._scroller = Scroller(items, line_separator=True, spacing=0)
|
||||
|
||||
self._sub_panels = {
|
||||
"volume_control": StarPilotVolumeControlLayout(),
|
||||
"custom_alerts": StarPilotCustomAlertsLayout(),
|
||||
}
|
||||
|
||||
# Wire up navigation callbacks for sub-panels
|
||||
for name, panel in self._sub_panels.items():
|
||||
if hasattr(panel, 'set_navigate_callback'):
|
||||
panel.set_navigate_callback(self._navigate_to)
|
||||
if hasattr(panel, 'set_back_callback'):
|
||||
panel.set_back_callback(self._go_back)
|
||||
|
||||
def refresh_visibility(self):
|
||||
for panel in self._sub_panels.values():
|
||||
if hasattr(panel, 'refresh_visibility'):
|
||||
panel.refresh_visibility()
|
||||
|
||||
class StarPilotVolumeControlLayout(StarPilotPanel):
|
||||
VOLUME_INFO = {
|
||||
"DisengageVolume": {
|
||||
"title": tr_noop("Disengage Volume"),
|
||||
"desc": tr_noop(
|
||||
"<b>Set the volume for alerts when openpilot disengages.</b><br><br>Examples include: \"Cruise Fault: Restart the Car\", \"Parking Brake Engaged\", \"Pedal Pressed\"."
|
||||
),
|
||||
"min": 0,
|
||||
},
|
||||
"EngageVolume": {
|
||||
"title": tr_noop("Engage Volume"),
|
||||
"desc": tr_noop("<b>Set the volume for the chime when openpilot engages</b>, such as after pressing the \"RESUME\" or \"SET\" steering wheel buttons."),
|
||||
"min": 0,
|
||||
},
|
||||
"PromptVolume": {
|
||||
"title": tr_noop("Prompt Volume"),
|
||||
"desc": tr_noop(
|
||||
"<b>Set the volume for prompts that need attention.</b><br><br>Examples include: \"Car Detected in Blindspot\", \"Steering Temporarily Unavailable\", \"Turn Exceeds Steering Limit\"."
|
||||
),
|
||||
"min": 0,
|
||||
},
|
||||
"PromptDistractedVolume": {
|
||||
"title": tr_noop("Prompt Distracted Volume"),
|
||||
"desc": tr_noop(
|
||||
"<b>Set the volume for prompts when openpilot detects driver distraction or unresponsiveness.</b><br><br>Examples include: \"Pay Attention\", \"Touch Steering Wheel\"."
|
||||
),
|
||||
"min": 0,
|
||||
},
|
||||
"RefuseVolume": {
|
||||
"title": tr_noop("Refuse Volume"),
|
||||
"desc": tr_noop(
|
||||
"<b>Set the volume for alerts when openpilot refuses to engage.</b><br><br>Examples include: \"Brake Hold Active\", \"Door Open\", \"Seatbelt Unlatched\"."
|
||||
),
|
||||
"min": 0,
|
||||
},
|
||||
"WarningSoftVolume": {
|
||||
"title": tr_noop("Warning Soft Volume"),
|
||||
"desc": tr_noop(
|
||||
"<b>Set the volume for softer warnings about potential risks.</b><br><br>Examples include: \"BRAKE! Risk of Collision\", \"Steering Temporarily Unavailable\"."
|
||||
),
|
||||
"min": 25,
|
||||
},
|
||||
"WarningImmediateVolume": {
|
||||
"title": tr_noop("Warning Immediate Volume"),
|
||||
"desc": tr_noop(
|
||||
"<b>Set the volume for the loudest warnings that require urgent attention.</b><br><br>Examples include: \"DISENGAGE IMMEDIATELY — Driver Distracted\", \"DISENGAGE IMMEDIATELY — Driver Unresponsive\"."
|
||||
),
|
||||
"min": 25,
|
||||
},
|
||||
}
|
||||
|
||||
_sound_player_process = None
|
||||
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
self._init_sound_player()
|
||||
|
||||
volume_labels = {0.0: tr("Muted"), 101.0: tr("Auto")}
|
||||
for i in range(1, 101):
|
||||
volume_labels[float(i)] = f"{i}%"
|
||||
|
||||
items = []
|
||||
for key in StarPilotSoundsLayout.VOLUME_KEYS:
|
||||
info = self.VOLUME_INFO[key]
|
||||
items.append(
|
||||
value_button_item(
|
||||
info["title"],
|
||||
key,
|
||||
min_val=info["min"],
|
||||
max_val=101,
|
||||
step=1,
|
||||
button_text="Test",
|
||||
button_callback=lambda k=key: self._test_sound(k),
|
||||
description=info["desc"],
|
||||
labels=volume_labels,
|
||||
)
|
||||
)
|
||||
|
||||
self._scroller = Scroller(items, line_separator=True, spacing=0)
|
||||
|
||||
@classmethod
|
||||
def _init_sound_player(cls):
|
||||
if cls._sound_player_process is not None:
|
||||
return
|
||||
|
||||
program = """
|
||||
import numpy as np
|
||||
import sounddevice as sd
|
||||
import sys
|
||||
import wave
|
||||
|
||||
while True:
|
||||
try:
|
||||
line = sys.stdin.readline()
|
||||
if not line:
|
||||
break
|
||||
path, volume = line.strip().split('|')
|
||||
|
||||
sound_file = wave.open(path, 'rb')
|
||||
audio = np.frombuffer(sound_file.readframes(sound_file.getnframes()), dtype=np.int16).astype(np.float32) / 32768.0
|
||||
|
||||
sd.play(audio * float(volume), sound_file.getframerate())
|
||||
sd.wait()
|
||||
except Exception:
|
||||
pass
|
||||
"""
|
||||
|
||||
cls._sound_player_process = subprocess.Popen(
|
||||
["python3", "-u", "-c", program],
|
||||
stdin=subprocess.PIPE,
|
||||
stdout=subprocess.DEVNULL,
|
||||
stderr=subprocess.DEVNULL,
|
||||
)
|
||||
|
||||
def _play_sound_offroad(self, key: str):
|
||||
base_name = key.replace("Volume", "")
|
||||
snake_case = "".join(["_" + c.lower() if c.isupper() else c for c in base_name]).lstrip("_")
|
||||
stock_path = Path(BASEDIR) / "selfdrive" / "assets" / "sounds" / f"{snake_case}.wav"
|
||||
theme_path = ACTIVE_THEME_PATH / "sounds" / f"{snake_case}.wav"
|
||||
|
||||
sound_path = theme_path if theme_path.exists() else stock_path
|
||||
if not sound_path.exists():
|
||||
return
|
||||
|
||||
volume = self._params.get_int(key, return_default=True, default=100) / 100.0
|
||||
|
||||
try:
|
||||
self._sound_player_process.stdin.write(f"{sound_path}|{volume}\n".encode())
|
||||
self._sound_player_process.stdin.flush()
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
def _test_sound(self, key: str):
|
||||
base_name = key.replace("Volume", "")
|
||||
if ui_state.started:
|
||||
camel_case = base_name[0].lower() + base_name[1:]
|
||||
self._params_memory.put("TestAlert", camel_case)
|
||||
else:
|
||||
self._play_sound_offroad(key)
|
||||
|
||||
class StarPilotCustomAlertsLayout(StarPilotPanel):
|
||||
ALERT_INFO = {
|
||||
"GoatScream": {
|
||||
"title": tr_noop("Goat Scream"),
|
||||
"desc": tr_noop(
|
||||
"<b>Play the infamous \"Goat Scream\" when the steering controller reaches its limit.</b> Based on the \"Turn Exceeds Steering Limit\" event."
|
||||
),
|
||||
},
|
||||
"GreenLightAlert": {
|
||||
"title": tr_noop("Green Light Alert"),
|
||||
"desc": tr_noop(
|
||||
"<b>Play an alert when the model predicts a red light has turned green.</b><br><br><i><b>Disclaimer</b>: openpilot does not explicitly detect traffic lights. This alert is based on end-to-end model predictions from camera input and may trigger even when the light has not changed.</i>"
|
||||
),
|
||||
},
|
||||
"LeadDepartingAlert": {
|
||||
"title": tr_noop("Lead Departing Alert"),
|
||||
"desc": tr_noop("<b>Play an alert when the lead vehicle departs from a stop.</b>"),
|
||||
},
|
||||
"LoudBlindspotAlert": {
|
||||
"title": tr_noop("Loud \"Car Detected in Blindspot\" Alert"),
|
||||
"desc": tr_noop(
|
||||
"<b>Play a louder alert if a vehicle is in the blind spot when attempting to change lanes.</b> Based on the \"Car Detected in Blindspot\" event."
|
||||
),
|
||||
},
|
||||
"SpeedLimitChangedAlert": {
|
||||
"title": tr_noop("Speed Limit Changed Alert"),
|
||||
"desc": tr_noop("<b>Play an alert when the posted speed limit changes.</b>"),
|
||||
},
|
||||
}
|
||||
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
self._toggle_items = {}
|
||||
|
||||
for key in StarPilotSoundsLayout.CUSTOM_ALERTS_KEYS:
|
||||
info = self.ALERT_INFO[key]
|
||||
self._toggle_items[key] = toggle_item(
|
||||
info["title"],
|
||||
info["desc"],
|
||||
self._params.get_bool(key),
|
||||
callback=lambda s, k=key: self._params.put_bool(k, s),
|
||||
)
|
||||
|
||||
self._scroller = Scroller(list(self._toggle_items.values()), line_separator=True, spacing=0)
|
||||
|
||||
def refresh_visibility(self):
|
||||
current_level = int(self._params.get("TuningLevel", return_default=True, default="1") or "1")
|
||||
for key, item in self._toggle_items.items():
|
||||
min_level = self._tuning_levels.get(key, 0)
|
||||
visible = current_level >= min_level
|
||||
|
||||
if key == "LoudBlindspotAlert":
|
||||
visible &= starpilot_state.car_state.hasBSM
|
||||
elif key == "SpeedLimitChangedAlert":
|
||||
visible &= self._params.get_bool("ShowSpeedLimits") or (
|
||||
starpilot_state.car_state.hasOpenpilotLongitudinal and self._params.get_bool("SpeedLimitController")
|
||||
)
|
||||
|
||||
item.set_visible(visible)
|
||||
@@ -0,0 +1,30 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from openpilot.system.ui.lib.multilang import tr, tr_noop
|
||||
from openpilot.system.ui.widgets.list_view import button_item
|
||||
from openpilot.system.ui.widgets.scroller_tici import Scroller
|
||||
from openpilot.selfdrive.ui.layouts.settings.starpilot.panel import StarPilotPanel
|
||||
|
||||
class StarPilotThemesLayout(StarPilotPanel):
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
|
||||
items = [
|
||||
button_item(
|
||||
tr_noop("Select Theme"),
|
||||
lambda: tr("SELECT"),
|
||||
tr_noop("<b>Select a theme</b> for the StarPilot interface."),
|
||||
),
|
||||
button_item(
|
||||
tr_noop("Holiday Themes"),
|
||||
lambda: tr("MANAGE"),
|
||||
tr_noop("<b>Enable or disable holiday-themed visuals.</b>"),
|
||||
),
|
||||
button_item(
|
||||
tr_noop("Custom Theme"),
|
||||
lambda: tr("MANAGE"),
|
||||
tr_noop("<b>Create or import a custom theme.</b>"),
|
||||
),
|
||||
]
|
||||
|
||||
self._scroller = Scroller(items, line_separator=True, spacing=0)
|
||||
@@ -0,0 +1,30 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from openpilot.system.ui.lib.multilang import tr, tr_noop
|
||||
from openpilot.system.ui.widgets.list_view import button_item
|
||||
from openpilot.system.ui.widgets.scroller_tici import Scroller
|
||||
from openpilot.selfdrive.ui.layouts.settings.starpilot.panel import StarPilotPanel
|
||||
|
||||
class StarPilotUtilitiesLayout(StarPilotPanel):
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
|
||||
items = [
|
||||
button_item(
|
||||
tr_noop("Update StarPilot"),
|
||||
lambda: tr("CHECK"),
|
||||
tr_noop("<b>Check for updates</b> and update StarPilot to the latest version."),
|
||||
),
|
||||
button_item(
|
||||
tr_noop("Reset Settings"),
|
||||
lambda: tr("RESET"),
|
||||
tr_noop("<b>Reset all StarPilot settings</b> to their default values."),
|
||||
),
|
||||
button_item(
|
||||
tr_noop("View Logs"),
|
||||
lambda: tr("VIEW"),
|
||||
tr_noop("<b>View StarPilot logs</b> for debugging."),
|
||||
),
|
||||
]
|
||||
|
||||
self._scroller = Scroller(items, line_separator=True, spacing=0)
|
||||
@@ -0,0 +1,20 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from openpilot.system.ui.lib.multilang import tr, tr_noop
|
||||
from openpilot.system.ui.widgets.list_view import button_item
|
||||
from openpilot.system.ui.widgets.scroller_tici import Scroller
|
||||
from openpilot.selfdrive.ui.layouts.settings.starpilot.panel import StarPilotPanel
|
||||
|
||||
class StarPilotVehicleSettingsLayout(StarPilotPanel):
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
|
||||
items = [
|
||||
button_item(
|
||||
tr_noop("Vehicle Settings"),
|
||||
lambda: tr("MANAGE"),
|
||||
tr_noop("<b>Configure car-specific options</b> like model name and features."),
|
||||
),
|
||||
]
|
||||
|
||||
self._scroller = Scroller(items, line_separator=True, spacing=0)
|
||||
@@ -0,0 +1,40 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from openpilot.system.ui.lib.multilang import tr, tr_noop
|
||||
from openpilot.system.ui.widgets.list_view import button_item
|
||||
from openpilot.system.ui.widgets.scroller_tici import Scroller
|
||||
from openpilot.selfdrive.ui.layouts.settings.starpilot.panel import StarPilotPanel
|
||||
|
||||
class StarPilotVisualsLayout(StarPilotPanel):
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
|
||||
items = [
|
||||
button_item(
|
||||
tr_noop("Advanced UI Controls"),
|
||||
lambda: tr("MANAGE"),
|
||||
tr_noop("<b>Advanced visual changes</b> to fine-tune how the driving screen looks."),
|
||||
),
|
||||
button_item(
|
||||
tr_noop("Driving Screen Widgets"),
|
||||
lambda: tr("MANAGE"),
|
||||
tr_noop("<b>Custom StarPilot widgets</b> for the driving screen."),
|
||||
),
|
||||
button_item(
|
||||
tr_noop("Model UI"),
|
||||
lambda: tr("MANAGE"),
|
||||
tr_noop("<b>Model visualizations</b> for the driving path, lane lines, path edges, and road edges."),
|
||||
),
|
||||
button_item(
|
||||
tr_noop("Navigation Widgets"),
|
||||
lambda: tr("MANAGE"),
|
||||
tr_noop("<b>Speed limits, and other navigation widgets.</b>"),
|
||||
),
|
||||
button_item(
|
||||
tr_noop("Quality of Life"),
|
||||
lambda: tr("MANAGE"),
|
||||
tr_noop("<b>Miscellaneous visual changes</b> to fine-tune how the driving screen looks."),
|
||||
),
|
||||
]
|
||||
|
||||
self._scroller = Scroller(items, line_separator=True, spacing=0)
|
||||
@@ -0,0 +1,20 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from openpilot.system.ui.lib.multilang import tr, tr_noop
|
||||
from openpilot.system.ui.widgets.list_view import button_item
|
||||
from openpilot.system.ui.widgets.scroller_tici import Scroller
|
||||
from openpilot.selfdrive.ui.layouts.settings.starpilot.panel import StarPilotPanel
|
||||
|
||||
class StarPilotWheelLayout(StarPilotPanel):
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
|
||||
items = [
|
||||
button_item(
|
||||
tr_noop("Wheel Controls"),
|
||||
lambda: tr("MANAGE"),
|
||||
tr_noop("<b>Configure steering wheel button mappings</b> for custom controls."),
|
||||
),
|
||||
]
|
||||
|
||||
self._scroller = Scroller(items, line_separator=True, spacing=0)
|
||||