commit
7455dde790
136 changed files with 1779 additions and 3565 deletions
@ -1 +1 @@ |
|||||||
#define COMMA_VERSION "0.10.0" |
#define COMMA_VERSION "0.10.1" |
||||||
|
@ -1 +1 @@ |
|||||||
Subproject commit 22b8df68fb6d8aa197fb9b432a428da6dd96c98c |
Subproject commit 43006b9a41e233325cb7cbcb6ff40de0234217a0 |
@ -1 +1 @@ |
|||||||
Subproject commit c2723b2f6bcce7e2d97f685db1ec2472a8dd28f5 |
Subproject commit 3dc21386239e3073a623156b75901aa302340d6c |
@ -0,0 +1,3 @@ |
|||||||
|
version https://git-lfs.github.com/spec/v1 |
||||||
|
oid sha256:a69514fe68dd1b4c73f28771640dcba10fc40989d1c7e771cb48bfd830fef206 |
||||||
|
size 5713 |
@ -1,3 +1,3 @@ |
|||||||
version https://git-lfs.github.com/spec/v1 |
version https://git-lfs.github.com/spec/v1 |
||||||
oid sha256:22aec22a10ce09384d4a4af2a0bbff08d54af7e0c888503508f356fae4ff0e29 |
oid sha256:04b763fb71efe57a8a4c4168a8043ecd58939015026ded0dc755ded6905ac251 |
||||||
size 15583374 |
size 12343523 |
||||||
|
@ -1,3 +1,3 @@ |
|||||||
version https://git-lfs.github.com/spec/v1 |
version https://git-lfs.github.com/spec/v1 |
||||||
oid sha256:c824f68646a3b94f117f01c70dc8316fb466e05fbd42ccdba440b8a8dc86914b |
oid sha256:e66bb8d53eced3786ed71a59b55ffc6810944cb217f0518621cc76303260a1ef |
||||||
size 46265993 |
size 46271991 |
||||||
|
@ -0,0 +1,54 @@ |
|||||||
|
import math |
||||||
|
from enum import StrEnum, auto |
||||||
|
|
||||||
|
from cereal import car, messaging |
||||||
|
from openpilot.common.realtime import DT_CTRL |
||||||
|
from openpilot.selfdrive.locationd.helpers import Pose |
||||||
|
from opendbc.car import ACCELERATION_DUE_TO_GRAVITY |
||||||
|
from opendbc.car.lateral import ISO_LATERAL_ACCEL |
||||||
|
from opendbc.car.interfaces import ACCEL_MIN, ACCEL_MAX |
||||||
|
|
||||||
|
MIN_EXCESSIVE_ACTUATION_COUNT = int(0.25 / DT_CTRL) |
||||||
|
MIN_LATERAL_ENGAGE_BUFFER = int(1 / DT_CTRL) |
||||||
|
|
||||||
|
|
||||||
|
class ExcessiveActuationType(StrEnum): |
||||||
|
LONGITUDINAL = auto() |
||||||
|
LATERAL = auto() |
||||||
|
|
||||||
|
|
||||||
|
class ExcessiveActuationCheck: |
||||||
|
def __init__(self): |
||||||
|
self._excessive_counter = 0 |
||||||
|
self._engaged_counter = 0 |
||||||
|
|
||||||
|
def update(self, sm: messaging.SubMaster, CS: car.CarState, calibrated_pose: Pose) -> ExcessiveActuationType | None: |
||||||
|
# CS.aEgo can be noisy to bumps in the road, transitioning from standstill, losing traction, etc. |
||||||
|
# longitudinal |
||||||
|
accel_calibrated = calibrated_pose.acceleration.x |
||||||
|
excessive_long_actuation = sm['carControl'].longActive and (accel_calibrated > ACCEL_MAX * 2 or accel_calibrated < ACCEL_MIN * 2) |
||||||
|
|
||||||
|
# lateral |
||||||
|
yaw_rate = calibrated_pose.angular_velocity.yaw |
||||||
|
roll = sm['liveParameters'].roll |
||||||
|
roll_compensated_lateral_accel = (CS.vEgo * yaw_rate) - (math.sin(roll) * ACCELERATION_DUE_TO_GRAVITY) |
||||||
|
|
||||||
|
# Prevent false positives after overriding |
||||||
|
excessive_lat_actuation = False |
||||||
|
self._engaged_counter = self._engaged_counter + 1 if sm['carControl'].latActive and not CS.steeringPressed else 0 |
||||||
|
if self._engaged_counter > MIN_LATERAL_ENGAGE_BUFFER: |
||||||
|
if abs(roll_compensated_lateral_accel) > ISO_LATERAL_ACCEL * 2: |
||||||
|
excessive_lat_actuation = True |
||||||
|
|
||||||
|
# livePose acceleration can be noisy due to bad mounting or aliased livePose measurements |
||||||
|
livepose_valid = abs(CS.aEgo - accel_calibrated) < 2 |
||||||
|
self._excessive_counter = self._excessive_counter + 1 if livepose_valid and (excessive_long_actuation or excessive_lat_actuation) else 0 |
||||||
|
|
||||||
|
excessive_type = None |
||||||
|
if self._excessive_counter > MIN_EXCESSIVE_ACTUATION_COUNT: |
||||||
|
if excessive_long_actuation: |
||||||
|
excessive_type = ExcessiveActuationType.LONGITUDINAL |
||||||
|
else: |
||||||
|
excessive_type = ExcessiveActuationType.LATERAL |
||||||
|
|
||||||
|
return excessive_type |
@ -1 +1 @@ |
|||||||
8ff1b4c9c7a34589142a07579b0051acddfe7699 |
6d3219bca9f66a229b38a5382d301a92b0147edb |
@ -0,0 +1,71 @@ |
|||||||
|
#!/usr/bin/env python3 |
||||||
|
import cereal.messaging as messaging |
||||||
|
from openpilot.common.params import Params |
||||||
|
from openpilot.common.swaglog import cloudlog |
||||||
|
from cereal import car |
||||||
|
from openpilot.system.micd import SAMPLE_RATE, SAMPLE_BUFFER |
||||||
|
|
||||||
|
FEEDBACK_MAX_DURATION = 10.0 |
||||||
|
ButtonType = car.CarState.ButtonEvent.Type |
||||||
|
|
||||||
|
|
||||||
|
def main(): |
||||||
|
params = Params() |
||||||
|
pm = messaging.PubMaster(['userBookmark', 'audioFeedback']) |
||||||
|
sm = messaging.SubMaster(['rawAudioData', 'bookmarkButton', 'carState']) |
||||||
|
should_record_audio = False |
||||||
|
block_num = 0 |
||||||
|
waiting_for_release = False |
||||||
|
early_stop_triggered = False |
||||||
|
|
||||||
|
while True: |
||||||
|
sm.update() |
||||||
|
should_send_bookmark = False |
||||||
|
|
||||||
|
# TODO: https://github.com/commaai/openpilot/issues/36015 |
||||||
|
if False and sm.updated['carState'] and sm['carState'].canValid: |
||||||
|
for be in sm['carState'].buttonEvents: |
||||||
|
if be.type == ButtonType.lkas: |
||||||
|
if be.pressed: |
||||||
|
if not should_record_audio: |
||||||
|
if params.get_bool("RecordAudioFeedback"): # Start recording on first press if toggle set |
||||||
|
should_record_audio = True |
||||||
|
block_num = 0 |
||||||
|
waiting_for_release = False |
||||||
|
early_stop_triggered = False |
||||||
|
cloudlog.info("LKAS button pressed - starting 10-second audio feedback") |
||||||
|
else: |
||||||
|
should_send_bookmark = True # immediately send bookmark if toggle false |
||||||
|
cloudlog.info("LKAS button pressed - bookmarking") |
||||||
|
elif should_record_audio and not waiting_for_release: # Wait for release of second press to stop recording early |
||||||
|
waiting_for_release = True |
||||||
|
elif waiting_for_release: # Second press released |
||||||
|
waiting_for_release = False |
||||||
|
early_stop_triggered = True |
||||||
|
cloudlog.info("LKAS button released - ending recording early") |
||||||
|
|
||||||
|
if should_record_audio and sm.updated['rawAudioData']: |
||||||
|
raw_audio = sm['rawAudioData'] |
||||||
|
msg = messaging.new_message('audioFeedback', valid=True) |
||||||
|
msg.audioFeedback.audio.data = raw_audio.data |
||||||
|
msg.audioFeedback.audio.sampleRate = raw_audio.sampleRate |
||||||
|
msg.audioFeedback.blockNum = block_num |
||||||
|
block_num += 1 |
||||||
|
if (block_num * SAMPLE_BUFFER / SAMPLE_RATE) >= FEEDBACK_MAX_DURATION or early_stop_triggered: # Check for timeout or early stop |
||||||
|
should_send_bookmark = True # send bookmark at end of audio segment |
||||||
|
should_record_audio = False |
||||||
|
early_stop_triggered = False |
||||||
|
cloudlog.info("10-second recording completed or second button press - stopping audio feedback") |
||||||
|
pm.send('audioFeedback', msg) |
||||||
|
|
||||||
|
if sm.updated['bookmarkButton']: |
||||||
|
cloudlog.info("Bookmark button pressed!") |
||||||
|
should_send_bookmark = True |
||||||
|
|
||||||
|
if should_send_bookmark: |
||||||
|
msg = messaging.new_message('userBookmark', valid=True) |
||||||
|
pm.send('userBookmark', msg) |
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__': |
||||||
|
main() |
@ -1,23 +0,0 @@ |
|||||||
import os |
|
||||||
import platform |
|
||||||
from cffi import FFI |
|
||||||
|
|
||||||
import sip |
|
||||||
|
|
||||||
from openpilot.common.basedir import BASEDIR |
|
||||||
|
|
||||||
def suffix(): |
|
||||||
return ".dylib" if platform.system() == "Darwin" else ".so" |
|
||||||
|
|
||||||
|
|
||||||
def get_ffi(): |
|
||||||
lib = os.path.join(BASEDIR, "selfdrive", "ui", "qt", "libpython_helpers" + suffix()) |
|
||||||
|
|
||||||
ffi = FFI() |
|
||||||
ffi.cdef("void set_main_window(void *w);") |
|
||||||
return ffi, ffi.dlopen(lib) |
|
||||||
|
|
||||||
|
|
||||||
def set_main_window(widget): |
|
||||||
ffi, lib = get_ffi() |
|
||||||
lib.set_main_window(ffi.cast('void*', sip.unwrapinstance(widget))) |
|
@ -1,541 +0,0 @@ |
|||||||
#include "selfdrive/ui/qt/setup/setup.h" |
|
||||||
|
|
||||||
#include <cstdio> |
|
||||||
#include <cstdlib> |
|
||||||
#include <sstream> |
|
||||||
#include <string> |
|
||||||
|
|
||||||
#include <QApplication> |
|
||||||
#include <QLabel> |
|
||||||
#include <QVBoxLayout> |
|
||||||
|
|
||||||
#include <curl/curl.h> |
|
||||||
|
|
||||||
#include "common/util.h" |
|
||||||
#include "system/hardware/hw.h" |
|
||||||
#include "selfdrive/ui/qt/api.h" |
|
||||||
#include "selfdrive/ui/qt/qt_window.h" |
|
||||||
#include "selfdrive/ui/qt/network/networking.h" |
|
||||||
#include "selfdrive/ui/qt/util.h" |
|
||||||
#include "selfdrive/ui/qt/widgets/input.h" |
|
||||||
|
|
||||||
const std::string USER_AGENT = "AGNOSSetup-"; |
|
||||||
const QString OPENPILOT_URL = "https://openpilot.comma.ai"; |
|
||||||
|
|
||||||
bool is_elf(char *fname) { |
|
||||||
FILE *fp = fopen(fname, "rb"); |
|
||||||
if (fp == NULL) { |
|
||||||
return false; |
|
||||||
} |
|
||||||
char buf[4]; |
|
||||||
size_t n = fread(buf, 1, 4, fp); |
|
||||||
fclose(fp); |
|
||||||
return n == 4 && buf[0] == 0x7f && buf[1] == 'E' && buf[2] == 'L' && buf[3] == 'F'; |
|
||||||
} |
|
||||||
|
|
||||||
void Setup::download(QString url) { |
|
||||||
// autocomplete incomplete urls
|
|
||||||
if (QRegularExpression("^([^/.]+)/([^/]+)$").match(url).hasMatch()) { |
|
||||||
url.prepend("https://installer.comma.ai/"); |
|
||||||
} |
|
||||||
|
|
||||||
CURL *curl = curl_easy_init(); |
|
||||||
if (!curl) { |
|
||||||
emit finished(url, tr("Something went wrong. Reboot the device.")); |
|
||||||
return; |
|
||||||
} |
|
||||||
|
|
||||||
auto version = util::read_file("/VERSION"); |
|
||||||
|
|
||||||
struct curl_slist *list = NULL; |
|
||||||
std::string header = "X-openpilot-serial: " + Hardware::get_serial(); |
|
||||||
list = curl_slist_append(list, header.c_str()); |
|
||||||
|
|
||||||
char tmpfile[] = "/tmp/installer_XXXXXX"; |
|
||||||
FILE *fp = fdopen(mkstemp(tmpfile), "wb"); |
|
||||||
|
|
||||||
curl_easy_setopt(curl, CURLOPT_URL, url.toStdString().c_str()); |
|
||||||
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, NULL); |
|
||||||
curl_easy_setopt(curl, CURLOPT_WRITEDATA, fp); |
|
||||||
curl_easy_setopt(curl, CURLOPT_VERBOSE, 1L); |
|
||||||
curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L); |
|
||||||
curl_easy_setopt(curl, CURLOPT_USERAGENT, (USER_AGENT + version).c_str()); |
|
||||||
curl_easy_setopt(curl, CURLOPT_HTTPHEADER, list); |
|
||||||
curl_easy_setopt(curl, CURLOPT_TIMEOUT, 30L); |
|
||||||
|
|
||||||
int ret = curl_easy_perform(curl); |
|
||||||
long res_status = 0; |
|
||||||
curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &res_status); |
|
||||||
|
|
||||||
if (ret != CURLE_OK || res_status != 200) { |
|
||||||
emit finished(url, tr("Ensure the entered URL is valid, and the device’s internet connection is good.")); |
|
||||||
} else if (!is_elf(tmpfile)) { |
|
||||||
emit finished(url, tr("No custom software found at this URL.")); |
|
||||||
} else { |
|
||||||
rename(tmpfile, "/tmp/installer"); |
|
||||||
|
|
||||||
FILE *fp_url = fopen("/tmp/installer_url", "w"); |
|
||||||
fprintf(fp_url, "%s", url.toStdString().c_str()); |
|
||||||
fclose(fp_url); |
|
||||||
|
|
||||||
emit finished(url); |
|
||||||
} |
|
||||||
|
|
||||||
curl_slist_free_all(list); |
|
||||||
curl_easy_cleanup(curl); |
|
||||||
fclose(fp); |
|
||||||
} |
|
||||||
|
|
||||||
QWidget * Setup::low_voltage() { |
|
||||||
QWidget *widget = new QWidget(); |
|
||||||
QVBoxLayout *main_layout = new QVBoxLayout(widget); |
|
||||||
main_layout->setContentsMargins(55, 0, 55, 55); |
|
||||||
main_layout->setSpacing(0); |
|
||||||
|
|
||||||
// inner text layout: warning icon, title, and body
|
|
||||||
QVBoxLayout *inner_layout = new QVBoxLayout(); |
|
||||||
inner_layout->setContentsMargins(110, 144, 365, 0); |
|
||||||
main_layout->addLayout(inner_layout); |
|
||||||
|
|
||||||
QLabel *triangle = new QLabel(); |
|
||||||
triangle->setPixmap(QPixmap(ASSET_PATH + "icons/warning.png")); |
|
||||||
inner_layout->addWidget(triangle, 0, Qt::AlignTop | Qt::AlignLeft); |
|
||||||
inner_layout->addSpacing(80); |
|
||||||
|
|
||||||
QLabel *title = new QLabel(tr("WARNING: Low Voltage")); |
|
||||||
title->setStyleSheet("font-size: 90px; font-weight: 500; color: #FF594F;"); |
|
||||||
inner_layout->addWidget(title, 0, Qt::AlignTop | Qt::AlignLeft); |
|
||||||
|
|
||||||
inner_layout->addSpacing(25); |
|
||||||
|
|
||||||
QLabel *body = new QLabel(tr("Power your device in a car with a harness or proceed at your own risk.")); |
|
||||||
body->setWordWrap(true); |
|
||||||
body->setAlignment(Qt::AlignTop | Qt::AlignLeft); |
|
||||||
body->setStyleSheet("font-size: 80px; font-weight: 300;"); |
|
||||||
inner_layout->addWidget(body); |
|
||||||
|
|
||||||
inner_layout->addStretch(); |
|
||||||
|
|
||||||
// power off + continue buttons
|
|
||||||
QHBoxLayout *blayout = new QHBoxLayout(); |
|
||||||
blayout->setSpacing(50); |
|
||||||
main_layout->addLayout(blayout, 0); |
|
||||||
|
|
||||||
QPushButton *poweroff = new QPushButton(tr("Power off")); |
|
||||||
poweroff->setObjectName("navBtn"); |
|
||||||
blayout->addWidget(poweroff); |
|
||||||
QObject::connect(poweroff, &QPushButton::clicked, this, [=]() { |
|
||||||
Hardware::poweroff(); |
|
||||||
}); |
|
||||||
|
|
||||||
QPushButton *cont = new QPushButton(tr("Continue")); |
|
||||||
cont->setObjectName("navBtn"); |
|
||||||
blayout->addWidget(cont); |
|
||||||
QObject::connect(cont, &QPushButton::clicked, this, &Setup::nextPage); |
|
||||||
|
|
||||||
return widget; |
|
||||||
} |
|
||||||
|
|
||||||
QWidget * Setup::custom_software_warning() { |
|
||||||
QWidget *widget = new QWidget(); |
|
||||||
QVBoxLayout *main_layout = new QVBoxLayout(widget); |
|
||||||
main_layout->setContentsMargins(55, 0, 55, 55); |
|
||||||
main_layout->setSpacing(0); |
|
||||||
|
|
||||||
QVBoxLayout *inner_layout = new QVBoxLayout(); |
|
||||||
inner_layout->setContentsMargins(110, 110, 300, 0); |
|
||||||
main_layout->addLayout(inner_layout); |
|
||||||
|
|
||||||
QLabel *title = new QLabel(tr("WARNING: Custom Software")); |
|
||||||
title->setStyleSheet("font-size: 90px; font-weight: 500; color: #FF594F;"); |
|
||||||
inner_layout->addWidget(title, 0, Qt::AlignTop | Qt::AlignLeft); |
|
||||||
|
|
||||||
inner_layout->addSpacing(25); |
|
||||||
|
|
||||||
QLabel *body = new QLabel(tr("Use caution when installing third-party software. Third-party software has not been tested by comma, and may cause damage to your device and/or vehicle.\n\nIf you'd like to proceed, use https://flash.comma.ai to restore your device to a factory state later.")); |
|
||||||
body->setWordWrap(true); |
|
||||||
body->setAlignment(Qt::AlignTop | Qt::AlignLeft); |
|
||||||
body->setStyleSheet("font-size: 65px; font-weight: 300;"); |
|
||||||
inner_layout->addWidget(body); |
|
||||||
|
|
||||||
inner_layout->addStretch(); |
|
||||||
|
|
||||||
QHBoxLayout *blayout = new QHBoxLayout(); |
|
||||||
blayout->setSpacing(50); |
|
||||||
main_layout->addLayout(blayout, 0); |
|
||||||
|
|
||||||
QPushButton *back = new QPushButton(tr("Back")); |
|
||||||
back->setObjectName("navBtn"); |
|
||||||
blayout->addWidget(back); |
|
||||||
QObject::connect(back, &QPushButton::clicked, this, &Setup::prevPage); |
|
||||||
|
|
||||||
QPushButton *cont = new QPushButton(tr("Continue")); |
|
||||||
cont->setObjectName("navBtn"); |
|
||||||
blayout->addWidget(cont); |
|
||||||
QObject::connect(cont, &QPushButton::clicked, this, [=]() { |
|
||||||
QTimer::singleShot(0, [=]() { |
|
||||||
setCurrentWidget(downloading_widget); |
|
||||||
}); |
|
||||||
QString url = InputDialog::getText(tr("Enter URL"), this, tr("for Custom Software")); |
|
||||||
if (!url.isEmpty()) { |
|
||||||
QTimer::singleShot(1000, this, [=]() { |
|
||||||
download(url); |
|
||||||
}); |
|
||||||
} else { |
|
||||||
setCurrentWidget(software_selection_widget); |
|
||||||
} |
|
||||||
}); |
|
||||||
|
|
||||||
return widget; |
|
||||||
} |
|
||||||
|
|
||||||
QWidget * Setup::getting_started() { |
|
||||||
QWidget *widget = new QWidget(); |
|
||||||
|
|
||||||
QHBoxLayout *main_layout = new QHBoxLayout(widget); |
|
||||||
main_layout->setMargin(0); |
|
||||||
|
|
||||||
QVBoxLayout *vlayout = new QVBoxLayout(); |
|
||||||
vlayout->setContentsMargins(165, 280, 100, 0); |
|
||||||
main_layout->addLayout(vlayout); |
|
||||||
|
|
||||||
QLabel *title = new QLabel(tr("Getting Started")); |
|
||||||
title->setStyleSheet("font-size: 90px; font-weight: 500;"); |
|
||||||
vlayout->addWidget(title, 0, Qt::AlignTop | Qt::AlignLeft); |
|
||||||
|
|
||||||
vlayout->addSpacing(90); |
|
||||||
QLabel *desc = new QLabel(tr("Before we get on the road, let’s finish installation and cover some details.")); |
|
||||||
desc->setWordWrap(true); |
|
||||||
desc->setStyleSheet("font-size: 80px; font-weight: 300;"); |
|
||||||
vlayout->addWidget(desc, 0, Qt::AlignTop | Qt::AlignLeft); |
|
||||||
|
|
||||||
vlayout->addStretch(); |
|
||||||
|
|
||||||
QPushButton *btn = new QPushButton(); |
|
||||||
btn->setIcon(QIcon(":/images/button_continue_triangle.svg")); |
|
||||||
btn->setIconSize(QSize(54, 106)); |
|
||||||
btn->setFixedSize(310, 1080); |
|
||||||
btn->setProperty("primary", true); |
|
||||||
btn->setStyleSheet("border: none;"); |
|
||||||
main_layout->addWidget(btn, 0, Qt::AlignRight); |
|
||||||
QObject::connect(btn, &QPushButton::clicked, this, &Setup::nextPage); |
|
||||||
|
|
||||||
return widget; |
|
||||||
} |
|
||||||
|
|
||||||
QWidget * Setup::network_setup() { |
|
||||||
QWidget *widget = new QWidget(); |
|
||||||
QVBoxLayout *main_layout = new QVBoxLayout(widget); |
|
||||||
main_layout->setContentsMargins(55, 50, 55, 50); |
|
||||||
|
|
||||||
// title
|
|
||||||
QLabel *title = new QLabel(tr("Connect to Wi-Fi")); |
|
||||||
title->setStyleSheet("font-size: 90px; font-weight: 500;"); |
|
||||||
main_layout->addWidget(title, 0, Qt::AlignLeft | Qt::AlignTop); |
|
||||||
|
|
||||||
main_layout->addSpacing(25); |
|
||||||
|
|
||||||
// wifi widget
|
|
||||||
Networking *networking = new Networking(this, false); |
|
||||||
networking->setStyleSheet("Networking {background-color: #292929; border-radius: 13px;}"); |
|
||||||
main_layout->addWidget(networking, 1); |
|
||||||
|
|
||||||
main_layout->addSpacing(35); |
|
||||||
|
|
||||||
// back + continue buttons
|
|
||||||
QHBoxLayout *blayout = new QHBoxLayout; |
|
||||||
main_layout->addLayout(blayout); |
|
||||||
blayout->setSpacing(50); |
|
||||||
|
|
||||||
QPushButton *back = new QPushButton(tr("Back")); |
|
||||||
back->setObjectName("navBtn"); |
|
||||||
QObject::connect(back, &QPushButton::clicked, this, &Setup::prevPage); |
|
||||||
blayout->addWidget(back); |
|
||||||
|
|
||||||
QPushButton *cont = new QPushButton(); |
|
||||||
cont->setObjectName("navBtn"); |
|
||||||
cont->setProperty("primary", true); |
|
||||||
cont->setEnabled(false); |
|
||||||
QObject::connect(cont, &QPushButton::clicked, this, &Setup::nextPage); |
|
||||||
blayout->addWidget(cont); |
|
||||||
|
|
||||||
// setup timer for testing internet connection
|
|
||||||
HttpRequest *request = new HttpRequest(this, false, 2500); |
|
||||||
QObject::connect(request, &HttpRequest::requestDone, [=](const QString &, bool success) { |
|
||||||
cont->setEnabled(success); |
|
||||||
if (success) { |
|
||||||
const bool wifi = networking->wifi->currentNetworkType() == NetworkType::WIFI; |
|
||||||
cont->setText(wifi ? tr("Continue") : tr("Continue without Wi-Fi")); |
|
||||||
} else { |
|
||||||
cont->setText(tr("Waiting for internet")); |
|
||||||
} |
|
||||||
repaint(); |
|
||||||
}); |
|
||||||
request->sendRequest(OPENPILOT_URL); |
|
||||||
QTimer *timer = new QTimer(this); |
|
||||||
QObject::connect(timer, &QTimer::timeout, [=]() { |
|
||||||
if (!request->active() && cont->isVisible()) { |
|
||||||
request->sendRequest(OPENPILOT_URL); |
|
||||||
} |
|
||||||
}); |
|
||||||
timer->start(1000); |
|
||||||
|
|
||||||
return widget; |
|
||||||
} |
|
||||||
|
|
||||||
QWidget * radio_button(QString title, QButtonGroup *group) { |
|
||||||
QPushButton *btn = new QPushButton(title); |
|
||||||
btn->setCheckable(true); |
|
||||||
group->addButton(btn); |
|
||||||
btn->setStyleSheet(R"( |
|
||||||
QPushButton { |
|
||||||
height: 230; |
|
||||||
padding-left: 100px; |
|
||||||
padding-right: 100px; |
|
||||||
text-align: left; |
|
||||||
font-size: 80px; |
|
||||||
font-weight: 400; |
|
||||||
border-radius: 10px; |
|
||||||
background-color: #4F4F4F; |
|
||||||
} |
|
||||||
QPushButton:checked { |
|
||||||
background-color: #465BEA; |
|
||||||
} |
|
||||||
)"); |
|
||||||
|
|
||||||
// checkmark icon
|
|
||||||
QPixmap pix(":/icons/circled_check.svg"); |
|
||||||
btn->setIcon(pix); |
|
||||||
btn->setIconSize(QSize(0, 0)); |
|
||||||
btn->setLayoutDirection(Qt::RightToLeft); |
|
||||||
QObject::connect(btn, &QPushButton::toggled, [=](bool checked) { |
|
||||||
btn->setIconSize(checked ? QSize(104, 104) : QSize(0, 0)); |
|
||||||
}); |
|
||||||
return btn; |
|
||||||
} |
|
||||||
|
|
||||||
QWidget * Setup::software_selection() { |
|
||||||
QWidget *widget = new QWidget(); |
|
||||||
QVBoxLayout *main_layout = new QVBoxLayout(widget); |
|
||||||
main_layout->setContentsMargins(55, 50, 55, 50); |
|
||||||
main_layout->setSpacing(0); |
|
||||||
|
|
||||||
// title
|
|
||||||
QLabel *title = new QLabel(tr("Choose Software to Install")); |
|
||||||
title->setStyleSheet("font-size: 90px; font-weight: 500;"); |
|
||||||
main_layout->addWidget(title, 0, Qt::AlignLeft | Qt::AlignTop); |
|
||||||
|
|
||||||
main_layout->addSpacing(50); |
|
||||||
|
|
||||||
// openpilot + custom radio buttons
|
|
||||||
QButtonGroup *group = new QButtonGroup(widget); |
|
||||||
group->setExclusive(true); |
|
||||||
|
|
||||||
QWidget *openpilot = radio_button(tr("openpilot"), group); |
|
||||||
main_layout->addWidget(openpilot); |
|
||||||
|
|
||||||
main_layout->addSpacing(30); |
|
||||||
|
|
||||||
QWidget *custom = radio_button(tr("Custom Software"), group); |
|
||||||
main_layout->addWidget(custom); |
|
||||||
|
|
||||||
main_layout->addStretch(); |
|
||||||
|
|
||||||
// back + continue buttons
|
|
||||||
QHBoxLayout *blayout = new QHBoxLayout; |
|
||||||
main_layout->addLayout(blayout); |
|
||||||
blayout->setSpacing(50); |
|
||||||
|
|
||||||
QPushButton *back = new QPushButton(tr("Back")); |
|
||||||
back->setObjectName("navBtn"); |
|
||||||
QObject::connect(back, &QPushButton::clicked, this, &Setup::prevPage); |
|
||||||
blayout->addWidget(back); |
|
||||||
|
|
||||||
QPushButton *cont = new QPushButton(tr("Continue")); |
|
||||||
cont->setObjectName("navBtn"); |
|
||||||
cont->setEnabled(false); |
|
||||||
cont->setProperty("primary", true); |
|
||||||
blayout->addWidget(cont); |
|
||||||
|
|
||||||
QObject::connect(cont, &QPushButton::clicked, [=]() { |
|
||||||
if (group->checkedButton() != openpilot) { |
|
||||||
QTimer::singleShot(0, [=]() { |
|
||||||
setCurrentWidget(custom_software_warning_widget); |
|
||||||
}); |
|
||||||
} else { |
|
||||||
QTimer::singleShot(0, [=]() { |
|
||||||
setCurrentWidget(downloading_widget); |
|
||||||
}); |
|
||||||
QTimer::singleShot(1000, this, [=]() { |
|
||||||
download(OPENPILOT_URL); |
|
||||||
}); |
|
||||||
} |
|
||||||
}); |
|
||||||
|
|
||||||
connect(group, QOverload<QAbstractButton *>::of(&QButtonGroup::buttonClicked), [=](QAbstractButton *btn) { |
|
||||||
btn->setChecked(true); |
|
||||||
cont->setEnabled(true); |
|
||||||
}); |
|
||||||
|
|
||||||
return widget; |
|
||||||
} |
|
||||||
|
|
||||||
QWidget * Setup::downloading() { |
|
||||||
QWidget *widget = new QWidget(); |
|
||||||
QVBoxLayout *main_layout = new QVBoxLayout(widget); |
|
||||||
QLabel *txt = new QLabel(tr("Downloading...")); |
|
||||||
txt->setStyleSheet("font-size: 90px; font-weight: 500;"); |
|
||||||
main_layout->addWidget(txt, 0, Qt::AlignCenter); |
|
||||||
return widget; |
|
||||||
} |
|
||||||
|
|
||||||
QWidget * Setup::download_failed(QLabel *url, QLabel *body) { |
|
||||||
QWidget *widget = new QWidget(); |
|
||||||
QVBoxLayout *main_layout = new QVBoxLayout(widget); |
|
||||||
main_layout->setContentsMargins(55, 185, 55, 55); |
|
||||||
main_layout->setSpacing(0); |
|
||||||
|
|
||||||
QLabel *title = new QLabel(tr("Download Failed")); |
|
||||||
title->setStyleSheet("font-size: 90px; font-weight: 500;"); |
|
||||||
main_layout->addWidget(title, 0, Qt::AlignTop | Qt::AlignLeft); |
|
||||||
|
|
||||||
main_layout->addSpacing(67); |
|
||||||
|
|
||||||
url->setWordWrap(true); |
|
||||||
url->setAlignment(Qt::AlignTop | Qt::AlignLeft); |
|
||||||
url->setStyleSheet("font-family: \"JetBrains Mono\"; font-size: 64px; font-weight: 400; margin-right: 100px;"); |
|
||||||
main_layout->addWidget(url); |
|
||||||
|
|
||||||
main_layout->addSpacing(48); |
|
||||||
|
|
||||||
body->setWordWrap(true); |
|
||||||
body->setAlignment(Qt::AlignTop | Qt::AlignLeft); |
|
||||||
body->setStyleSheet("font-size: 80px; font-weight: 300; margin-right: 100px;"); |
|
||||||
main_layout->addWidget(body); |
|
||||||
|
|
||||||
main_layout->addStretch(); |
|
||||||
|
|
||||||
// reboot + start over buttons
|
|
||||||
QHBoxLayout *blayout = new QHBoxLayout(); |
|
||||||
blayout->setSpacing(50); |
|
||||||
main_layout->addLayout(blayout, 0); |
|
||||||
|
|
||||||
QPushButton *reboot = new QPushButton(tr("Reboot device")); |
|
||||||
reboot->setObjectName("navBtn"); |
|
||||||
blayout->addWidget(reboot); |
|
||||||
QObject::connect(reboot, &QPushButton::clicked, this, [=]() { |
|
||||||
Hardware::reboot(); |
|
||||||
}); |
|
||||||
|
|
||||||
QPushButton *restart = new QPushButton(tr("Start over")); |
|
||||||
restart->setObjectName("navBtn"); |
|
||||||
restart->setProperty("primary", true); |
|
||||||
blayout->addWidget(restart); |
|
||||||
QObject::connect(restart, &QPushButton::clicked, this, [=]() { |
|
||||||
setCurrentIndex(1); |
|
||||||
}); |
|
||||||
|
|
||||||
widget->setStyleSheet(R"( |
|
||||||
QLabel { |
|
||||||
margin-left: 117; |
|
||||||
} |
|
||||||
)"); |
|
||||||
return widget; |
|
||||||
} |
|
||||||
|
|
||||||
void Setup::prevPage() { |
|
||||||
setCurrentIndex(currentIndex() - 1); |
|
||||||
} |
|
||||||
|
|
||||||
void Setup::nextPage() { |
|
||||||
setCurrentIndex(currentIndex() + 1); |
|
||||||
} |
|
||||||
|
|
||||||
Setup::Setup(QWidget *parent) : QStackedWidget(parent) { |
|
||||||
if (std::getenv("MULTILANG")) { |
|
||||||
selectLanguage(); |
|
||||||
} |
|
||||||
|
|
||||||
std::stringstream buffer; |
|
||||||
buffer << std::ifstream("/sys/class/hwmon/hwmon1/in1_input").rdbuf(); |
|
||||||
float voltage = (float)std::atoi(buffer.str().c_str()) / 1000.; |
|
||||||
if (voltage < 7) { |
|
||||||
addWidget(low_voltage()); |
|
||||||
} |
|
||||||
|
|
||||||
addWidget(getting_started()); |
|
||||||
addWidget(network_setup()); |
|
||||||
software_selection_widget = software_selection(); |
|
||||||
addWidget(software_selection_widget); |
|
||||||
custom_software_warning_widget = custom_software_warning(); |
|
||||||
addWidget(custom_software_warning_widget); |
|
||||||
downloading_widget = downloading(); |
|
||||||
addWidget(downloading_widget); |
|
||||||
|
|
||||||
QLabel *url_label = new QLabel(); |
|
||||||
QLabel *body_label = new QLabel(); |
|
||||||
failed_widget = download_failed(url_label, body_label); |
|
||||||
addWidget(failed_widget); |
|
||||||
|
|
||||||
QObject::connect(this, &Setup::finished, [=](const QString &url, const QString &error) { |
|
||||||
qDebug() << "finished" << url << error; |
|
||||||
if (error.isEmpty()) { |
|
||||||
// hide setup on success
|
|
||||||
QTimer::singleShot(3000, this, &QWidget::hide); |
|
||||||
} else { |
|
||||||
url_label->setText(url); |
|
||||||
body_label->setText(error); |
|
||||||
setCurrentWidget(failed_widget); |
|
||||||
} |
|
||||||
}); |
|
||||||
|
|
||||||
// TODO: revisit pressed bg color
|
|
||||||
setStyleSheet(R"( |
|
||||||
* { |
|
||||||
color: white; |
|
||||||
font-family: Inter; |
|
||||||
} |
|
||||||
Setup { |
|
||||||
background-color: black; |
|
||||||
} |
|
||||||
QPushButton#navBtn { |
|
||||||
height: 160; |
|
||||||
font-size: 55px; |
|
||||||
font-weight: 400; |
|
||||||
border-radius: 10px; |
|
||||||
background-color: #333333; |
|
||||||
} |
|
||||||
QPushButton#navBtn:disabled, QPushButton[primary='true']:disabled { |
|
||||||
color: #808080; |
|
||||||
background-color: #333333; |
|
||||||
} |
|
||||||
QPushButton#navBtn:pressed { |
|
||||||
background-color: #444444; |
|
||||||
} |
|
||||||
QPushButton[primary='true'], #navBtn[primary='true'] { |
|
||||||
background-color: #465BEA; |
|
||||||
} |
|
||||||
QPushButton[primary='true']:pressed, #navBtn:pressed[primary='true'] { |
|
||||||
background-color: #3049F4; |
|
||||||
} |
|
||||||
)"); |
|
||||||
} |
|
||||||
|
|
||||||
void Setup::selectLanguage() { |
|
||||||
QMap<QString, QString> langs = getSupportedLanguages(); |
|
||||||
QString selection = MultiOptionDialog::getSelection(tr("Select a language"), langs.keys(), "", this); |
|
||||||
if (!selection.isEmpty()) { |
|
||||||
QString selectedLang = langs[selection]; |
|
||||||
Params().put("LanguageSetting", selectedLang.toStdString()); |
|
||||||
if (translator.load(":/" + selectedLang)) { |
|
||||||
qApp->installTranslator(&translator); |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
int main(int argc, char *argv[]) { |
|
||||||
QApplication a(argc, argv); |
|
||||||
Setup setup; |
|
||||||
setMainWindow(&setup); |
|
||||||
return a.exec(); |
|
||||||
} |
|
@ -1,38 +0,0 @@ |
|||||||
#pragma once |
|
||||||
|
|
||||||
#include <QLabel> |
|
||||||
#include <QStackedWidget> |
|
||||||
#include <QString> |
|
||||||
#include <QTranslator> |
|
||||||
#include <QWidget> |
|
||||||
|
|
||||||
class Setup : public QStackedWidget { |
|
||||||
Q_OBJECT |
|
||||||
|
|
||||||
public: |
|
||||||
explicit Setup(QWidget *parent = 0); |
|
||||||
|
|
||||||
private: |
|
||||||
void selectLanguage(); |
|
||||||
QWidget *low_voltage(); |
|
||||||
QWidget *custom_software_warning(); |
|
||||||
QWidget *getting_started(); |
|
||||||
QWidget *network_setup(); |
|
||||||
QWidget *software_selection(); |
|
||||||
QWidget *downloading(); |
|
||||||
QWidget *download_failed(QLabel *url, QLabel *body); |
|
||||||
|
|
||||||
QWidget *failed_widget; |
|
||||||
QWidget *downloading_widget; |
|
||||||
QWidget *custom_software_warning_widget; |
|
||||||
QWidget *software_selection_widget; |
|
||||||
QTranslator translator; |
|
||||||
|
|
||||||
signals: |
|
||||||
void finished(const QString &url, const QString &error = ""); |
|
||||||
|
|
||||||
public slots: |
|
||||||
void nextPage(); |
|
||||||
void prevPage(); |
|
||||||
void download(QString url); |
|
||||||
}; |
|
@ -0,0 +1,53 @@ |
|||||||
|
import pytest |
||||||
|
import cereal.messaging as messaging |
||||||
|
from cereal import car |
||||||
|
from openpilot.common.params import Params |
||||||
|
from openpilot.system.manager.process_config import managed_processes |
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.skip("tmp disabled") |
||||||
|
class TestFeedbackd: |
||||||
|
def setup_method(self): |
||||||
|
self.pm = messaging.PubMaster(['carState', 'rawAudioData']) |
||||||
|
self.sm = messaging.SubMaster(['audioFeedback']) |
||||||
|
|
||||||
|
def _send_lkas_button(self, pressed: bool): |
||||||
|
msg = messaging.new_message('carState') |
||||||
|
msg.carState.canValid = True |
||||||
|
msg.carState.buttonEvents = [{'type': car.CarState.ButtonEvent.Type.lkas, 'pressed': pressed}] |
||||||
|
self.pm.send('carState', msg) |
||||||
|
|
||||||
|
def _send_audio_data(self, count: int = 5): |
||||||
|
for _ in range(count): |
||||||
|
audio_msg = messaging.new_message('rawAudioData') |
||||||
|
audio_msg.rawAudioData.data = bytes(1600) # 800 samples of int16 |
||||||
|
audio_msg.rawAudioData.sampleRate = 16000 |
||||||
|
self.pm.send('rawAudioData', audio_msg) |
||||||
|
self.sm.update(timeout=100) |
||||||
|
|
||||||
|
@pytest.mark.parametrize("record_feedback", [False, True]) |
||||||
|
def test_audio_feedback(self, record_feedback): |
||||||
|
Params().put_bool("RecordAudioFeedback", record_feedback) |
||||||
|
|
||||||
|
managed_processes["feedbackd"].start() |
||||||
|
assert self.pm.wait_for_readers_to_update('carState', timeout=5) |
||||||
|
assert self.pm.wait_for_readers_to_update('rawAudioData', timeout=5) |
||||||
|
|
||||||
|
self._send_lkas_button(pressed=True) |
||||||
|
self._send_audio_data() |
||||||
|
self._send_lkas_button(pressed=False) |
||||||
|
self._send_audio_data() |
||||||
|
|
||||||
|
if record_feedback: |
||||||
|
assert self.sm.updated['audioFeedback'], "audioFeedback should be published when enabled" |
||||||
|
else: |
||||||
|
assert not self.sm.updated['audioFeedback'], "audioFeedback should not be published when disabled" |
||||||
|
|
||||||
|
self._send_lkas_button(pressed=True) |
||||||
|
self._send_audio_data() |
||||||
|
self._send_lkas_button(pressed=False) |
||||||
|
self._send_audio_data() |
||||||
|
|
||||||
|
assert not self.sm.updated['audioFeedback'], "audioFeedback should not be published after second press" |
||||||
|
|
||||||
|
managed_processes["feedbackd"].stop() |
@ -0,0 +1,8 @@ |
|||||||
|
import time |
||||||
|
from openpilot.selfdrive.test.helpers import with_processes |
||||||
|
|
||||||
|
|
||||||
|
@with_processes(["raylib_ui"]) |
||||||
|
def test_raylib_ui(): |
||||||
|
"""Test initialization of the UI widgets is successful.""" |
||||||
|
time.sleep(1) |
@ -1,33 +0,0 @@ |
|||||||
#include <QApplication> |
|
||||||
#include <QtWidgets> |
|
||||||
|
|
||||||
#include "selfdrive/ui/qt/qt_window.h" |
|
||||||
#include "selfdrive/ui/qt/util.h" |
|
||||||
#include "selfdrive/ui/qt/widgets/cameraview.h" |
|
||||||
|
|
||||||
int main(int argc, char *argv[]) { |
|
||||||
initApp(argc, argv); |
|
||||||
|
|
||||||
QApplication a(argc, argv); |
|
||||||
QWidget w; |
|
||||||
setMainWindow(&w); |
|
||||||
|
|
||||||
QVBoxLayout *layout = new QVBoxLayout(&w); |
|
||||||
layout->setMargin(0); |
|
||||||
layout->setSpacing(0); |
|
||||||
|
|
||||||
{ |
|
||||||
QHBoxLayout *hlayout = new QHBoxLayout(); |
|
||||||
layout->addLayout(hlayout); |
|
||||||
hlayout->addWidget(new CameraWidget("camerad", VISION_STREAM_ROAD)); |
|
||||||
} |
|
||||||
|
|
||||||
{ |
|
||||||
QHBoxLayout *hlayout = new QHBoxLayout(); |
|
||||||
layout->addLayout(hlayout); |
|
||||||
hlayout->addWidget(new CameraWidget("camerad", VISION_STREAM_DRIVER)); |
|
||||||
hlayout->addWidget(new CameraWidget("camerad", VISION_STREAM_WIDE_ROAD)); |
|
||||||
} |
|
||||||
|
|
||||||
return a.exec(); |
|
||||||
} |
|
@ -0,0 +1,17 @@ |
|||||||
|
#!/usr/bin/env python3 |
||||||
|
import pyray as rl |
||||||
|
|
||||||
|
from msgq.visionipc import VisionStreamType |
||||||
|
from openpilot.system.ui.lib.application import gui_app |
||||||
|
from openpilot.selfdrive.ui.onroad.cameraview import CameraView |
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__": |
||||||
|
gui_app.init_window("watch3") |
||||||
|
road = CameraView("camerad", VisionStreamType.VISION_STREAM_ROAD) |
||||||
|
driver = CameraView("camerad", VisionStreamType.VISION_STREAM_DRIVER) |
||||||
|
wide = CameraView("camerad", VisionStreamType.VISION_STREAM_WIDE_ROAD) |
||||||
|
for _ in gui_app.render(): |
||||||
|
road.render(rl.Rectangle(gui_app.width // 4, 0, gui_app.width // 2, gui_app.height // 2)) |
||||||
|
driver.render(rl.Rectangle(0, gui_app.height // 2, gui_app.width // 2, gui_app.height // 2)) |
||||||
|
wide.render(rl.Rectangle(gui_app.width // 2, gui_app.height // 2, gui_app.width // 2, gui_app.height // 2)) |
@ -1,34 +0,0 @@ |
|||||||
#if SENSOR_ID == 1 |
|
||||||
|
|
||||||
#define VIGNETTE_PROFILE_8DT0MM |
|
||||||
|
|
||||||
#define BIT_DEPTH 12 |
|
||||||
#define PV_MAX 4096 |
|
||||||
#define BLACK_LVL 168 |
|
||||||
|
|
||||||
float4 normalize_pv(int4 parsed, float vignette_factor) { |
|
||||||
float4 pv = (convert_float4(parsed) - BLACK_LVL) / (PV_MAX - BLACK_LVL); |
|
||||||
return clamp(pv*vignette_factor, 0.0, 1.0); |
|
||||||
} |
|
||||||
|
|
||||||
float3 color_correct(float3 rgb) { |
|
||||||
float3 corrected = rgb.x * (float3)(1.82717181, -0.31231438, 0.07307673); |
|
||||||
corrected += rgb.y * (float3)(-0.5743977, 1.36858544, -0.53183455); |
|
||||||
corrected += rgb.z * (float3)(-0.25277411, -0.05627105, 1.45875782); |
|
||||||
return corrected; |
|
||||||
} |
|
||||||
|
|
||||||
float3 apply_gamma(float3 rgb, int expo_time) { |
|
||||||
// tone mapping params
|
|
||||||
const float gamma_k = 0.75; |
|
||||||
const float gamma_b = 0.125; |
|
||||||
const float mp = 0.01; // ideally midpoint should be adaptive
|
|
||||||
const float rk = 9 - 100*mp; |
|
||||||
|
|
||||||
// poly approximation for s curve
|
|
||||||
return (rgb > mp) ? |
|
||||||
((rk * (rgb-mp) * (1-(gamma_k*mp+gamma_b)) * (1+1/(rk*(1-mp))) / (1+rk*(rgb-mp))) + gamma_k*mp + gamma_b) : |
|
||||||
((rk * (rgb-mp) * (gamma_k*mp+gamma_b) * (1+1/(rk*mp)) / (1-rk*(rgb-mp))) + gamma_k*mp + gamma_b); |
|
||||||
} |
|
||||||
|
|
||||||
#endif |
|
@ -1,58 +0,0 @@ |
|||||||
#if SENSOR_ID == 3 |
|
||||||
|
|
||||||
#define BGGR |
|
||||||
#define VIGNETTE_PROFILE_4DT6MM |
|
||||||
|
|
||||||
#define BIT_DEPTH 12 |
|
||||||
#define PV_MAX10 1023 |
|
||||||
#define PV_MAX12 4095 |
|
||||||
#define PV_MAX16 65536 // gamma curve is calibrated to 16bit
|
|
||||||
#define BLACK_LVL 48 |
|
||||||
|
|
||||||
float combine_dual_pvs(float lv, float sv, int expo_time) { |
|
||||||
float svc = fmax(sv * expo_time, (float)(64 * (PV_MAX10 - BLACK_LVL))); |
|
||||||
float svd = sv * fmin(expo_time, 8.0) / 8; |
|
||||||
|
|
||||||
if (expo_time > 64) { |
|
||||||
if (lv < PV_MAX10 - BLACK_LVL) { |
|
||||||
return lv / (PV_MAX16 - BLACK_LVL); |
|
||||||
} else { |
|
||||||
return (svc / 64) / (PV_MAX16 - BLACK_LVL); |
|
||||||
} |
|
||||||
} else { |
|
||||||
if (lv > 32) { |
|
||||||
return (lv * 64 / fmax(expo_time, 8.0)) / (PV_MAX16 - BLACK_LVL); |
|
||||||
} else { |
|
||||||
return svd / (PV_MAX16 - BLACK_LVL); |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
float4 normalize_pv_hdr(int4 parsed, int4 short_parsed, float vignette_factor, int expo_time) { |
|
||||||
float4 pl = convert_float4(parsed - BLACK_LVL); |
|
||||||
float4 ps = convert_float4(short_parsed - BLACK_LVL); |
|
||||||
float4 pv; |
|
||||||
pv.s0 = combine_dual_pvs(pl.s0, ps.s0, expo_time); |
|
||||||
pv.s1 = combine_dual_pvs(pl.s1, ps.s1, expo_time); |
|
||||||
pv.s2 = combine_dual_pvs(pl.s2, ps.s2, expo_time); |
|
||||||
pv.s3 = combine_dual_pvs(pl.s3, ps.s3, expo_time); |
|
||||||
return clamp(pv*vignette_factor, 0.0, 1.0); |
|
||||||
} |
|
||||||
|
|
||||||
float4 normalize_pv(int4 parsed, float vignette_factor) { |
|
||||||
float4 pv = (convert_float4(parsed) - BLACK_LVL) / (PV_MAX12 - BLACK_LVL); |
|
||||||
return clamp(pv*vignette_factor, 0.0, 1.0); |
|
||||||
} |
|
||||||
|
|
||||||
float3 color_correct(float3 rgb) { |
|
||||||
float3 corrected = rgb.x * (float3)(1.55361989, -0.268894615, -0.000593219); |
|
||||||
corrected += rgb.y * (float3)(-0.421217301, 1.51883144, -0.69760146); |
|
||||||
corrected += rgb.z * (float3)(-0.132402589, -0.249936825, 1.69819468); |
|
||||||
return corrected; |
|
||||||
} |
|
||||||
|
|
||||||
float3 apply_gamma(float3 rgb, int expo_time) { |
|
||||||
return (10 * rgb) / (1 + 9 * rgb); |
|
||||||
} |
|
||||||
|
|
||||||
#endif |
|
@ -1,47 +0,0 @@ |
|||||||
#if SENSOR_ID == 2 |
|
||||||
|
|
||||||
#define VIGNETTE_PROFILE_8DT0MM |
|
||||||
|
|
||||||
#define BIT_DEPTH 12 |
|
||||||
#define BLACK_LVL 64 |
|
||||||
|
|
||||||
float ox_lut_func(int x) { |
|
||||||
if (x < 512) { |
|
||||||
return x * 5.94873e-8; |
|
||||||
} else if (512 <= x && x < 768) { |
|
||||||
return 3.0458e-05 + (x-512) * 1.19913e-7; |
|
||||||
} else if (768 <= x && x < 1536) { |
|
||||||
return 6.1154e-05 + (x-768) * 2.38493e-7; |
|
||||||
} else if (1536 <= x && x < 1792) { |
|
||||||
return 0.0002448 + (x-1536) * 9.56930e-7; |
|
||||||
} else if (1792 <= x && x < 2048) { |
|
||||||
return 0.00048977 + (x-1792) * 1.91441e-6; |
|
||||||
} else if (2048 <= x && x < 2304) { |
|
||||||
return 0.00097984 + (x-2048) * 3.82937e-6; |
|
||||||
} else if (2304 <= x && x < 2560) { |
|
||||||
return 0.0019601 + (x-2304) * 7.659055e-6; |
|
||||||
} else if (2560 <= x && x < 2816) { |
|
||||||
return 0.0039207 + (x-2560) * 1.525e-5; |
|
||||||
} else { |
|
||||||
return 0.0078421 + (exp((x-2816)/273.0) - 1) * 0.0092421; |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
float4 normalize_pv(int4 parsed, float vignette_factor) { |
|
||||||
// PWL
|
|
||||||
float4 pv = {ox_lut_func(parsed.s0), ox_lut_func(parsed.s1), ox_lut_func(parsed.s2), ox_lut_func(parsed.s3)}; |
|
||||||
return clamp(pv*vignette_factor*256.0, 0.0, 1.0); |
|
||||||
} |
|
||||||
|
|
||||||
float3 color_correct(float3 rgb) { |
|
||||||
float3 corrected = rgb.x * (float3)(1.5664815, -0.29808738, -0.03973474); |
|
||||||
corrected += rgb.y * (float3)(-0.48672447, 1.41914433, -0.40295248); |
|
||||||
corrected += rgb.z * (float3)(-0.07975703, -0.12105695, 1.44268722); |
|
||||||
return corrected; |
|
||||||
} |
|
||||||
|
|
||||||
float3 apply_gamma(float3 rgb, int expo_time) { |
|
||||||
return -0.507089*exp(-12.54124638*rgb) + 0.9655*powr(rgb, 0.5) - 0.472597*rgb + 0.507089; |
|
||||||
} |
|
||||||
|
|
||||||
#endif |
|
@ -0,0 +1,47 @@ |
|||||||
|
import io |
||||||
|
import re |
||||||
|
|
||||||
|
from PIL import Image, ImageDraw, ImageFont |
||||||
|
import pyray as rl |
||||||
|
|
||||||
|
_cache: dict[str, rl.Texture] = {} |
||||||
|
|
||||||
|
EMOJI_REGEX = re.compile( |
||||||
|
"""[\U0001F600-\U0001F64F |
||||||
|
\U0001F300-\U0001F5FF |
||||||
|
\U0001F680-\U0001F6FF |
||||||
|
\U0001F1E0-\U0001F1FF |
||||||
|
\U00002700-\U000027BF |
||||||
|
\U0001F900-\U0001F9FF |
||||||
|
\U00002600-\U000026FF |
||||||
|
\U00002300-\U000023FF |
||||||
|
\U00002B00-\U00002BFF |
||||||
|
\U0001FA70-\U0001FAFF |
||||||
|
\U0001F700-\U0001F77F |
||||||
|
\u2640-\u2642 |
||||||
|
\u2600-\u2B55 |
||||||
|
\u200d |
||||||
|
\u23cf |
||||||
|
\u23e9 |
||||||
|
\u231a |
||||||
|
\ufe0f |
||||||
|
\u3030 |
||||||
|
]+""", |
||||||
|
flags=re.UNICODE |
||||||
|
) |
||||||
|
|
||||||
|
def find_emoji(text): |
||||||
|
return [(m.start(), m.end(), m.group()) for m in EMOJI_REGEX.finditer(text)] |
||||||
|
|
||||||
|
def emoji_tex(emoji): |
||||||
|
if emoji not in _cache: |
||||||
|
img = Image.new("RGBA", (128, 128), (0, 0, 0, 0)) |
||||||
|
draw = ImageDraw.Draw(img) |
||||||
|
font = ImageFont.truetype("NotoColorEmoji", 109) |
||||||
|
draw.text((0, 0), emoji, font=font, embedded_color=True) |
||||||
|
buffer = io.BytesIO() |
||||||
|
img.save(buffer, format="PNG") |
||||||
|
l = buffer.tell() |
||||||
|
buffer.seek(0) |
||||||
|
_cache[emoji] = rl.load_texture_from_image(rl.load_image_from_memory(".png", buffer.getvalue(), l)) |
||||||
|
return _cache[emoji] |
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in new issue