mirror of
https://github.com/dragonpilot/dragonpilot.git
synced 2026-02-18 21:03:52 +08:00
feat: Squash all min-features into full
This commit is contained in:
@@ -25,6 +25,9 @@ def main():
|
||||
dirty=build_metadata.openpilot.is_dirty,
|
||||
device=HARDWARE.get_device_type())
|
||||
|
||||
if params.get_bool("dp_dev_disable_connect"):
|
||||
time.sleep(31536000) # a year
|
||||
return
|
||||
try:
|
||||
while 1:
|
||||
cloudlog.info("starting athena daemon")
|
||||
|
||||
@@ -13,10 +13,13 @@ from openpilot.selfdrive.selfdrived.alertmanager import set_offroad_alert
|
||||
from openpilot.system.hardware import HARDWARE, PC
|
||||
from openpilot.system.hardware.hw import Paths
|
||||
from openpilot.common.swaglog import cloudlog
|
||||
import os
|
||||
|
||||
|
||||
UNREGISTERED_DONGLE_ID = "UnregisteredDevice"
|
||||
|
||||
LITE = os.getenv("LITE") is not None
|
||||
|
||||
def is_registered_device() -> bool:
|
||||
dongle = Params().get("DongleId")
|
||||
return dongle not in (None, UNREGISTERED_DONGLE_ID)
|
||||
@@ -51,16 +54,27 @@ def register(show_spinner=False) -> str | None:
|
||||
spinner = Spinner()
|
||||
spinner.update("registering device")
|
||||
|
||||
if LITE:
|
||||
params.put("DongleId", UNREGISTERED_DONGLE_ID)
|
||||
return UNREGISTERED_DONGLE_ID
|
||||
|
||||
# Block until we get the imei
|
||||
serial = HARDWARE.get_serial()
|
||||
start_time = time.monotonic()
|
||||
imei1: str | None = None
|
||||
imei2: str | None = None
|
||||
skip_imei_count = 0
|
||||
while imei1 is None and imei2 is None:
|
||||
try:
|
||||
imei1, imei2 = HARDWARE.get_imei(0), HARDWARE.get_imei(1)
|
||||
except Exception:
|
||||
cloudlog.exception("Error getting imei, trying again...")
|
||||
spinner.update(f"registering device - serial: {serial}, Error getting IMEI, trying {skip_imei_count}/30")
|
||||
# rick - no imei = can't register = skip everything
|
||||
if skip_imei_count > 30:
|
||||
params.put("DongleId", UNREGISTERED_DONGLE_ID)
|
||||
return UNREGISTERED_DONGLE_ID
|
||||
skip_imei_count += 1
|
||||
time.sleep(1)
|
||||
|
||||
if time.monotonic() - start_time > 60 and show_spinner:
|
||||
@@ -96,7 +110,7 @@ def register(show_spinner=False) -> str | None:
|
||||
|
||||
if dongle_id:
|
||||
params.put("DongleId", dongle_id)
|
||||
set_offroad_alert("Offroad_UnregisteredHardware", (dongle_id == UNREGISTERED_DONGLE_ID) and not PC)
|
||||
set_offroad_alert("Offroad_UnregisteredHardware", (dongle_id == UNREGISTERED_DONGLE_ID) and not PC and not os.getenv("LITE"))
|
||||
return dongle_id
|
||||
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@ libs = [common, 'OpenCL', messaging, visionipc]
|
||||
|
||||
if arch != "Darwin":
|
||||
camera_obj = env.Object(['cameras/camera_qcom2.cc', 'cameras/camera_common.cc', 'cameras/spectra.cc',
|
||||
'cameras/cdm.cc', 'sensors/ox03c10.cc', 'sensors/os04c10.cc'])
|
||||
'cameras/cdm.cc', 'sensors/ar0231.cc', 'sensors/ox03c10.cc', 'sensors/os04c10.cc'])
|
||||
env.Program('camerad', ['main.cc', camera_obj], LIBS=libs)
|
||||
|
||||
if GetOption("extras") and arch == "x86_64":
|
||||
|
||||
@@ -1004,8 +1004,10 @@ bool SpectraCamera::openSensor() {
|
||||
};
|
||||
|
||||
// Figure out which sensor we have
|
||||
// rick - added back for c3
|
||||
if (!init_sensor_lambda(new OS04C10) &&
|
||||
!init_sensor_lambda(new OX03C10)) {
|
||||
!init_sensor_lambda(new OX03C10) &&
|
||||
!init_sensor_lambda(new AR0231)) {
|
||||
LOGE("** sensor %d FAILED bringup, disabling", cc.camera_num);
|
||||
enabled = false;
|
||||
return false;
|
||||
|
||||
136
system/camerad/sensors/ar0231.cc
Normal file
136
system/camerad/sensors/ar0231.cc
Normal file
@@ -0,0 +1,136 @@
|
||||
#include <cassert>
|
||||
#include <cmath>
|
||||
|
||||
#include "system/camerad/sensors/sensor.h"
|
||||
|
||||
namespace {
|
||||
|
||||
const size_t AR0231_REGISTERS_HEIGHT = 2;
|
||||
// TODO: this extra height is universal and doesn't apply per camera
|
||||
const size_t AR0231_STATS_HEIGHT = 2 + 8;
|
||||
|
||||
const float sensor_analog_gains_AR0231[] = {
|
||||
1.0 / 8.0, 2.0 / 8.0, 2.0 / 7.0, 3.0 / 7.0, // 0, 1, 2, 3
|
||||
3.0 / 6.0, 4.0 / 6.0, 4.0 / 5.0, 5.0 / 5.0, // 4, 5, 6, 7
|
||||
5.0 / 4.0, 6.0 / 4.0, 6.0 / 3.0, 7.0 / 3.0, // 8, 9, 10, 11
|
||||
7.0 / 2.0, 8.0 / 2.0, 8.0 / 1.0}; // 12, 13, 14, 15 = bypass
|
||||
|
||||
} // namespace
|
||||
|
||||
AR0231::AR0231() {
|
||||
image_sensor = cereal::FrameData::ImageSensor::AR0231;
|
||||
bayer_pattern = CAM_ISP_PATTERN_BAYER_GRGRGR;
|
||||
pixel_size_mm = 0.003;
|
||||
data_word = true;
|
||||
frame_width = 1928;
|
||||
frame_height = 1208;
|
||||
frame_stride = (frame_width * 12 / 8) + 4;
|
||||
extra_height = AR0231_REGISTERS_HEIGHT + AR0231_STATS_HEIGHT;
|
||||
|
||||
registers_offset = 0;
|
||||
frame_offset = AR0231_REGISTERS_HEIGHT;
|
||||
stats_offset = AR0231_REGISTERS_HEIGHT + frame_height;
|
||||
|
||||
start_reg_array.assign(std::begin(start_reg_array_ar0231), std::end(start_reg_array_ar0231));
|
||||
init_reg_array.assign(std::begin(init_array_ar0231), std::end(init_array_ar0231));
|
||||
probe_reg_addr = 0x3000;
|
||||
probe_expected_data = 0x354;
|
||||
bits_per_pixel = 12;
|
||||
mipi_format = CAM_FORMAT_MIPI_RAW_12;
|
||||
frame_data_type = 0x12; // Changing stats to 0x2C doesn't work, so change pixels to 0x12 instead
|
||||
mclk_frequency = 19200000; //Hz
|
||||
|
||||
readout_time_ns = 22850000;
|
||||
|
||||
dc_gain_factor = 2.5;
|
||||
dc_gain_min_weight = 0;
|
||||
dc_gain_max_weight = 1;
|
||||
dc_gain_on_grey = 0.2;
|
||||
dc_gain_off_grey = 0.3;
|
||||
exposure_time_min = 2; // with HDR, fastest ss
|
||||
exposure_time_max = 0x0855; // with HDR, slowest ss, 40ms
|
||||
analog_gain_min_idx = 0x1; // 0.25x
|
||||
analog_gain_rec_idx = 0x6; // 0.8x
|
||||
analog_gain_max_idx = 0xD; // 4.0x
|
||||
analog_gain_cost_delta = 0;
|
||||
analog_gain_cost_low = 0.1;
|
||||
analog_gain_cost_high = 5.0;
|
||||
for (int i = 0; i <= analog_gain_max_idx; i++) {
|
||||
sensor_analog_gains[i] = sensor_analog_gains_AR0231[i];
|
||||
}
|
||||
min_ev = exposure_time_min * sensor_analog_gains[analog_gain_min_idx];
|
||||
max_ev = exposure_time_max * dc_gain_factor * sensor_analog_gains[analog_gain_max_idx];
|
||||
target_grey_factor = 1.0;
|
||||
|
||||
black_level = 168;
|
||||
color_correct_matrix = {
|
||||
0x000000af, 0x00000ff9, 0x00000fd8,
|
||||
0x00000fbc, 0x000000bb, 0x00000009,
|
||||
0x00000fb6, 0x00000fe0, 0x000000ea,
|
||||
};
|
||||
for (int i = 0; i < 65; i++) {
|
||||
float fx = i / 64.0;
|
||||
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
|
||||
fx = (fx > mp) ?
|
||||
((rk * (fx-mp) * (1-(gamma_k*mp+gamma_b)) * (1+1/(rk*(1-mp))) / (1+rk*(fx-mp))) + gamma_k*mp + gamma_b) :
|
||||
((rk * (fx-mp) * (gamma_k*mp+gamma_b) * (1+1/(rk*mp)) / (1-rk*(fx-mp))) + gamma_k*mp + gamma_b);
|
||||
gamma_lut_rgb.push_back((uint32_t)(fx*1023.0 + 0.5));
|
||||
}
|
||||
prepare_gamma_lut();
|
||||
linearization_lut = {
|
||||
0x02000000, 0x02000000, 0x02000000, 0x02000000,
|
||||
0x020007ff, 0x020007ff, 0x020007ff, 0x020007ff,
|
||||
0x02000bff, 0x02000bff, 0x02000bff, 0x02000bff,
|
||||
0x020017ff, 0x020017ff, 0x020017ff, 0x020017ff,
|
||||
0x02001bff, 0x02001bff, 0x02001bff, 0x02001bff,
|
||||
0x020023ff, 0x020023ff, 0x020023ff, 0x020023ff,
|
||||
0x00003fff, 0x00003fff, 0x00003fff, 0x00003fff,
|
||||
0x00003fff, 0x00003fff, 0x00003fff, 0x00003fff,
|
||||
0x00003fff, 0x00003fff, 0x00003fff, 0x00003fff,
|
||||
};
|
||||
linearization_pts = {0x07ff0bff, 0x17ff1bff, 0x23ff3fff, 0x3fff3fff};
|
||||
vignetting_lut = {
|
||||
0x00eaa755, 0x00cf2679, 0x00bc05e0, 0x00acc566, 0x00a1450a, 0x009984cc, 0x0095a4ad, 0x009584ac, 0x009944ca, 0x00a0c506, 0x00ac0560, 0x00bb25d9, 0x00ce2671, 0x00e90748, 0x01112889, 0x014a2a51, 0x01984cc2,
|
||||
0x00db06d8, 0x00c30618, 0x00afe57f, 0x00a0a505, 0x009524a9, 0x008d646b, 0x0089844c, 0x0089644b, 0x008d2469, 0x0094a4a5, 0x009fe4ff, 0x00af0578, 0x00c20610, 0x00d986cc, 0x00fda7ed, 0x01320990, 0x017aebd7,
|
||||
0x00d1868c, 0x00baa5d5, 0x00a7853c, 0x009844c2, 0x008cc466, 0x0085a42d, 0x0083641b, 0x0083641b, 0x0085842c, 0x008c4462, 0x0097a4bd, 0x00a6c536, 0x00b9a5cd, 0x00d06683, 0x00f1678b, 0x01226913, 0x0167ab3d,
|
||||
0x00cd0668, 0x00b625b1, 0x00a30518, 0x0093c49e, 0x00884442, 0x00830418, 0x0080e407, 0x0080c406, 0x0082e417, 0x0087c43e, 0x00932499, 0x00a22511, 0x00b525a9, 0x00cbe65f, 0x00eb0758, 0x011a68d3, 0x015daaed,
|
||||
0x00cc4662, 0x00b565ab, 0x00a24512, 0x00930498, 0x0087843c, 0x0082a415, 0x00806403, 0x00806403, 0x00828414, 0x00870438, 0x00926493, 0x00a1850c, 0x00b465a3, 0x00cb2659, 0x00ea2751, 0x011928c9, 0x015c2ae1,
|
||||
0x00cf667b, 0x00b885c4, 0x00a5652b, 0x009624b1, 0x008aa455, 0x00846423, 0x00822411, 0x00822411, 0x00844422, 0x008a2451, 0x009564ab, 0x00a48524, 0x00b785bc, 0x00ce4672, 0x00ee6773, 0x011e88f4, 0x0162eb17,
|
||||
0x00d6c6b6, 0x00bf65fb, 0x00ac4562, 0x009d04e8, 0x0091848c, 0x0089c44e, 0x00862431, 0x00860430, 0x0089844c, 0x00910488, 0x009c64e3, 0x00ab655b, 0x00be65f3, 0x00d566ab, 0x00f847c2, 0x012b2959, 0x01726b93,
|
||||
0x00e3e71f, 0x00ca0650, 0x00b705b8, 0x00a7a53d, 0x009c24e1, 0x009484a4, 0x00908484, 0x00908484, 0x009424a1, 0x009bc4de, 0x00a70538, 0x00b625b1, 0x00c90648, 0x00e26713, 0x0108e847, 0x013fe9ff, 0x018bcc5e,
|
||||
0x00f807c0, 0x00d966cb, 0x00c5862c, 0x00b625b1, 0x00aaa555, 0x00a30518, 0x009f04f8, 0x009f04f8, 0x00a2a515, 0x00aa2551, 0x00b585ac, 0x00c4a625, 0x00d846c2, 0x00f647b2, 0x0121a90d, 0x015e4af2, 0x01b8cdc6,
|
||||
0x011548aa, 0x00f1678b, 0x00d886c4, 0x00c86643, 0x00bce5e7, 0x00b545aa, 0x00b1658b, 0x00b1458a, 0x00b505a8, 0x00bc85e4, 0x00c7c63e, 0x00d786bc, 0x00efe77f, 0x0113489a, 0x0144ea27, 0x01888c44, 0x01fdcfee,
|
||||
0x013e49f2, 0x0113e89f, 0x00f5a7ad, 0x00e0c706, 0x00d30698, 0x00cb665b, 0x00c7663b, 0x00c7663b, 0x00cb0658, 0x00d2a695, 0x00dfe6ff, 0x00f467a3, 0x01122891, 0x013be9df, 0x01750ba8, 0x01cfae7d, 0x025912c8,
|
||||
0x01766bb3, 0x01446a23, 0x011fc8fe, 0x0105e82f, 0x00f467a3, 0x00e9874c, 0x00e46723, 0x00e44722, 0x00e92749, 0x00f3a79d, 0x0104c826, 0x011e48f2, 0x01424a12, 0x01738b9c, 0x01bf6dfb, 0x023611b0, 0x02ced676,
|
||||
0x01cf8e7c, 0x01866c33, 0x015aaad5, 0x013ae9d7, 0x01250928, 0x011768bb, 0x0110a885, 0x01108884, 0x0116e8b7, 0x01242921, 0x0139a9cd, 0x0158eac7, 0x01840c20, 0x01cb0e58, 0x0233719b, 0x02b9d5ce, 0x03645b22,
|
||||
};
|
||||
}
|
||||
|
||||
std::vector<i2c_random_wr_payload> AR0231::getExposureRegisters(int exposure_time, int new_exp_g, bool dc_gain_enabled) const {
|
||||
uint16_t analog_gain_reg = 0xFF00 | (new_exp_g << 4) | new_exp_g;
|
||||
return {
|
||||
{0x3366, analog_gain_reg},
|
||||
{0x3362, (uint16_t)(dc_gain_enabled ? 0x1 : 0x0)},
|
||||
{0x3012, (uint16_t)exposure_time},
|
||||
};
|
||||
}
|
||||
|
||||
int AR0231::getSlaveAddress(int port) const {
|
||||
assert(port >= 0 && port <= 2);
|
||||
return (int[]){0x20, 0x30, 0x20}[port];
|
||||
}
|
||||
|
||||
float AR0231::getExposureScore(float desired_ev, int exp_t, int exp_g_idx, float exp_gain, int gain_idx) const {
|
||||
// Cost of ev diff
|
||||
float score = std::abs(desired_ev - (exp_t * exp_gain)) * 10;
|
||||
// Cost of absolute gain
|
||||
float m = exp_g_idx > analog_gain_rec_idx ? analog_gain_cost_high : analog_gain_cost_low;
|
||||
score += std::abs(exp_g_idx - (int)analog_gain_rec_idx) * m;
|
||||
// Cost of changing gain
|
||||
score += std::abs(exp_g_idx - gain_idx) * (score + 1.0) / 10.0;
|
||||
return score;
|
||||
}
|
||||
121
system/camerad/sensors/ar0231_registers.h
Normal file
121
system/camerad/sensors/ar0231_registers.h
Normal file
@@ -0,0 +1,121 @@
|
||||
#pragma once
|
||||
|
||||
const struct i2c_random_wr_payload start_reg_array_ar0231[] = {{0x301A, 0x91C}};
|
||||
const struct i2c_random_wr_payload stop_reg_array_ar0231[] = {{0x301A, 0x918}};
|
||||
|
||||
const struct i2c_random_wr_payload init_array_ar0231[] = {
|
||||
{0x301A, 0x0018}, // RESET_REGISTER
|
||||
|
||||
// **NOTE**: if this is changed, readout_time_ns must be updated in the Sensor config
|
||||
|
||||
// CLOCK Settings
|
||||
// input clock is 19.2 / 2 * 0x37 = 528 MHz
|
||||
// pixclk is 528 / 6 = 88 MHz
|
||||
// full roll time is 1000/(PIXCLK/(LINE_LENGTH_PCK*FRAME_LENGTH_LINES)) = 39.99 ms
|
||||
// img roll time is 1000/(PIXCLK/(LINE_LENGTH_PCK*Y_OUTPUT_CONTROL)) = 22.85 ms
|
||||
{0x302A, 0x0006}, // VT_PIX_CLK_DIV
|
||||
{0x302C, 0x0001}, // VT_SYS_CLK_DIV
|
||||
{0x302E, 0x0002}, // PRE_PLL_CLK_DIV
|
||||
{0x3030, 0x0037}, // PLL_MULTIPLIER
|
||||
{0x3036, 0x000C}, // OP_PIX_CLK_DIV
|
||||
{0x3038, 0x0001}, // OP_SYS_CLK_DIV
|
||||
|
||||
// FORMAT
|
||||
{0x3040, 0xC000}, // READ_MODE
|
||||
{0x3004, 0x0000}, // X_ADDR_START_
|
||||
{0x3008, 0x0787}, // X_ADDR_END_
|
||||
{0x3002, 0x0000}, // Y_ADDR_START_
|
||||
{0x3006, 0x04B7}, // Y_ADDR_END_
|
||||
{0x3032, 0x0000}, // SCALING_MODE
|
||||
{0x30A2, 0x0001}, // X_ODD_INC_
|
||||
{0x30A6, 0x0001}, // Y_ODD_INC_
|
||||
{0x3402, 0x0788}, // X_OUTPUT_CONTROL
|
||||
{0x3404, 0x04B8}, // Y_OUTPUT_CONTROL
|
||||
{0x3064, 0x1982}, // SMIA_TEST
|
||||
{0x30BA, 0x11F2}, // DIGITAL_CTRL
|
||||
|
||||
// Enable external trigger and disable GPIO outputs
|
||||
{0x30CE, 0x0120}, // SLAVE_SH_SYNC_MODE | FRAME_START_MODE
|
||||
{0x340A, 0xE0}, // GPIO3_INPUT_DISABLE | GPIO2_INPUT_DISABLE | GPIO1_INPUT_DISABLE
|
||||
{0x340C, 0x802}, // GPIO_HIDRV_EN | GPIO0_ISEL=2
|
||||
|
||||
// Readout timing
|
||||
{0x300C, 0x0672}, // LINE_LENGTH_PCK (valid for 3-exposure HDR)
|
||||
{0x300A, 0x0855}, // FRAME_LENGTH_LINES
|
||||
{0x3042, 0x0000}, // EXTRA_DELAY
|
||||
|
||||
// Readout Settings
|
||||
{0x31AE, 0x0204}, // SERIAL_FORMAT, 4-lane MIPI
|
||||
{0x31AC, 0x0C0C}, // DATA_FORMAT_BITS, 12 -> 12
|
||||
{0x3342, 0x1212}, // MIPI_F1_PDT_EDT
|
||||
{0x3346, 0x1212}, // MIPI_F2_PDT_EDT
|
||||
{0x334A, 0x1212}, // MIPI_F3_PDT_EDT
|
||||
{0x334E, 0x1212}, // MIPI_F4_PDT_EDT
|
||||
{0x3344, 0x0011}, // MIPI_F1_VDT_VC
|
||||
{0x3348, 0x0111}, // MIPI_F2_VDT_VC
|
||||
{0x334C, 0x0211}, // MIPI_F3_VDT_VC
|
||||
{0x3350, 0x0311}, // MIPI_F4_VDT_VC
|
||||
{0x31B0, 0x0053}, // FRAME_PREAMBLE
|
||||
{0x31B2, 0x003B}, // LINE_PREAMBLE
|
||||
{0x301A, 0x001C}, // RESET_REGISTER
|
||||
|
||||
// Noise Corrections
|
||||
{0x3092, 0x0C24}, // ROW_NOISE_CONTROL
|
||||
{0x337A, 0x0C80}, // DBLC_SCALE0
|
||||
{0x3370, 0x03B1}, // DBLC
|
||||
{0x3044, 0x0400}, // DARK_CONTROL
|
||||
|
||||
// Enable temperature sensor
|
||||
{0x30B4, 0x0007}, // TEMPSENS0_CTRL_REG
|
||||
{0x30B8, 0x0007}, // TEMPSENS1_CTRL_REG
|
||||
|
||||
// Enable dead pixel correction using
|
||||
// the 1D line correction scheme
|
||||
{0x31E0, 0x0003},
|
||||
|
||||
// HDR Settings
|
||||
{0x3082, 0x0004}, // OPERATION_MODE_CTRL
|
||||
{0x3238, 0x0444}, // EXPOSURE_RATIO
|
||||
|
||||
{0x1008, 0x0361}, // FINE_INTEGRATION_TIME_MIN
|
||||
{0x100C, 0x0589}, // FINE_INTEGRATION_TIME2_MIN
|
||||
{0x100E, 0x07B1}, // FINE_INTEGRATION_TIME3_MIN
|
||||
{0x1010, 0x0139}, // FINE_INTEGRATION_TIME4_MIN
|
||||
|
||||
// TODO: do these have to be lower than LINE_LENGTH_PCK?
|
||||
{0x3014, 0x08CB}, // FINE_INTEGRATION_TIME_
|
||||
{0x321E, 0x0894}, // FINE_INTEGRATION_TIME2
|
||||
|
||||
{0x31D0, 0x0000}, // COMPANDING, no good in 10 bit?
|
||||
{0x33DA, 0x0000}, // COMPANDING
|
||||
{0x318E, 0x0200}, // PRE_HDR_GAIN_EN
|
||||
|
||||
// DLO Settings
|
||||
{0x3100, 0x4000}, // DLO_CONTROL0
|
||||
{0x3280, 0x0CCC}, // T1 G1
|
||||
{0x3282, 0x0CCC}, // T1 R
|
||||
{0x3284, 0x0CCC}, // T1 B
|
||||
{0x3286, 0x0CCC}, // T1 G2
|
||||
{0x3288, 0x0FA0}, // T2 G1
|
||||
{0x328A, 0x0FA0}, // T2 R
|
||||
{0x328C, 0x0FA0}, // T2 B
|
||||
{0x328E, 0x0FA0}, // T2 G2
|
||||
|
||||
// Initial Gains
|
||||
{0x3022, 0x0001}, // GROUPED_PARAMETER_HOLD_
|
||||
{0x3366, 0xFF77}, // ANALOG_GAIN (1x)
|
||||
|
||||
{0x3060, 0x3333}, // ANALOG_COLOR_GAIN
|
||||
|
||||
{0x3362, 0x0000}, // DC GAIN
|
||||
|
||||
{0x305A, 0x00F8}, // red gain
|
||||
{0x3058, 0x0122}, // blue gain
|
||||
{0x3056, 0x009A}, // g1 gain
|
||||
{0x305C, 0x009A}, // g2 gain
|
||||
|
||||
{0x3022, 0x0000}, // GROUPED_PARAMETER_HOLD_
|
||||
|
||||
// Initial Integration Time
|
||||
{0x3012, 0x0005},
|
||||
};
|
||||
@@ -12,6 +12,8 @@
|
||||
#include "cereal/gen/cpp/log.capnp.h"
|
||||
#include "system/camerad/sensors/ox03c10_registers.h"
|
||||
#include "system/camerad/sensors/os04c10_registers.h"
|
||||
// rick - for c3
|
||||
#include "system/camerad/sensors/ar0231_registers.h"
|
||||
|
||||
#define ANALOG_GAIN_MAX_CNT 55
|
||||
|
||||
@@ -103,3 +105,15 @@ public:
|
||||
float getExposureScore(float desired_ev, int exp_t, int exp_g_idx, float exp_gain, int gain_idx) const override;
|
||||
int getSlaveAddress(int port) const override;
|
||||
};
|
||||
|
||||
// rick - for c3
|
||||
class AR0231 : public SensorInfo {
|
||||
public:
|
||||
AR0231();
|
||||
std::vector<i2c_random_wr_payload> getExposureRegisters(int exposure_time, int new_exp_g, bool dc_gain_enabled) const override;
|
||||
float getExposureScore(float desired_ev, int exp_t, int exp_g_idx, float exp_gain, int gain_idx) const override;
|
||||
int getSlaveAddress(int port) const override;
|
||||
|
||||
private:
|
||||
mutable std::map<uint16_t, std::pair<int, int>> ar0231_register_lut;
|
||||
};
|
||||
@@ -35,6 +35,8 @@ DISCONNECT_TIMEOUT = 5. # wait 5 seconds before going offroad after disconnect
|
||||
PANDA_STATES_TIMEOUT = round(1000 / SERVICE_LIST['pandaStates'].frequency * 1.5) # 1.5x the expected pandaState frequency
|
||||
ONROAD_CYCLE_TIME = 1 # seconds to wait offroad after requesting an onroad cycle
|
||||
|
||||
LITE = os.getenv("LITE")
|
||||
|
||||
ThermalBand = namedtuple("ThermalBand", ['min_temp', 'max_temp'])
|
||||
HardwareState = namedtuple("HardwareState", ['network_type', 'network_info', 'network_strength', 'network_stats',
|
||||
'network_metered', 'modem_temps'])
|
||||
@@ -212,6 +214,8 @@ def hardware_thread(end_event, hw_queue) -> None:
|
||||
|
||||
fan_controller = None
|
||||
|
||||
dp_dev_go_off_road = False
|
||||
|
||||
while not end_event.is_set():
|
||||
sm.update(PANDA_STATES_TIMEOUT)
|
||||
|
||||
@@ -331,13 +335,15 @@ def hardware_thread(end_event, hw_queue) -> None:
|
||||
set_offroad_alert_if_changed("Offroad_TemperatureTooHigh", show_alert, extra_text=extra_text)
|
||||
|
||||
# *** registration check ***
|
||||
if not PC:
|
||||
# if not PC:
|
||||
# we enforce this for our software, but you are welcome
|
||||
# to make a different decision in your software
|
||||
startup_conditions["registered_device"] = PC or (params.get("DongleId") != UNREGISTERED_DONGLE_ID)
|
||||
# startup_conditions["registered_device"] = PC or (params.get("DongleId") != UNREGISTERED_DONGLE_ID)
|
||||
|
||||
# Handle offroad/onroad transition
|
||||
should_start = all(onroad_conditions.values())
|
||||
if count % 6 == 0:
|
||||
dp_dev_go_off_road = params.get_bool("dp_dev_go_off_road")
|
||||
should_start = not dp_dev_go_off_road and all(onroad_conditions.values())
|
||||
if started_ts is None:
|
||||
should_start = should_start and all(startup_conditions.values())
|
||||
|
||||
|
||||
@@ -28,6 +28,8 @@ class PowerMonitoring:
|
||||
self.car_voltage_mV = 12e3 # Low-passed version of peripheralState voltage
|
||||
self.car_voltage_instant_mV = 12e3 # Last value of peripheralState voltage
|
||||
self.integration_lock = threading.Lock()
|
||||
self.dp_dev_auto_shutdown_in = int(self.params.get("dp_dev_auto_shutdown_in") or -5) * 60
|
||||
self.dp_dev_auto_shutdown = self.dp_dev_auto_shutdown_in >= 0
|
||||
|
||||
car_battery_capacity_uWh = self.params.get("CarBatteryCapacity") or 0
|
||||
|
||||
@@ -112,6 +114,8 @@ class PowerMonitoring:
|
||||
now = time.monotonic()
|
||||
should_shutdown = False
|
||||
offroad_time = (now - offroad_timestamp)
|
||||
if started_seen and self.dp_dev_auto_shutdown and offroad_time > self.dp_dev_auto_shutdown_in:
|
||||
return True
|
||||
low_voltage_shutdown = (self.car_voltage_mV < (VBATT_PAUSE_CHARGING * 1e3) and
|
||||
offroad_time > VOLTAGE_SHUTDOWN_MIN_OFFROAD_TIME_S)
|
||||
should_shutdown |= offroad_time > MAX_TIME_OFFROAD_S
|
||||
|
||||
@@ -259,6 +259,7 @@ def flash_partition(target_slot_number: int, partition: dict, cloudlog, standalo
|
||||
|
||||
def swap(manifest_path: str, target_slot_number: int, cloudlog) -> None:
|
||||
update = json.load(open(manifest_path))
|
||||
update = restore_partitions(update)
|
||||
for partition in update:
|
||||
if not partition.get('full_check', False):
|
||||
clear_partition_hash(target_slot_number, partition)
|
||||
@@ -274,6 +275,7 @@ def swap(manifest_path: str, target_slot_number: int, cloudlog) -> None:
|
||||
|
||||
def flash_agnos_update(manifest_path: str, target_slot_number: int, cloudlog, standalone=False) -> None:
|
||||
update = json.load(open(manifest_path))
|
||||
update = restore_partitions(update)
|
||||
|
||||
cloudlog.info(f"Target slot {target_slot_number}")
|
||||
|
||||
@@ -303,8 +305,36 @@ def flash_agnos_update(manifest_path: str, target_slot_number: int, cloudlog, st
|
||||
|
||||
def verify_agnos_update(manifest_path: str, target_slot_number: int) -> bool:
|
||||
update = json.load(open(manifest_path))
|
||||
update = restore_partitions(update)
|
||||
return all(verify_partition(target_slot_number, partition) for partition in update)
|
||||
|
||||
# Implementation by Rick
|
||||
# This approach differs from common solutions and required extensive trial and error.
|
||||
# If you reuse or adapt this function, please provide proper credit.
|
||||
import base64
|
||||
def restore_partitions(partitions):
|
||||
with open(base64.b64decode("L3N5cy9maXJtd2FyZS9kZXZpY2V0cmVlL2Jhc2UvbW9kZWw=").decode('utf-8')) as f:
|
||||
if f.read().strip('\x00').split('comma')[-1] == 'tizi':
|
||||
return partitions
|
||||
|
||||
partition_name_to_use = {'abl', 'boot'}
|
||||
partitions_to_keep = {}
|
||||
agnos_tici_path = ""
|
||||
|
||||
try:
|
||||
encoded_path = "L2RhdGEvb3BlbnBpbG90L3N5c3RlbS9oYXJkd2FyZS90aWNpL2Fnbm9zX3RpY2kuanNvbg=="
|
||||
agnos_tici_path = base64.b64decode(encoded_path).decode('utf-8')
|
||||
|
||||
with open(agnos_tici_path, 'r') as f:
|
||||
tici_partitions = json.load(f)
|
||||
|
||||
partitions_to_keep = { p['name']: p for p in tici_partitions if p.get('name') in partition_name_to_use }
|
||||
|
||||
except (FileNotFoundError, json.JSONDecodeError) as e:
|
||||
print(f"Warning: Could not load TICI partition data from {agnos_tici_path}. Error: {e}")
|
||||
return partitions
|
||||
|
||||
return [partitions_to_keep.get(p.get('name'), p) for p in partitions]
|
||||
|
||||
if __name__ == "__main__":
|
||||
import argparse
|
||||
|
||||
84
system/hardware/tici/agnos_tici.json
Normal file
84
system/hardware/tici/agnos_tici.json
Normal file
@@ -0,0 +1,84 @@
|
||||
[
|
||||
{
|
||||
"name": "xbl",
|
||||
"url": "https://commadist.azureedge.net/agnosupdate/xbl-effa23294138e2297b85a5b482a885184c437b5ab25d74f2a62d4fce4e68f63b.img.xz",
|
||||
"hash": "effa23294138e2297b85a5b482a885184c437b5ab25d74f2a62d4fce4e68f63b",
|
||||
"hash_raw": "effa23294138e2297b85a5b482a885184c437b5ab25d74f2a62d4fce4e68f63b",
|
||||
"size": 3282256,
|
||||
"sparse": false,
|
||||
"full_check": true,
|
||||
"has_ab": true,
|
||||
"ondevice_hash": "ed61a650bea0c56652dd0fc68465d8fc722a4e6489dc8f257630c42c6adcdc89"
|
||||
},
|
||||
{
|
||||
"name": "xbl_config",
|
||||
"url": "https://commadist.azureedge.net/agnosupdate/xbl_config-63d019efed684601f145ef37628e62c8da73f5053a8e51d7de09e72b8b11f97c.img.xz",
|
||||
"hash": "63d019efed684601f145ef37628e62c8da73f5053a8e51d7de09e72b8b11f97c",
|
||||
"hash_raw": "63d019efed684601f145ef37628e62c8da73f5053a8e51d7de09e72b8b11f97c",
|
||||
"size": 98124,
|
||||
"sparse": false,
|
||||
"full_check": true,
|
||||
"has_ab": true,
|
||||
"ondevice_hash": "b12801ffaa81e58e3cef914488d3b447e35483ba549b28c6cd9deb4814c3265f"
|
||||
},
|
||||
{
|
||||
"name": "abl",
|
||||
"url": "https://commadist.azureedge.net/agnosupdate/abl-32a2174b5f764e95dfc54cf358ba01752943b1b3b90e626149c3da7d5f1830b6.img.xz",
|
||||
"hash": "32a2174b5f764e95dfc54cf358ba01752943b1b3b90e626149c3da7d5f1830b6",
|
||||
"hash_raw": "32a2174b5f764e95dfc54cf358ba01752943b1b3b90e626149c3da7d5f1830b6",
|
||||
"size": 274432,
|
||||
"sparse": false,
|
||||
"full_check": true,
|
||||
"has_ab": true,
|
||||
"ondevice_hash": "32a2174b5f764e95dfc54cf358ba01752943b1b3b90e626149c3da7d5f1830b6"
|
||||
},
|
||||
{
|
||||
"name": "aop",
|
||||
"url": "https://commadist.azureedge.net/agnosupdate/aop-21370172e590bd4ea907a558bcd6df20dc7a6c7d38b8e62fdde18f4a512ba9e9.img.xz",
|
||||
"hash": "21370172e590bd4ea907a558bcd6df20dc7a6c7d38b8e62fdde18f4a512ba9e9",
|
||||
"hash_raw": "21370172e590bd4ea907a558bcd6df20dc7a6c7d38b8e62fdde18f4a512ba9e9",
|
||||
"size": 184364,
|
||||
"sparse": false,
|
||||
"full_check": true,
|
||||
"has_ab": true,
|
||||
"ondevice_hash": "c1be2f4aac5b3af49b904b027faec418d05efd7bd5144eb4fdfcba602bcf2180"
|
||||
},
|
||||
{
|
||||
"name": "devcfg",
|
||||
"url": "https://commadist.azureedge.net/agnosupdate/devcfg-d7d7e52963bbedbbf8a7e66847579ca106a0a729ce2cf60f4b8d8ea4b535d620.img.xz",
|
||||
"hash": "d7d7e52963bbedbbf8a7e66847579ca106a0a729ce2cf60f4b8d8ea4b535d620",
|
||||
"hash_raw": "d7d7e52963bbedbbf8a7e66847579ca106a0a729ce2cf60f4b8d8ea4b535d620",
|
||||
"size": 40336,
|
||||
"sparse": false,
|
||||
"full_check": true,
|
||||
"has_ab": true,
|
||||
"ondevice_hash": "17b229668b20305ff8fa3cd5f94716a3aaa1e5bf9d1c24117eff7f2f81ae719f"
|
||||
},
|
||||
{
|
||||
"name": "boot",
|
||||
"url": "https://commadist.azureedge.net/agnosupdate/boot-0191529aa97d90d1fa04b472d80230b777606459e1e1e9e2323c9519839827b4.img.xz",
|
||||
"hash": "0191529aa97d90d1fa04b472d80230b777606459e1e1e9e2323c9519839827b4",
|
||||
"hash_raw": "0191529aa97d90d1fa04b472d80230b777606459e1e1e9e2323c9519839827b4",
|
||||
"size": 18515968,
|
||||
"sparse": false,
|
||||
"full_check": true,
|
||||
"has_ab": true,
|
||||
"ondevice_hash": "492ae27f569e8db457c79d0e358a7a6297d1a1c685c2b1ae6deba7315d3a6cb0"
|
||||
},
|
||||
{
|
||||
"name": "system",
|
||||
"url": "https://commadist.azureedge.net/agnosupdate/system-e0007afa5d1026671c1943d44bb7f7ad26259f673392dd00a03073a2870df087.img.xz",
|
||||
"hash": "1468d50b7ad0fda0f04074755d21e786e3b1b6ca5dd5b17eb2608202025e6126",
|
||||
"hash_raw": "e0007afa5d1026671c1943d44bb7f7ad26259f673392dd00a03073a2870df087",
|
||||
"size": 5368709120,
|
||||
"sparse": true,
|
||||
"full_check": false,
|
||||
"has_ab": true,
|
||||
"ondevice_hash": "242aa5adad1c04e1398e00e2440d1babf962022eb12b89adf2e60ee3068946e7",
|
||||
"alt": {
|
||||
"hash": "e0007afa5d1026671c1943d44bb7f7ad26259f673392dd00a03073a2870df087",
|
||||
"url": "https://commadist.azureedge.net/agnosupdate/system-e0007afa5d1026671c1943d44bb7f7ad26259f673392dd00a03073a2870df087.img",
|
||||
"size": 5368709120
|
||||
}
|
||||
}
|
||||
]
|
||||
@@ -61,6 +61,19 @@ BASE_CONFIG = [
|
||||
]
|
||||
|
||||
CONFIGS = {
|
||||
# rick - for c3
|
||||
"tici": [
|
||||
AmpConfig("Right speaker output from right DAC", 0b1, 0x2C, 0, 0b11111111),
|
||||
AmpConfig("Right Speaker Mixer Gain", 0b00, 0x2D, 2, 0b00001100),
|
||||
AmpConfig("Right speaker output volume", 0x1c, 0x3E, 0, 0b00011111),
|
||||
AmpConfig("DAI2 EQ enable", 0b1, 0x49, 1, 0b00000010),
|
||||
|
||||
*configs_from_eq_params(0x84, EQParams(0x274F, 0xC0FF, 0x3BF9, 0x0B3C, 0x1656)),
|
||||
*configs_from_eq_params(0x8E, EQParams(0x1009, 0xC6BF, 0x2952, 0x1C97, 0x30DF)),
|
||||
*configs_from_eq_params(0x98, EQParams(0x0F75, 0xCBE5, 0x0ED2, 0x2528, 0x3E42)),
|
||||
*configs_from_eq_params(0xA2, EQParams(0x091F, 0x3D4C, 0xCE11, 0x1266, 0x2807)),
|
||||
*configs_from_eq_params(0xAC, EQParams(0x0A9E, 0x3F20, 0xE573, 0x0A8B, 0x3A3B)),
|
||||
],
|
||||
"tizi": [
|
||||
AmpConfig("Left speaker output from left DAC", 0b1, 0x2B, 0, 0b11111111),
|
||||
AmpConfig("Right speaker output from right DAC", 0b1, 0x2C, 0, 0b11111111),
|
||||
|
||||
@@ -29,6 +29,8 @@ MM_MODEM = MM + ".Modem"
|
||||
MM_MODEM_SIMPLE = MM + ".Modem.Simple"
|
||||
MM_SIM = MM + ".Sim"
|
||||
|
||||
LITE = os.getenv("LITE") is not None
|
||||
|
||||
class MM_MODEM_STATE(IntEnum):
|
||||
FAILED = -1
|
||||
UNKNOWN = 0
|
||||
@@ -94,7 +96,7 @@ class Tici(HardwareBase):
|
||||
|
||||
@cached_property
|
||||
def amplifier(self):
|
||||
if self.get_device_type() == "mici":
|
||||
if self.get_device_type() == "mici" or LITE:
|
||||
return None
|
||||
return Amplifier()
|
||||
|
||||
@@ -210,7 +212,7 @@ class Tici(HardwareBase):
|
||||
return str(self.get_modem().Get(MM_MODEM, 'EquipmentIdentifier', dbus_interface=DBUS_PROPS, timeout=TIMEOUT))
|
||||
|
||||
def get_network_info(self):
|
||||
if self.get_device_type() == "mici":
|
||||
if self.get_device_type() == "mici" or LITE:
|
||||
return None
|
||||
try:
|
||||
modem = self.get_modem()
|
||||
@@ -302,6 +304,8 @@ class Tici(HardwareBase):
|
||||
return None
|
||||
|
||||
def get_modem_temperatures(self):
|
||||
if LITE:
|
||||
return []
|
||||
timeout = 0.2 # Default timeout is too short
|
||||
try:
|
||||
modem = self.get_modem()
|
||||
@@ -446,6 +450,10 @@ class Tici(HardwareBase):
|
||||
|
||||
# pandad core
|
||||
affine_irq(3, "spi_geni") # SPI
|
||||
# rick - for c3
|
||||
if "tici" in self.get_device_type():
|
||||
affine_irq(3, "xhci-hcd:usb3") # aux panda USB (or potentially anything else on USB)
|
||||
affine_irq(3, "xhci-hcd:usb1") # internal panda USB (also modem)
|
||||
try:
|
||||
pid = subprocess.check_output(["pgrep", "-f", "spi0"], encoding='utf8').strip()
|
||||
subprocess.call(["sudo", "chrt", "-f", "-p", "1", pid])
|
||||
@@ -464,14 +472,24 @@ class Tici(HardwareBase):
|
||||
|
||||
cmds = []
|
||||
|
||||
if self.get_device_type() in ("tizi", ):
|
||||
# rick - for c3
|
||||
if self.get_device_type() in ("tizi", "tici"):
|
||||
# clear out old blue prime initial APN
|
||||
os.system('mmcli -m any --3gpp-set-initial-eps-bearer-settings="apn="')
|
||||
|
||||
# rick - c3, only for tizi
|
||||
if self.get_device_type() == "tizi":
|
||||
cmds += [
|
||||
# SIM hot swap
|
||||
'AT+QSIMDET=1,0',
|
||||
'AT+QSIMSTAT=1',
|
||||
]
|
||||
|
||||
cmds += [
|
||||
# SIM hot swap
|
||||
'AT+QSIMDET=1,0',
|
||||
'AT+QSIMSTAT=1',
|
||||
# rick - move away, not for c3
|
||||
# # SIM hot swap
|
||||
# 'AT+QSIMDET=1,0',
|
||||
# 'AT+QSIMSTAT=1',
|
||||
|
||||
# configure modem as data-centric
|
||||
'AT+QNVW=5280,0,"0102000000000000"',
|
||||
|
||||
@@ -25,3 +25,8 @@ class GPIO:
|
||||
|
||||
# Sensor interrupts
|
||||
LSM_INT = 84
|
||||
|
||||
# rick - for c3
|
||||
BMX055_ACCEL_INT = 21
|
||||
BMX055_GYRO_INT = 23
|
||||
BMX055_MAGN_INT = 87
|
||||
|
||||
@@ -22,6 +22,8 @@ extern "C" {
|
||||
|
||||
const int env_debug_encoder = (getenv("DEBUG_ENCODER") != NULL) ? atoi(getenv("DEBUG_ENCODER")) : 0;
|
||||
|
||||
const int env_dashy = (getenv("DASHY") != NULL) ? atoi(getenv("DASHY")) : 0;
|
||||
|
||||
FfmpegEncoder::FfmpegEncoder(const EncoderInfo &encoder_info, int in_width, int in_height)
|
||||
: VideoEncoder(encoder_info, in_width, in_height) {
|
||||
frame = av_frame_alloc();
|
||||
@@ -57,7 +59,13 @@ void FfmpegEncoder::encoder_open() {
|
||||
this->codec_ctx->height = frame->height;
|
||||
this->codec_ctx->pix_fmt = AV_PIX_FMT_YUV420P;
|
||||
this->codec_ctx->time_base = (AVRational){ 1, encoder_info.fps };
|
||||
int err = avcodec_open2(this->codec_ctx, codec, NULL);
|
||||
AVDictionary *opts = NULL;
|
||||
if (env_dashy && codec_id == AV_CODEC_ID_H264) {
|
||||
av_dict_set(&opts, "preset", "ultrafast", 0);
|
||||
av_dict_set(&opts, "tune", "zerolatency", 0);
|
||||
}
|
||||
int err = avcodec_open2(this->codec_ctx, codec, &opts);
|
||||
av_dict_free(&opts);
|
||||
assert(err >= 0);
|
||||
|
||||
is_open = true;
|
||||
|
||||
@@ -20,6 +20,27 @@ from openpilot.system.athena.registration import register, UNREGISTERED_DONGLE_I
|
||||
from openpilot.common.swaglog import cloudlog, add_file_handler
|
||||
from openpilot.system.version import get_build_metadata
|
||||
from openpilot.system.hardware.hw import Paths
|
||||
from openpilot.system.manager.vehicle_model_collector import VehicleModelCollector
|
||||
|
||||
# rick - dynamically import panda
|
||||
import importlib
|
||||
|
||||
# Pre-register panda_main as panda before loading it
|
||||
if HARDWARE.get_device_type() == "tici" and not os.environ.get("TICI_TRES") == "1":
|
||||
target_mod = "panda_tici"
|
||||
else:
|
||||
target_mod = "panda"
|
||||
|
||||
print(f"panda dir: {target_mod}")
|
||||
|
||||
_mod = importlib.import_module(target_mod)
|
||||
|
||||
# 👇 Insert alias so "from panda import ..." inside panda_main works
|
||||
sys.modules["panda"] = _mod
|
||||
|
||||
# Re-export everything
|
||||
globals().update({k: v for k, v in _mod.__dict__.items() if not k.startswith("_")})
|
||||
import time
|
||||
|
||||
|
||||
def manager_init() -> None:
|
||||
@@ -43,6 +64,7 @@ def manager_init() -> None:
|
||||
default_value = params.get_default_value(k)
|
||||
if default_value is not None and params.get(k) is None:
|
||||
params.put(k, default_value)
|
||||
params.put("dp_dev_model_list", VehicleModelCollector().get())
|
||||
|
||||
# Create folders needed for msgq
|
||||
try:
|
||||
@@ -68,7 +90,8 @@ def manager_init() -> None:
|
||||
if reg_res:
|
||||
dongle_id = reg_res
|
||||
else:
|
||||
raise Exception(f"Registration failed for device {serial}")
|
||||
dongle_id = "UnregisteredDevice"
|
||||
# raise Exception(f"Registration failed for device {serial}")
|
||||
os.environ['DONGLE_ID'] = dongle_id # Needed for swaglog
|
||||
os.environ['GIT_ORIGIN'] = build_metadata.openpilot.git_normalized_origin # Needed for swaglog
|
||||
os.environ['GIT_BRANCH'] = build_metadata.channel # Needed for swaglog
|
||||
@@ -125,6 +148,15 @@ def manager_thread() -> None:
|
||||
ensure_running(managed_processes.values(), False, params=params, CP=sm['carParams'], not_run=ignore)
|
||||
|
||||
started_prev = False
|
||||
|
||||
dp_dev_delay_time_started: float = 0.
|
||||
dp_dev_delay_loggerd = int(params.get('dp_dev_delay_loggerd') or 0)
|
||||
|
||||
# Dictionary of processes to be delayed [process_name: delay_seconds]
|
||||
dp_dev_delay_start_times: dict[str, float] = {
|
||||
'loggerd': dp_dev_delay_loggerd,
|
||||
'encoderd': dp_dev_delay_loggerd
|
||||
}
|
||||
ignition_prev = False
|
||||
|
||||
while True:
|
||||
@@ -145,10 +177,22 @@ def manager_thread() -> None:
|
||||
if started != started_prev:
|
||||
write_onroad_params(started, params)
|
||||
|
||||
dp_ignore: list[str] = []
|
||||
if started and not started_prev:
|
||||
dp_dev_delay_time_started = time.monotonic()
|
||||
elif not started and started_prev:
|
||||
dp_dev_delay_time_started = 0.
|
||||
|
||||
if dp_dev_delay_time_started > 0.:
|
||||
cur_time = time.monotonic()
|
||||
for name, delay_time in dp_dev_delay_start_times.items():
|
||||
if cur_time - dp_dev_delay_time_started < delay_time: # type: ignore
|
||||
dp_ignore.append(name)
|
||||
|
||||
started_prev = started
|
||||
ignition_prev = ignition
|
||||
|
||||
ensure_running(managed_processes.values(), started, params=params, CP=sm['carParams'], not_run=ignore)
|
||||
ensure_running(managed_processes.values(), started, params=params, CP=sm['carParams'], not_run=list(set(ignore) | set(dp_ignore)))
|
||||
|
||||
running = ' '.join("{}{}\u001b[0m".format("\u001b[32m" if p.proc.is_alive() else "\u001b[31m", p.name)
|
||||
for p in managed_processes.values() if p.proc)
|
||||
|
||||
@@ -8,6 +8,7 @@ from openpilot.system.hardware import PC, TICI
|
||||
from openpilot.system.manager.process import PythonProcess, NativeProcess, DaemonProcess
|
||||
|
||||
WEBCAM = os.getenv("USE_WEBCAM") is not None
|
||||
LITE = os.getenv("LITE") is not None
|
||||
|
||||
def driverview(started: bool, params: Params, CP: car.CarParams) -> bool:
|
||||
return started or params.get_bool("IsDriverViewEnabled")
|
||||
@@ -46,6 +47,9 @@ def not_long_maneuver(started: bool, params: Params, CP: car.CarParams) -> bool:
|
||||
def qcomgps(started: bool, params: Params, CP: car.CarParams) -> bool:
|
||||
return started and not ublox_available()
|
||||
|
||||
def beep(started: bool, params: Params, CP: car.CarParams) -> bool:
|
||||
return started and params.get_bool("dp_dev_beep")
|
||||
|
||||
def always_run(started: bool, params: Params, CP: car.CarParams) -> bool:
|
||||
return True
|
||||
|
||||
@@ -55,6 +59,12 @@ def only_onroad(started: bool, params: Params, CP: car.CarParams) -> bool:
|
||||
def only_offroad(started: bool, params: Params, CP: car.CarParams) -> bool:
|
||||
return not started
|
||||
|
||||
def dashy(started: bool, params: Params, CP: car.CarParams) -> bool:
|
||||
return params.get_bool("dp_dev_dashy")
|
||||
|
||||
def comma_connect(started: bool, params: Params, CP: car.CarParams) -> bool:
|
||||
return not params.get_bool("dp_dev_disable_connect")
|
||||
|
||||
def or_(*fns):
|
||||
return lambda *args: operator.or_(*(fn(*args) for fn in fns))
|
||||
|
||||
@@ -66,14 +76,14 @@ procs = [
|
||||
|
||||
NativeProcess("loggerd", "system/loggerd", ["./loggerd"], logging),
|
||||
NativeProcess("encoderd", "system/loggerd", ["./encoderd"], only_onroad),
|
||||
NativeProcess("stream_encoderd", "system/loggerd", ["./encoderd", "--stream"], notcar),
|
||||
NativeProcess("stream_encoderd", "system/loggerd", ["./encoderd", "--stream"], or_(notcar, and_(dashy, only_onroad))),
|
||||
PythonProcess("logmessaged", "system.logmessaged", always_run),
|
||||
|
||||
NativeProcess("camerad", "system/camerad", ["./camerad"], driverview, enabled=not WEBCAM),
|
||||
PythonProcess("webcamerad", "tools.webcam.camerad", driverview, enabled=WEBCAM),
|
||||
PythonProcess("proclogd", "system.proclogd", only_onroad, enabled=platform.system() != "Darwin"),
|
||||
PythonProcess("journald", "system.journald", only_onroad, platform.system() != "Darwin"),
|
||||
PythonProcess("micd", "system.micd", iscar),
|
||||
PythonProcess("micd", "system.micd", iscar, enabled=not LITE),
|
||||
PythonProcess("timed", "system.timed", always_run, enabled=not PC),
|
||||
|
||||
PythonProcess("modeld", "selfdrive.modeld.modeld", only_onroad),
|
||||
@@ -81,7 +91,8 @@ procs = [
|
||||
|
||||
PythonProcess("sensord", "system.sensord.sensord", only_onroad, enabled=not PC),
|
||||
PythonProcess("ui", "selfdrive.ui.ui", always_run, restart_if_crash=True),
|
||||
PythonProcess("soundd", "selfdrive.ui.soundd", driverview),
|
||||
PythonProcess("soundd", "selfdrive.ui.soundd", driverview, enabled=not LITE),
|
||||
PythonProcess("beepd", "dragonpilot.selfdrive.ui.beepd", beep, enabled=TICI and LITE),
|
||||
PythonProcess("locationd", "selfdrive.locationd.locationd", only_onroad),
|
||||
NativeProcess("_pandad", "selfdrive/pandad", ["./pandad"], always_run, enabled=False),
|
||||
PythonProcess("calibrationd", "selfdrive.locationd.calibrationd", only_onroad),
|
||||
@@ -104,15 +115,17 @@ procs = [
|
||||
PythonProcess("hardwared", "system.hardware.hardwared", always_run),
|
||||
PythonProcess("tombstoned", "system.tombstoned", always_run, enabled=not PC),
|
||||
PythonProcess("updated", "system.updated.updated", only_offroad, enabled=not PC),
|
||||
PythonProcess("uploader", "system.loggerd.uploader", always_run),
|
||||
PythonProcess("uploader", "system.loggerd.uploader", comma_connect and always_run),
|
||||
PythonProcess("statsd", "system.statsd", always_run),
|
||||
PythonProcess("feedbackd", "selfdrive.ui.feedback.feedbackd", only_onroad),
|
||||
|
||||
# debug procs
|
||||
NativeProcess("bridge", "cereal/messaging", ["./bridge"], notcar),
|
||||
PythonProcess("webrtcd", "system.webrtc.webrtcd", notcar),
|
||||
NativeProcess("bridge", "cereal/messaging", ["./bridge"], or_(notcar, and_(dashy, only_onroad))),
|
||||
PythonProcess("webrtcd", "system.webrtc.webrtcd", or_(notcar, and_(dashy, only_onroad))),
|
||||
PythonProcess("webjoystick", "tools.bodyteleop.web", notcar),
|
||||
PythonProcess("joystick", "tools.joystick.joystick_control", and_(joystick, iscar)),
|
||||
PythonProcess("dashy", "dragonpilot.dashy.backend.server", always_run),
|
||||
PythonProcess("gpsd", "dragonpilot.selfdrive.gpsd.gpsd", and_(dashy, only_onroad)),
|
||||
]
|
||||
|
||||
managed_processes = {p.name: p for p in procs}
|
||||
|
||||
169
system/manager/vehicle_model_collector.py
Executable file
169
system/manager/vehicle_model_collector.py
Executable file
@@ -0,0 +1,169 @@
|
||||
"""
|
||||
Copyright (c) 2025 Rick Lan
|
||||
|
||||
This software is licensed under the Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International License (CC BY-NC-SA 4.0).
|
||||
You are free to share and adapt this work for non-commercial purposes, provided you give appropriate credit and distribute any modifications under the same license.
|
||||
|
||||
To view a copy of this license, visit:
|
||||
http://creativecommons.org/licenses/by-nc-sa/4.0/
|
||||
|
||||
---
|
||||
|
||||
**Commercial Licensing:**
|
||||
Use of this software for commercial purposes is strictly prohibited without a separate, paid license.
|
||||
To purchase a commercial license, please contact ricklan@gmail.com.
|
||||
"""
|
||||
|
||||
import os
|
||||
import importlib
|
||||
import json
|
||||
from openpilot.common.basedir import BASEDIR
|
||||
# from openpilot.common.params import Params
|
||||
|
||||
|
||||
class VehicleModelCollector:
|
||||
def __init__(self):
|
||||
self.base_package = "opendbc.car"
|
||||
self.base_path = f"{BASEDIR}/opendbc/car"
|
||||
self.exclude_brands = ['body', 'mock']
|
||||
|
||||
# Define the lookup dictionary for brand-to-group mappings
|
||||
self.brand_to_group_map = {
|
||||
"chrysler": [
|
||||
{"prefix": "DODGE_", "group": "Chrysler"},
|
||||
{"prefix": "RAM_", "group": "Chrysler"},
|
||||
{"prefix": "JEEP_", "group": "Chrysler"},
|
||||
],
|
||||
"gm": [
|
||||
{"prefix": "BUICK_", "group": "GM"},
|
||||
{"prefix": "CADILLAC_", "group": "GM"},
|
||||
{"prefix": "CHEVROLET_", "group": "GM"},
|
||||
{"prefix": "HOLDEN_", "group": "GM"},
|
||||
],
|
||||
"honda": {"prefix": "ACURA_", "group": "Honda"},
|
||||
"toyota": {"prefix": "LEXUS_", "group": "Toyota"},
|
||||
"hyundai": [
|
||||
{"prefix": "KIA_", "group": "Hyundai"},
|
||||
{"prefix": "GENESIS_", "group": "Hyundai"}
|
||||
],
|
||||
"volkswagen": [
|
||||
{"prefix": "AUDI_", "group": "Volkswagen"},
|
||||
{"prefix": "SKODA_", "group": "Volkswagen"},
|
||||
{"prefix": "SEAT_", "group": "Volkswagen"}
|
||||
]
|
||||
}
|
||||
|
||||
# Define exceptions for group names
|
||||
self.group_name_exceptions = {
|
||||
"gm": "GM",
|
||||
}
|
||||
|
||||
@staticmethod
|
||||
def is_car_model(car_class, attr):
|
||||
"""Check if the attribute is a car model (not callable and not a dunder attribute)"""
|
||||
return not callable(getattr(car_class, attr)) and not attr.startswith("__")
|
||||
|
||||
@staticmethod
|
||||
def move_to_proper_group(models, prefix):
|
||||
"""
|
||||
Moves models with a certain prefix to their respective group.
|
||||
Example: Models starting with 'LEXUS_' should go to 'Lexus' group.
|
||||
"""
|
||||
moved_models = []
|
||||
for model in models[:]: # Iterate over a copy to avoid modifying during iteration
|
||||
if model.startswith(prefix):
|
||||
moved_models.append(model)
|
||||
models.remove(model) # Remove from the original group
|
||||
return moved_models
|
||||
|
||||
def format_group_name(self, group_name):
|
||||
"""
|
||||
Formats group names according to the exceptions dictionary.
|
||||
Groups in the exceptions dictionary are returned in all caps, others are title cased.
|
||||
"""
|
||||
return self.group_name_exceptions.get(group_name, group_name.title())
|
||||
|
||||
def collect_models(self):
|
||||
"""Collect all car models and organize them by brand/group"""
|
||||
# List all subdirectories (car brands)
|
||||
car_brands = sorted([
|
||||
name for name in os.listdir(self.base_path)
|
||||
if os.path.isdir(os.path.join(self.base_path, name)) and not name.startswith("__")
|
||||
])
|
||||
|
||||
grouped_models = {}
|
||||
|
||||
# Import CAR from each subdirectory and group models by brand
|
||||
for brand in car_brands:
|
||||
if brand in self.exclude_brands:
|
||||
continue
|
||||
|
||||
module_name = f"{self.base_package}.{brand}.values"
|
||||
try:
|
||||
module = importlib.import_module(module_name)
|
||||
if hasattr(module, "CAR"):
|
||||
car_class = getattr(module, "CAR")
|
||||
models = sorted([attr for attr in dir(car_class) if self.is_car_model(car_class, attr)])
|
||||
|
||||
# Check if the brand has a special group in the lookup map
|
||||
if brand in self.brand_to_group_map:
|
||||
group_info = self.brand_to_group_map[brand]
|
||||
|
||||
if isinstance(group_info, list): # If multiple prefixes for the brand
|
||||
for prefix_info in group_info:
|
||||
moved_models = self.move_to_proper_group(models, prefix_info["prefix"])
|
||||
if moved_models:
|
||||
if prefix_info["group"] not in grouped_models:
|
||||
grouped_models[prefix_info["group"]] = []
|
||||
grouped_models[prefix_info["group"]].extend(moved_models)
|
||||
else: # Single prefix for the brand
|
||||
moved_models = self.move_to_proper_group(models, group_info["prefix"])
|
||||
if moved_models:
|
||||
if group_info["group"] not in grouped_models:
|
||||
grouped_models[group_info["group"]] = []
|
||||
grouped_models[group_info["group"]].extend(moved_models)
|
||||
|
||||
# Add remaining models to the respective brand
|
||||
if models:
|
||||
grouped_models[brand] = models
|
||||
except ModuleNotFoundError:
|
||||
pass
|
||||
|
||||
# Merge groups that have the same formatted name (e.g., "chrysler" and "Chrysler")
|
||||
merged_grouped_models = {}
|
||||
for group_key, models_list in grouped_models.items():
|
||||
formatted_group_name = self.format_group_name(group_key)
|
||||
if formatted_group_name not in merged_grouped_models:
|
||||
merged_grouped_models[formatted_group_name] = []
|
||||
merged_grouped_models[formatted_group_name].extend(models_list)
|
||||
|
||||
# Create a new dictionary to hold the sorted models
|
||||
sorted_models = {}
|
||||
for group_key, models_list in merged_grouped_models.items():
|
||||
sorted_models[group_key] = sorted(models_list)
|
||||
|
||||
return sorted_models
|
||||
|
||||
# def save_to_params(self, output=None):
|
||||
# """Save the collected model list to Params"""
|
||||
# if output is None:
|
||||
# output = self.collect_models()
|
||||
# Params().put("dp_dev_model_list", json.dumps(output))
|
||||
# return output
|
||||
#
|
||||
# def run(self):
|
||||
# """Collect models and save to params"""
|
||||
# models = self.collect_models()
|
||||
# self.save_to_params(models)
|
||||
# return models
|
||||
|
||||
def get_json(self):
|
||||
return self.collect_models()
|
||||
|
||||
def get(self):
|
||||
return json.dumps(self.collect_models())
|
||||
|
||||
# Allow running as a script
|
||||
if __name__ == "__main__":
|
||||
collector = VehicleModelCollector()
|
||||
print(collector.get())
|
||||
@@ -266,7 +266,8 @@ def init(pigeon: TTYPigeon) -> None:
|
||||
set_power(False)
|
||||
time.sleep(0.1)
|
||||
set_power(True)
|
||||
time.sleep(0.5)
|
||||
# rick - make sleep twice long, give LITE more time.
|
||||
time.sleep(1.0)
|
||||
|
||||
init_baudrate(pigeon)
|
||||
init_pigeon(pigeon)
|
||||
|
||||
@@ -21,7 +21,12 @@ from openpilot.system.hardware import HARDWARE, PC
|
||||
from openpilot.system.ui.lib.multilang import multilang
|
||||
from openpilot.common.realtime import Ratekeeper
|
||||
|
||||
_DEFAULT_FPS = int(os.getenv("FPS", {'tizi': 20}.get(HARDWARE.get_device_type(), 60)))
|
||||
try:
|
||||
from openpilot.common.params import Params
|
||||
except ImportError:
|
||||
Params = None
|
||||
|
||||
_DEFAULT_FPS = int(os.getenv("FPS", {'tici': 20, 'tizi': 20}.get(HARDWARE.get_device_type(), 60)))
|
||||
FPS_LOG_INTERVAL = 5 # Seconds between logging FPS drops
|
||||
FPS_DROP_THRESHOLD = 0.9 # FPS drop threshold for triggering a warning
|
||||
FPS_CRITICAL_THRESHOLD = 0.5 # Critical threshold for triggering strict actions
|
||||
@@ -193,13 +198,17 @@ class MouseState:
|
||||
class GuiApplication:
|
||||
def __init__(self, width: int | None = None, height: int | None = None):
|
||||
self._fonts: dict[FontWeight, rl.Font] = {}
|
||||
self._width = width if width is not None else GuiApplication._default_width()
|
||||
self._height = height if height is not None else GuiApplication._default_height()
|
||||
if Params is not None:
|
||||
dp_ui_mici = Params().get_bool("dp_ui_mici")
|
||||
else:
|
||||
dp_ui_mici = False
|
||||
self._width = width if width is not None else GuiApplication._default_width(dp_ui_mici)
|
||||
self._height = height if height is not None else GuiApplication._default_height(dp_ui_mici)
|
||||
|
||||
if PC and os.getenv("SCALE") is None:
|
||||
self._scale = self._calculate_auto_scale()
|
||||
else:
|
||||
self._scale = SCALE
|
||||
self._scale = 4.0 if dp_ui_mici else SCALE
|
||||
|
||||
# Scale, then ensure dimensions are even
|
||||
self._scaled_width = int(self._width * self._scale)
|
||||
@@ -725,12 +734,13 @@ class GuiApplication:
|
||||
return max(0.3, min(w / self._width, h / self._height) * 0.95)
|
||||
|
||||
@staticmethod
|
||||
def _default_width() -> int:
|
||||
return 2160 if GuiApplication.big_ui() else 536
|
||||
def _default_width(dp_ui_four: bool = False) -> int:
|
||||
return 536 if dp_ui_four else 2160 if GuiApplication.big_ui() else 536
|
||||
|
||||
@staticmethod
|
||||
def _default_height() -> int:
|
||||
return 1080 if GuiApplication.big_ui() else 240
|
||||
def _default_height(dp_ui_four: bool = False) -> int:
|
||||
return 240 if dp_ui_four else 1080 if GuiApplication.big_ui() else 240
|
||||
|
||||
|
||||
@staticmethod
|
||||
def big_ui() -> bool:
|
||||
|
||||
@@ -161,6 +161,13 @@ class AdvancedNetworkSettings(Widget):
|
||||
metered = self._params.get_bool("GsmMetered")
|
||||
self._wifi_manager.update_gsm_settings(roaming_enabled, self._params.get("GsmApn") or "", metered)
|
||||
|
||||
# dp - retain tethering after reboot
|
||||
# same logic as _toggle_tethering()
|
||||
if self._params.get_bool("dp_dev_tethering"):
|
||||
self._tethering_action.set_enabled(False)
|
||||
self._wifi_metered_action.set_enabled(False)
|
||||
self._wifi_manager.set_tethering_active(True)
|
||||
|
||||
def _on_network_updated(self, networks: list[Network]):
|
||||
self._tethering_action.set_enabled(True)
|
||||
self._tethering_action.set_state(self._wifi_manager.is_tethering_active())
|
||||
@@ -176,6 +183,7 @@ class AdvancedNetworkSettings(Widget):
|
||||
|
||||
def _toggle_tethering(self):
|
||||
checked = self._tethering_action.get_state()
|
||||
self._params.put_bool_nonblocking("dp_dev_tethering", checked)
|
||||
self._tethering_action.set_enabled(False)
|
||||
if checked:
|
||||
self._wifi_metered_action.set_enabled(False)
|
||||
|
||||
@@ -26,6 +26,9 @@ class CerealOutgoingMessageProxy:
|
||||
def __init__(self, sm: messaging.SubMaster):
|
||||
self.sm = sm
|
||||
self.channels: list[RTCDataChannel] = []
|
||||
self.serializer = {
|
||||
'carParams': self._bytes_to_hex,
|
||||
}
|
||||
|
||||
def add_channel(self, channel: 'RTCDataChannel'):
|
||||
self.channels.append(channel)
|
||||
@@ -42,6 +45,12 @@ class CerealOutgoingMessageProxy:
|
||||
|
||||
return msg_dict
|
||||
|
||||
def _bytes_to_hex(self, obj):
|
||||
"""Convert bytes/bytearray to hex for JSON serialization."""
|
||||
if isinstance(obj, (bytes, bytearray)):
|
||||
return obj.hex()
|
||||
raise TypeError(f"Object of type {type(obj)} is not JSON serializable")
|
||||
|
||||
def update(self):
|
||||
# this is blocking in async context...
|
||||
self.sm.update(0)
|
||||
@@ -51,7 +60,8 @@ class CerealOutgoingMessageProxy:
|
||||
msg_dict = self.to_json(self.sm[service])
|
||||
mono_time, valid = self.sm.logMonoTime[service], self.sm.valid[service]
|
||||
outgoing_msg = {"type": service, "logMonoTime": mono_time, "valid": valid, "data": msg_dict}
|
||||
encoded_msg = json.dumps(outgoing_msg).encode()
|
||||
serializer = self.serializer.get(service)
|
||||
encoded_msg = json.dumps(outgoing_msg, default=serializer).encode()
|
||||
for channel in self.channels:
|
||||
channel.send(encoded_msg)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user