commit
c80c752cc0
132 changed files with 2983 additions and 1778 deletions
@ -1 +1 @@ |
||||
Subproject commit e29625c30bb2a4a34ce21134d0e5a91b2d4fa1b1 |
||||
Subproject commit d81d86e7cd83d1eb40314964a4d194231381d557 |
@ -1,45 +0,0 @@ |
||||
import time |
||||
|
||||
class Profiler(): |
||||
def __init__(self, enabled=False): |
||||
self.enabled = enabled |
||||
self.cp = {} |
||||
self.cp_ignored = [] |
||||
self.iter = 0 |
||||
self.start_time = time.time() |
||||
self.last_time = self.start_time |
||||
self.tot = 0. |
||||
|
||||
def reset(self, enabled=False): |
||||
self.enabled = enabled |
||||
self.cp = {} |
||||
self.cp_ignored = [] |
||||
self.iter = 0 |
||||
self.start_time = time.time() |
||||
self.last_time = self.start_time |
||||
|
||||
def checkpoint(self, name, ignore=False): |
||||
# ignore flag needed when benchmarking threads with ratekeeper |
||||
if not self.enabled: |
||||
return |
||||
tt = time.time() |
||||
if name not in self.cp: |
||||
self.cp[name] = 0. |
||||
if ignore: |
||||
self.cp_ignored.append(name) |
||||
self.cp[name] += tt - self.last_time |
||||
if not ignore: |
||||
self.tot += tt - self.last_time |
||||
self.last_time = tt |
||||
|
||||
def display(self): |
||||
if not self.enabled: |
||||
return |
||||
self.iter += 1 |
||||
print("******* Profiling %d *******" % self.iter) |
||||
for n, ms in sorted(self.cp.items(), key=lambda x: -x[1]): |
||||
if n in self.cp_ignored: |
||||
print("%30s: %9.2f avg: %7.2f percent: %3.0f IGNORED" % (n, ms*1000.0, ms*1000.0/self.iter, ms/self.tot*100)) |
||||
else: |
||||
print("%30s: %9.2f avg: %7.2f percent: %3.0f" % (n, ms*1000.0, ms*1000.0/self.iter, ms/self.tot*100)) |
||||
print(f"Iter clock: {self.tot / self.iter:2.6f} TOTAL: {self.tot:2.2f}") |
@ -1 +1 @@ |
||||
Subproject commit 20722a59467fc7d697bfe1edd233a38bafbc89d2 |
||||
Subproject commit 2a0536c63148a02add52555386b5533f3555ef58 |
File diff suppressed because it is too large
Load Diff
@ -1,111 +0,0 @@ |
||||
#!/usr/bin/env python3 |
||||
''' |
||||
printing the gap between interrupts in a histogram to check if the |
||||
frequency is what we expect, the bmx is not interrupt driven for as we |
||||
get interrupts in a 2kHz rate. |
||||
''' |
||||
|
||||
import argparse |
||||
import sys |
||||
import numpy as np |
||||
from collections import defaultdict |
||||
|
||||
from openpilot.tools.lib.logreader import LogReader |
||||
from openpilot.tools.lib.route import Route |
||||
|
||||
import matplotlib.pyplot as plt |
||||
|
||||
SRC_BMX = "bmx055" |
||||
SRC_LSM = "lsm6ds3" |
||||
|
||||
|
||||
def parseEvents(log_reader): |
||||
bmx_data = defaultdict(list) |
||||
lsm_data = defaultdict(list) |
||||
|
||||
for m in log_reader: |
||||
if m.which() not in ['accelerometer', 'gyroscope']: |
||||
continue |
||||
|
||||
d = getattr(m, m.which()).to_dict() |
||||
|
||||
if d["source"] == SRC_BMX and "acceleration" in d: |
||||
bmx_data["accel"].append(d["timestamp"] / 1e9) |
||||
|
||||
if d["source"] == SRC_BMX and "gyroUncalibrated" in d: |
||||
bmx_data["gyro"].append(d["timestamp"] / 1e9) |
||||
|
||||
if d["source"] == SRC_LSM and "acceleration" in d: |
||||
lsm_data["accel"].append(d["timestamp"] / 1e9) |
||||
|
||||
if d["source"] == SRC_LSM and "gyroUncalibrated" in d: |
||||
lsm_data["gyro"].append(d["timestamp"] / 1e9) |
||||
|
||||
return bmx_data, lsm_data |
||||
|
||||
|
||||
def cleanData(data): |
||||
if len(data) == 0: |
||||
return [], [] |
||||
|
||||
data.sort() |
||||
diffs = np.diff(data) |
||||
return data, diffs |
||||
|
||||
|
||||
def logAvgValues(data, sensor): |
||||
if len(data) == 0: |
||||
print(f"{sensor}: no data to average") |
||||
return |
||||
|
||||
avg = sum(data) / len(data) |
||||
hz = 1 / avg |
||||
print(f"{sensor}: data_points: {len(data)} avg [ns]: {avg} avg [Hz]: {hz}") |
||||
|
||||
|
||||
if __name__ == "__main__": |
||||
parser = argparse.ArgumentParser() |
||||
parser.add_argument("route", type=str, help="route name") |
||||
parser.add_argument("segment", type=int, help="segment number") |
||||
args = parser.parse_args() |
||||
|
||||
r = Route(args.route) |
||||
logs = r.log_paths() |
||||
|
||||
if len(logs) == 0: |
||||
print("NO data routes") |
||||
sys.exit(0) |
||||
|
||||
if args.segment >= len(logs): |
||||
print(f"RouteID: {args.segment} out of range, max: {len(logs) -1}") |
||||
sys.exit(0) |
||||
|
||||
lr = LogReader(logs[args.segment]) |
||||
bmx_data, lsm_data = parseEvents(lr) |
||||
|
||||
# sort bmx accel data, and then cal all the diffs, and to a histogram of those |
||||
bmx_accel, bmx_accel_diffs = cleanData(bmx_data["accel"]) |
||||
bmx_gyro, bmx_gyro_diffs = cleanData(bmx_data["gyro"]) |
||||
lsm_accel, lsm_accel_diffs = cleanData(lsm_data["accel"]) |
||||
lsm_gyro, lsm_gyro_diffs = cleanData(lsm_data["gyro"]) |
||||
|
||||
# get out the averages |
||||
logAvgValues(bmx_accel_diffs, "bmx accel") |
||||
logAvgValues(bmx_gyro_diffs, "bmx gyro ") |
||||
logAvgValues(lsm_accel_diffs, "lsm accel") |
||||
logAvgValues(lsm_gyro_diffs, "lsm gyro ") |
||||
|
||||
fig, axs = plt.subplots(1, 2, tight_layout=True) |
||||
axs[0].hist(bmx_accel_diffs, bins=50) |
||||
axs[0].set_title("bmx_accel") |
||||
axs[1].hist(bmx_gyro_diffs, bins=50) |
||||
axs[1].set_title("bmx_gyro") |
||||
|
||||
figl, axsl = plt.subplots(1, 2, tight_layout=True) |
||||
axsl[0].hist(lsm_accel_diffs, bins=50) |
||||
axsl[0].set_title("lsm_accel") |
||||
axsl[1].hist(lsm_gyro_diffs, bins=50) |
||||
axsl[1].set_title("lsm_gyro") |
||||
|
||||
print("check plot...") |
||||
plt.show() |
@ -0,0 +1,61 @@ |
||||
#!/usr/bin/env python3 |
||||
import json |
||||
import random |
||||
import unittest |
||||
import numpy as np |
||||
|
||||
import cereal.messaging as messaging |
||||
from openpilot.common.params import Params |
||||
from openpilot.selfdrive.manager.process_config import managed_processes |
||||
|
||||
|
||||
class TestNavd(unittest.TestCase): |
||||
def setUp(self): |
||||
self.params = Params() |
||||
self.sm = messaging.SubMaster(['navRoute', 'navInstruction']) |
||||
|
||||
def tearDown(self): |
||||
managed_processes['navd'].stop() |
||||
|
||||
def _check_route(self, start, end, check_coords=True): |
||||
self.params.put("NavDestination", json.dumps(end)) |
||||
self.params.put("LastGPSPosition", json.dumps(start)) |
||||
|
||||
managed_processes['navd'].start() |
||||
for _ in range(30): |
||||
self.sm.update(1000) |
||||
if all(f > 0 for f in self.sm.rcv_frame.values()): |
||||
break |
||||
else: |
||||
raise Exception("didn't get a route") |
||||
|
||||
assert managed_processes['navd'].proc.is_alive() |
||||
managed_processes['navd'].stop() |
||||
|
||||
# ensure start and end match up |
||||
if check_coords: |
||||
coords = self.sm['navRoute'].coordinates |
||||
assert np.allclose([start['latitude'], start['longitude'], end['latitude'], end['longitude']], |
||||
[coords[0].latitude, coords[0].longitude, coords[-1].latitude, coords[-1].longitude], |
||||
rtol=1e-3) |
||||
|
||||
def test_simple(self): |
||||
start = { |
||||
"latitude": 32.7427228, |
||||
"longitude": -117.2321177, |
||||
} |
||||
end = { |
||||
"latitude": 32.7557004, |
||||
"longitude": -117.268002, |
||||
} |
||||
self._check_route(start, end) |
||||
|
||||
def test_random(self): |
||||
for _ in range(10): |
||||
start = {"latitude": random.uniform(-90, 90), "longitude": random.uniform(-180, 180)} |
||||
end = {"latitude": random.uniform(-90, 90), "longitude": random.uniform(-180, 180)} |
||||
self._check_route(start, end, check_coords=False) |
||||
|
||||
|
||||
if __name__ == "__main__": |
||||
unittest.main() |
@ -1 +1 @@ |
||||
ea96f935a7a16c53623c3b03e70c0fbfa6b249e7 |
||||
1b981ce7f817974d4a7a28b06f01f727a5a7ea7b |
@ -0,0 +1,138 @@ |
||||
#!/usr/bin/env python3 |
||||
|
||||
import argparse |
||||
import json |
||||
import os |
||||
import pathlib |
||||
import xml.etree.ElementTree as ET |
||||
from typing import cast |
||||
|
||||
import requests |
||||
|
||||
TRANSLATIONS_DIR = pathlib.Path(__file__).resolve().parent |
||||
TRANSLATIONS_LANGUAGES = TRANSLATIONS_DIR / "languages.json" |
||||
|
||||
OPENAI_MODEL = "gpt-4" |
||||
OPENAI_API_KEY = os.environ.get("OPENAI_API_KEY") |
||||
OPENAI_PROMPT = "You are a professional translator from English to {language} (ISO 639 language code). " + \ |
||||
"The following sentence or word is in the GUI of a software called openpilot, translate it accordingly." |
||||
|
||||
|
||||
def get_language_files(languages: list[str] | None = None) -> dict[str, pathlib.Path]: |
||||
files = {} |
||||
|
||||
with open(TRANSLATIONS_LANGUAGES) as fp: |
||||
language_dict = json.load(fp) |
||||
|
||||
for filename in language_dict.values(): |
||||
path = TRANSLATIONS_DIR / f"{filename}.ts" |
||||
language = path.stem.split("main_")[1] |
||||
|
||||
if languages is None or language in languages: |
||||
files[language] = path |
||||
|
||||
return files |
||||
|
||||
|
||||
def translate_phrase(text: str, language: str) -> str: |
||||
response = requests.post( |
||||
"https://api.openai.com/v1/chat/completions", |
||||
json={ |
||||
"model": OPENAI_MODEL, |
||||
"messages": [ |
||||
{ |
||||
"role": "system", |
||||
"content": OPENAI_PROMPT.format(language=language), |
||||
}, |
||||
{ |
||||
"role": "user", |
||||
"content": text, |
||||
}, |
||||
], |
||||
"temperature": 0.8, |
||||
"max_tokens": 1024, |
||||
"top_p": 1, |
||||
}, |
||||
headers={ |
||||
"Authorization": f"Bearer {OPENAI_API_KEY}", |
||||
"Content-Type": "application/json", |
||||
}, |
||||
) |
||||
|
||||
if 400 <= response.status_code < 600: |
||||
raise requests.HTTPError(f'Error {response.status_code}: {response.json()}', response=response) |
||||
|
||||
data = response.json() |
||||
|
||||
return cast(str, data["choices"][0]["message"]["content"]) |
||||
|
||||
|
||||
def translate_file(path: pathlib.Path, language: str, all_: bool) -> None: |
||||
tree = ET.parse(path) |
||||
|
||||
root = tree.getroot() |
||||
|
||||
for context in root.findall("./context"): |
||||
name = context.find("name") |
||||
if name is None: |
||||
raise ValueError("name not found") |
||||
|
||||
print(f"Context: {name.text}") |
||||
|
||||
for message in context.findall("./message"): |
||||
source = message.find("source") |
||||
translation = message.find("translation") |
||||
|
||||
if source is None or translation is None: |
||||
raise ValueError("source or translation not found") |
||||
|
||||
if not all_ and translation.attrib.get("type") != "unfinished": |
||||
continue |
||||
|
||||
llm_translation = translate_phrase(cast(str, source.text), language) |
||||
|
||||
print(f"Source: {source.text}\n" + |
||||
f"Current translation: {translation.text}\n" + |
||||
f"LLM translation: {llm_translation}") |
||||
|
||||
translation.text = llm_translation |
||||
|
||||
with path.open("w", encoding="utf-8") as fp: |
||||
fp.write('<?xml version="1.0" encoding="utf-8"?>\n' + |
||||
'<!DOCTYPE TS>\n' + |
||||
ET.tostring(root, encoding="utf-8").decode()) |
||||
|
||||
|
||||
def main(): |
||||
arg_parser = argparse.ArgumentParser("Auto translate") |
||||
|
||||
group = arg_parser.add_mutually_exclusive_group(required=True) |
||||
group.add_argument("-a", "--all-files", action="store_true", help="Translate all files") |
||||
group.add_argument("-f", "--file", nargs="+", help="Translate the selected files. (Example: -f fr de)") |
||||
|
||||
arg_parser.add_argument("-t", "--all-translations", action="store_true", default=False, help="Translate all sections. (Default: only unfinished)") |
||||
|
||||
args = arg_parser.parse_args() |
||||
|
||||
if OPENAI_API_KEY is None: |
||||
print("OpenAI API key is missing. (Hint: use `export OPENAI_API_KEY=YOUR-KEY` before you run the script).\n" + |
||||
"If you don't have one go to: https://beta.openai.com/account/api-keys.") |
||||
exit(1) |
||||
|
||||
files = get_language_files(None if args.all_files else args.file) |
||||
|
||||
if args.file: |
||||
missing_files = set(args.file) - set(files) |
||||
if len(missing_files): |
||||
print(f"No language files found: {missing_files}") |
||||
exit(1) |
||||
|
||||
print(f"Translation mode: {'all' if args.all_translations else 'only unfinished'}. Files: {list(files)}") |
||||
|
||||
for lang, path in files.items(): |
||||
print(f"Translate {lang} ({path})") |
||||
translate_file(path, lang, args.all_translations) |
||||
|
||||
|
||||
if __name__ == "__main__": |
||||
main() |
@ -0,0 +1,105 @@ |
||||
#include "system/camerad/sensors/sensor.h" |
||||
|
||||
namespace { |
||||
|
||||
const float sensor_analog_gains_OS04C10[] = { |
||||
1.0, 1.0625, 1.125, 1.1875, 1.25, 1.3125, 1.375, 1.4375, 1.5, 1.5625, 1.6875, |
||||
1.8125, 1.9375, 2.0, 2.125, 2.25, 2.375, 2.5, 2.625, 2.75, 2.875, 3.0, |
||||
3.125, 3.375, 3.625, 3.875, 4.0, 4.25, 4.5, 4.75, 5.0, 5.25, 5.5, |
||||
5.75, 6.0, 6.25, 6.5, 7.0, 7.5, 8.0, 8.5, 9.0, 9.5, 10.0, |
||||
10.5, 11.0, 11.5, 12.0, 12.5, 13.0, 13.5, 14.0, 14.5, 15.0, 15.5}; |
||||
|
||||
const uint32_t os04c10_analog_gains_reg[] = { |
||||
0x100, 0x110, 0x120, 0x130, 0x140, 0x150, 0x160, 0x170, 0x180, 0x190, 0x1B0, |
||||
0x1D0, 0x1F0, 0x200, 0x220, 0x240, 0x260, 0x280, 0x2A0, 0x2C0, 0x2E0, 0x300, |
||||
0x320, 0x360, 0x3A0, 0x3E0, 0x400, 0x440, 0x480, 0x4C0, 0x500, 0x540, 0x580, |
||||
0x5C0, 0x600, 0x640, 0x680, 0x700, 0x780, 0x800, 0x880, 0x900, 0x980, 0xA00, |
||||
0xA80, 0xB00, 0xB80, 0xC00, 0xC80, 0xD00, 0xD80, 0xE00, 0xE80, 0xF00, 0xF80}; |
||||
|
||||
const uint32_t VS_TIME_MIN_OS04C10 = 1; |
||||
//const uint32_t VS_TIME_MAX_OS04C10 = 34; // vs < 35
|
||||
|
||||
} // namespace
|
||||
|
||||
OS04C10::OS04C10() { |
||||
image_sensor = cereal::FrameData::ImageSensor::OS04C10; |
||||
data_word = false; |
||||
|
||||
frame_width = 1920; |
||||
frame_height = 1080; |
||||
frame_stride = (1920*10/8); |
||||
|
||||
/*
|
||||
frame_width = 0xa80; |
||||
frame_height = 0x5f0; |
||||
frame_stride = 0xd20; |
||||
*/ |
||||
|
||||
extra_height = 0; |
||||
frame_offset = 0; |
||||
|
||||
start_reg_array.assign(std::begin(start_reg_array_os04c10), std::end(start_reg_array_os04c10)); |
||||
init_reg_array.assign(std::begin(init_array_os04c10), std::end(init_array_os04c10)); |
||||
probe_reg_addr = 0x300a; |
||||
probe_expected_data = 0x5304; |
||||
mipi_format = CAM_FORMAT_MIPI_RAW_10; |
||||
frame_data_type = 0x2b; |
||||
mclk_frequency = 24000000; // Hz
|
||||
|
||||
dc_gain_factor = 7.32; |
||||
dc_gain_min_weight = 1; // always on is fine
|
||||
dc_gain_max_weight = 1; |
||||
dc_gain_on_grey = 0.9; |
||||
dc_gain_off_grey = 1.0; |
||||
exposure_time_min = 2; // 1x
|
||||
exposure_time_max = 2016; |
||||
analog_gain_min_idx = 0x0; |
||||
analog_gain_rec_idx = 0x0; // 1x
|
||||
analog_gain_max_idx = 0x36; |
||||
analog_gain_cost_delta = -1; |
||||
analog_gain_cost_low = 0.4; |
||||
analog_gain_cost_high = 6.4; |
||||
for (int i = 0; i <= analog_gain_max_idx; i++) { |
||||
sensor_analog_gains[i] = sensor_analog_gains_OS04C10[i]; |
||||
} |
||||
min_ev = (exposure_time_min + VS_TIME_MIN_OS04C10) * 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 = 0.01; |
||||
} |
||||
|
||||
std::vector<i2c_random_wr_payload> OS04C10::getExposureRegisters(int exposure_time, int new_exp_g, bool dc_gain_enabled) const { |
||||
// t_HCG&t_LCG + t_VS on LPD, t_SPD on SPD
|
||||
uint32_t hcg_time = exposure_time; |
||||
//uint32_t lcg_time = hcg_time;
|
||||
//uint32_t spd_time = std::min(std::max((uint32_t)exposure_time, (exposure_time_max + VS_TIME_MAX_OS04C10) / 3), exposure_time_max + VS_TIME_MAX_OS04C10);
|
||||
//uint32_t vs_time = std::min(std::max((uint32_t)exposure_time / 40, VS_TIME_MIN_OS04C10), VS_TIME_MAX_OS04C10);
|
||||
|
||||
uint32_t real_gain = os04c10_analog_gains_reg[new_exp_g]; |
||||
|
||||
hcg_time = 100; |
||||
real_gain = 0x320; |
||||
|
||||
return { |
||||
{0x3501, hcg_time>>8}, {0x3502, hcg_time&0xFF}, |
||||
//{0x3581, lcg_time>>8}, {0x3582, lcg_time&0xFF},
|
||||
//{0x3541, spd_time>>8}, {0x3542, spd_time&0xFF},
|
||||
//{0x35c2, vs_time&0xFF},
|
||||
|
||||
{0x3508, real_gain>>8}, {0x3509, real_gain&0xFF}, |
||||
}; |
||||
} |
||||
|
||||
int OS04C10::getSlaveAddress(int port) const { |
||||
assert(port >= 0 && port <= 2); |
||||
return (int[]){0x6C, 0x20, 0x6C}[port]; |
||||
} |
||||
|
||||
float OS04C10::getExposureScore(float desired_ev, int exp_t, int exp_g_idx, float exp_gain, int gain_idx) const { |
||||
float score = std::abs(desired_ev - (exp_t * exp_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; |
||||
score += ((1 - analog_gain_cost_delta) + |
||||
analog_gain_cost_delta * (exp_g_idx - analog_gain_min_idx) / (analog_gain_max_idx - analog_gain_min_idx)) * |
||||
std::abs(exp_g_idx - gain_idx) * 5.0; |
||||
return score; |
||||
} |
@ -0,0 +1,298 @@ |
||||
#pragma once |
||||
|
||||
const struct i2c_random_wr_payload start_reg_array_os04c10[] = {{0x100, 1}}; |
||||
const struct i2c_random_wr_payload stop_reg_array_os04c10[] = {{0x100, 0}}; |
||||
|
||||
const struct i2c_random_wr_payload init_array_os04c10[] = { |
||||
// OS04C10_AA_00_02_17_wAO_1920x1080_MIPI728Mbps_Linear12bit_20FPS_4Lane_MCLK24MHz
|
||||
{0x0103, 0x01}, |
||||
{0x0301, 0x84}, |
||||
{0x0303, 0x01}, |
||||
{0x0305, 0x5b}, |
||||
{0x0306, 0x01}, |
||||
{0x0307, 0x17}, |
||||
{0x0323, 0x04}, |
||||
{0x0324, 0x01}, |
||||
{0x0325, 0x62}, |
||||
{0x3012, 0x06}, |
||||
{0x3013, 0x02}, |
||||
{0x3016, 0x72}, |
||||
{0x3021, 0x03}, |
||||
{0x3106, 0x21}, |
||||
{0x3107, 0xa1}, |
||||
{0x3500, 0x00}, |
||||
{0x3501, 0x00}, |
||||
{0x3502, 0x40}, |
||||
{0x3503, 0x88}, |
||||
{0x3508, 0x07}, |
||||
{0x3509, 0xc0}, |
||||
{0x350a, 0x04}, |
||||
{0x350b, 0x00}, |
||||
{0x350c, 0x07}, |
||||
{0x350d, 0xc0}, |
||||
{0x350e, 0x04}, |
||||
{0x350f, 0x00}, |
||||
{0x3510, 0x00}, |
||||
{0x3511, 0x00}, |
||||
{0x3512, 0x20}, |
||||
{0x3624, 0x00}, |
||||
{0x3625, 0x4c}, |
||||
{0x3660, 0x00}, |
||||
{0x3666, 0xa5}, |
||||
{0x3667, 0xa5}, |
||||
{0x366a, 0x64}, |
||||
{0x3673, 0x0d}, |
||||
{0x3672, 0x0d}, |
||||
{0x3671, 0x0d}, |
||||
{0x3670, 0x0d}, |
||||
{0x3685, 0x00}, |
||||
{0x3694, 0x0d}, |
||||
{0x3693, 0x0d}, |
||||
{0x3692, 0x0d}, |
||||
{0x3691, 0x0d}, |
||||
{0x3696, 0x4c}, |
||||
{0x3697, 0x4c}, |
||||
{0x3698, 0x40}, |
||||
{0x3699, 0x80}, |
||||
{0x369a, 0x18}, |
||||
{0x369b, 0x1f}, |
||||
{0x369c, 0x14}, |
||||
{0x369d, 0x80}, |
||||
{0x369e, 0x40}, |
||||
{0x369f, 0x21}, |
||||
{0x36a0, 0x12}, |
||||
{0x36a1, 0x5d}, |
||||
{0x36a2, 0x66}, |
||||
{0x370a, 0x00}, |
||||
{0x370e, 0x0c}, |
||||
{0x3710, 0x00}, |
||||
{0x3713, 0x00}, |
||||
{0x3725, 0x02}, |
||||
{0x372a, 0x03}, |
||||
{0x3738, 0xce}, |
||||
{0x3748, 0x00}, |
||||
{0x374a, 0x00}, |
||||
{0x374c, 0x00}, |
||||
{0x374e, 0x00}, |
||||
{0x3756, 0x00}, |
||||
{0x3757, 0x0e}, |
||||
{0x3767, 0x00}, |
||||
{0x3771, 0x00}, |
||||
{0x377b, 0x20}, |
||||
{0x377c, 0x00}, |
||||
{0x377d, 0x0c}, |
||||
{0x3781, 0x03}, |
||||
{0x3782, 0x00}, |
||||
{0x3789, 0x14}, |
||||
{0x3795, 0x02}, |
||||
{0x379c, 0x00}, |
||||
{0x379d, 0x00}, |
||||
{0x37b8, 0x04}, |
||||
{0x37ba, 0x03}, |
||||
{0x37bb, 0x00}, |
||||
{0x37bc, 0x04}, |
||||
{0x37be, 0x08}, |
||||
{0x37c4, 0x11}, |
||||
{0x37c5, 0x80}, |
||||
{0x37c6, 0x14}, |
||||
{0x37c7, 0x08}, |
||||
{0x37da, 0x11}, |
||||
{0x381f, 0x08}, |
||||
{0x3829, 0x03}, |
||||
{0x3881, 0x00}, |
||||
{0x3888, 0x04}, |
||||
{0x388b, 0x00}, |
||||
{0x3c80, 0x10}, |
||||
{0x3c86, 0x00}, |
||||
{0x3c8c, 0x20}, |
||||
{0x3c9f, 0x01}, |
||||
{0x3d85, 0x1b}, |
||||
{0x3d8c, 0x71}, |
||||
{0x3d8d, 0xe2}, |
||||
{0x3f00, 0x0b}, |
||||
{0x3f06, 0x04}, |
||||
{0x400a, 0x01}, |
||||
{0x400b, 0x50}, |
||||
{0x400e, 0x08}, |
||||
{0x4043, 0x7e}, |
||||
{0x4045, 0x7e}, |
||||
{0x4047, 0x7e}, |
||||
{0x4049, 0x7e}, |
||||
{0x4090, 0x14}, |
||||
{0x40b0, 0x00}, |
||||
{0x40b1, 0x00}, |
||||
{0x40b2, 0x00}, |
||||
{0x40b3, 0x00}, |
||||
{0x40b4, 0x00}, |
||||
{0x40b5, 0x00}, |
||||
{0x40b7, 0x00}, |
||||
{0x40b8, 0x00}, |
||||
{0x40b9, 0x00}, |
||||
{0x40ba, 0x00}, |
||||
{0x4301, 0x00}, |
||||
{0x4303, 0x00}, |
||||
{0x4502, 0x04}, |
||||
{0x4503, 0x00}, |
||||
{0x4504, 0x06}, |
||||
{0x4506, 0x00}, |
||||
{0x4507, 0x64}, |
||||
{0x4803, 0x00}, |
||||
{0x480c, 0x32}, |
||||
{0x480e, 0x00}, |
||||
{0x4813, 0x00}, |
||||
{0x4819, 0x70}, |
||||
{0x481f, 0x30}, |
||||
{0x4823, 0x3f}, |
||||
{0x4825, 0x30}, |
||||
{0x4833, 0x10}, |
||||
{0x484b, 0x07}, |
||||
{0x488b, 0x00}, |
||||
{0x4d00, 0x04}, |
||||
{0x4d01, 0xad}, |
||||
{0x4d02, 0xbc}, |
||||
{0x4d03, 0xa1}, |
||||
{0x4d04, 0x1f}, |
||||
{0x4d05, 0x4c}, |
||||
{0x4d0b, 0x01}, |
||||
{0x4e00, 0x2a}, |
||||
{0x4e0d, 0x00}, |
||||
{0x5001, 0x09}, |
||||
{0x5004, 0x00}, |
||||
{0x5080, 0x04}, |
||||
{0x5036, 0x00}, |
||||
{0x5180, 0x70}, |
||||
{0x5181, 0x10}, |
||||
{0x520a, 0x03}, |
||||
{0x520b, 0x06}, |
||||
{0x520c, 0x0c}, |
||||
{0x580b, 0x0f}, |
||||
{0x580d, 0x00}, |
||||
{0x580f, 0x00}, |
||||
{0x5820, 0x00}, |
||||
{0x5821, 0x00}, |
||||
{0x301c, 0xf8}, |
||||
{0x301e, 0xb4}, |
||||
{0x301f, 0xd0}, |
||||
{0x3022, 0x01}, |
||||
{0x3109, 0xe7}, |
||||
{0x3600, 0x00}, |
||||
{0x3610, 0x65}, |
||||
{0x3611, 0x85}, |
||||
{0x3613, 0x3a}, |
||||
{0x3615, 0x60}, |
||||
{0x3621, 0x90}, |
||||
{0x3620, 0x0c}, |
||||
{0x3629, 0x00}, |
||||
{0x3661, 0x04}, |
||||
{0x3664, 0x70}, |
||||
{0x3665, 0x00}, |
||||
{0x3681, 0xa6}, |
||||
{0x3682, 0x53}, |
||||
{0x3683, 0x2a}, |
||||
{0x3684, 0x15}, |
||||
{0x3700, 0x2a}, |
||||
{0x3701, 0x12}, |
||||
{0x3703, 0x28}, |
||||
{0x3704, 0x0e}, |
||||
{0x3706, 0x4a}, |
||||
{0x3709, 0x4a}, |
||||
{0x370b, 0xa2}, |
||||
{0x370c, 0x01}, |
||||
{0x370f, 0x04}, |
||||
{0x3714, 0x24}, |
||||
{0x3716, 0x24}, |
||||
{0x3719, 0x11}, |
||||
{0x371a, 0x1e}, |
||||
{0x3720, 0x00}, |
||||
{0x3724, 0x13}, |
||||
{0x373f, 0xb0}, |
||||
{0x3741, 0x4a}, |
||||
{0x3743, 0x4a}, |
||||
{0x3745, 0x4a}, |
||||
{0x3747, 0x4a}, |
||||
{0x3749, 0xa2}, |
||||
{0x374b, 0xa2}, |
||||
{0x374d, 0xa2}, |
||||
{0x374f, 0xa2}, |
||||
{0x3755, 0x10}, |
||||
{0x376c, 0x00}, |
||||
{0x378d, 0x30}, |
||||
{0x3790, 0x4a}, |
||||
{0x3791, 0xa2}, |
||||
{0x3798, 0x40}, |
||||
{0x379e, 0x00}, |
||||
{0x379f, 0x04}, |
||||
{0x37a1, 0x10}, |
||||
{0x37a2, 0x1e}, |
||||
{0x37a8, 0x10}, |
||||
{0x37a9, 0x1e}, |
||||
{0x37ac, 0xa0}, |
||||
{0x37b9, 0x01}, |
||||
{0x37bd, 0x01}, |
||||
{0x37bf, 0x26}, |
||||
{0x37c0, 0x11}, |
||||
{0x37c2, 0x04}, |
||||
{0x37cd, 0x19}, |
||||
{0x37e0, 0x08}, |
||||
{0x37e6, 0x04}, |
||||
{0x37e5, 0x02}, |
||||
{0x37e1, 0x0c}, |
||||
{0x3737, 0x04}, |
||||
{0x37d8, 0x02}, |
||||
{0x37e2, 0x10}, |
||||
{0x3739, 0x10}, |
||||
{0x3662, 0x10}, |
||||
{0x37e4, 0x20}, |
||||
{0x37e3, 0x08}, |
||||
{0x37d9, 0x08}, |
||||
{0x4040, 0x00}, |
||||
{0x4041, 0x07}, |
||||
{0x4008, 0x02}, |
||||
{0x4009, 0x0d}, |
||||
{0x3800, 0x01}, |
||||
{0x3801, 0x80}, |
||||
{0x3802, 0x00}, |
||||
{0x3803, 0xdc}, |
||||
{0x3804, 0x09}, |
||||
{0x3805, 0x0f}, |
||||
{0x3806, 0x05}, |
||||
{0x3807, 0x23}, |
||||
{0x3808, 0x07}, |
||||
{0x3809, 0x80}, |
||||
{0x380a, 0x04}, |
||||
{0x380b, 0x38}, |
||||
{0x380c, 0x04}, |
||||
{0x380d, 0x2e}, |
||||
{0x380e, 0x12}, |
||||
{0x380f, 0x70}, |
||||
{0x3811, 0x08}, |
||||
{0x3813, 0x08}, |
||||
{0x3814, 0x01}, |
||||
{0x3815, 0x01}, |
||||
{0x3816, 0x01}, |
||||
{0x3817, 0x01}, |
||||
{0x3820, 0x88}, |
||||
{0x3821, 0x00}, |
||||
{0x3880, 0x25}, |
||||
{0x3882, 0x20}, |
||||
{0x3c91, 0x0b}, |
||||
{0x3c94, 0x45}, |
||||
{0x3cad, 0x00}, |
||||
{0x3cae, 0x00}, |
||||
{0x4000, 0xf3}, |
||||
{0x4001, 0x60}, |
||||
{0x4003, 0x40}, |
||||
{0x4300, 0xff}, |
||||
{0x4302, 0x0f}, |
||||
{0x4305, 0x83}, |
||||
{0x4505, 0x84}, |
||||
{0x4809, 0x1e}, |
||||
{0x480a, 0x04}, |
||||
{0x4837, 0x15}, |
||||
{0x4c00, 0x08}, |
||||
{0x4c01, 0x08}, |
||||
{0x4c04, 0x00}, |
||||
{0x4c05, 0x00}, |
||||
{0x5000, 0xf9}, |
||||
{0x3c8c, 0x10}, |
||||
}; |
@ -0,0 +1,74 @@ |
||||
# tools/car_porting |
||||
|
||||
Check out [this blog post](https://blog.comma.ai/how-to-write-a-car-port-for-openpilot/) for a high-level overview of porting a car. |
||||
|
||||
## Useful car porting utilities |
||||
|
||||
Testing car ports in your car is very time-consuming. Check out these utilities to do basic checks on your work before running it in your car. |
||||
|
||||
### [Cabana](/tools/cabana/README.md) |
||||
|
||||
View your car's CAN signals through DBC files, which openpilot uses to parse and create messages that talk to the car. |
||||
|
||||
Example: |
||||
```bash |
||||
> tools/cabana/cabana '1bbe6bf2d62f58a8|2022-07-14--17-11-43' |
||||
``` |
||||
|
||||
### [tools/car_porting/auto_fingerprint.py](/tools/car_porting/auto_fingerprint.py) |
||||
|
||||
Given a route and platform, automatically inserts FW fingerprints from the platform into the correct place in fingerprints.py |
||||
|
||||
Example: |
||||
```bash |
||||
> python tools/car_porting/auto_fingerprint.py '1bbe6bf2d62f58a8|2022-07-14--17-11-43' 'SUBARU OUTBACK 6TH GEN' |
||||
Attempting to add fw version for: SUBARU OUTBACK 6TH GEN |
||||
``` |
||||
|
||||
### [selfdrive/car/tests/test_car_interfaces.py](/selfdrive/car/tests/test_car_interfaces.py) |
||||
|
||||
Finds common bugs for car interfaces, without even requiring a route. |
||||
|
||||
|
||||
#### Example: Typo in signal name |
||||
```bash |
||||
> pytest selfdrive/car/tests/test_car_interfaces.py -k subaru # replace with the brand you are working on |
||||
|
||||
===================================================================== |
||||
FAILED selfdrive/car/tests/test_car_interfaces.py::TestCarInterfaces::test_car_interfaces_165_SUBARU_LEGACY_7TH_GEN - KeyError: 'CruiseControlOOPS' |
||||
|
||||
``` |
||||
|
||||
### [tools/car_porting/test_car_model.py](/tools/car_porting/test_car_model.py) |
||||
|
||||
Given a route, runs most of the car interface to check for common errors like missing signals, blocked panda messages, and safety mismatches. |
||||
|
||||
#### Example: panda safety mismatch for gasPressed |
||||
```bash |
||||
> python tools/car_porting/test_car_model.py '4822a427b188122a|2023-08-14--16-22-21' |
||||
|
||||
===================================================================== |
||||
FAIL: test_panda_safety_carstate (__main__.CarModelTestCase.test_panda_safety_carstate) |
||||
Assert that panda safety matches openpilot's carState |
||||
---------------------------------------------------------------------- |
||||
Traceback (most recent call last): |
||||
File "/home/batman/xx/openpilot/openpilot/selfdrive/car/tests/test_models.py", line 380, in test_panda_safety_carstate |
||||
self.assertFalse(len(failed_checks), f"panda safety doesn't agree with openpilot: {failed_checks}") |
||||
AssertionError: 1 is not false : panda safety doesn't agree with openpilot: {'gasPressed': 116} |
||||
``` |
||||
|
||||
### [tools/car_porting/examples/subaru_steer_temp_fault.ipynb](/tools/car_porting/examples/subaru_steer_temp_fault.ipynb) |
||||
|
||||
An example of searching through a database of segments for a specific condition, and plotting the results. |
||||
|
||||
 |
||||
|
||||
*a plot of the steer_warning vs steering angle, where we can see it is clearly caused by a large steering angle change* |
||||
|
||||
### [tools/car_porting/examples/subaru_long_accel.ipynb](/tools/car_porting/examples/subaru_long_accel.ipynb) |
||||
|
||||
An example of plotting the response of an actuator when it is active. |
||||
|
||||
 |
||||
|
||||
*a plot of the brake_pressure vs acceleration, where we can see it is a fairly linear response.* |
@ -0,0 +1,120 @@ |
||||
{ |
||||
"cells": [ |
||||
{ |
||||
"cell_type": "code", |
||||
"execution_count": null, |
||||
"metadata": {}, |
||||
"outputs": [], |
||||
"source": [ |
||||
"segments = [\n", |
||||
" \"d9df6f87e8feff94|2023-03-28--17-41-10/1:12\"\n", |
||||
"]\n", |
||||
"platform = \"SUBARU OUTBACK 6TH GEN\"\n" |
||||
] |
||||
}, |
||||
{ |
||||
"cell_type": "code", |
||||
"execution_count": null, |
||||
"metadata": {}, |
||||
"outputs": [], |
||||
"source": [ |
||||
"import copy\n", |
||||
"import numpy as np\n", |
||||
"\n", |
||||
"from opendbc.can.parser import CANParser\n", |
||||
"\n", |
||||
"from openpilot.selfdrive.car.subaru.values import DBC\n", |
||||
"from openpilot.tools.lib.logreader import LogReader\n", |
||||
"\n", |
||||
"\"\"\"\n", |
||||
"In this example, we plot the relationship between Cruise_Brake and Acceleration for stock eyesight.\n", |
||||
"\"\"\"\n", |
||||
"\n", |
||||
"for segment in segments:\n", |
||||
" lr = LogReader(segment)\n", |
||||
"\n", |
||||
" messages = [\n", |
||||
" (\"ES_Distance\", 20),\n", |
||||
" (\"ES_Brake\", 20),\n", |
||||
" (\"ES_Status\", 20),\n", |
||||
" ]\n", |
||||
"\n", |
||||
" cp = CANParser(DBC[platform][\"pt\"], messages, 1)\n", |
||||
"\n", |
||||
" es_distance_history = []\n", |
||||
" es_status_history = []\n", |
||||
" es_brake_history = []\n", |
||||
" acceleration_history = []\n", |
||||
"\n", |
||||
" last_acc = 0\n", |
||||
"\n", |
||||
" for msg in lr:\n", |
||||
" if msg.which() == \"can\":\n", |
||||
" cp.update_strings([msg.as_builder().to_bytes()])\n", |
||||
" es_distance_history.append(copy.copy(cp.vl[\"ES_Distance\"]))\n", |
||||
" es_brake_history.append(copy.copy(cp.vl[\"ES_Brake\"]))\n", |
||||
" es_status_history.append(copy.copy(cp.vl[\"ES_Status\"]))\n", |
||||
"\n", |
||||
" acceleration_history.append(last_acc)\n", |
||||
" \n", |
||||
" if msg.which() == \"carState\":\n", |
||||
" last_acc = msg.carState.aEgo" |
||||
] |
||||
}, |
||||
{ |
||||
"cell_type": "code", |
||||
"execution_count": null, |
||||
"metadata": {}, |
||||
"outputs": [], |
||||
"source": [ |
||||
"def process(history, func):\n", |
||||
" return np.array([func(h) for h in history])\n", |
||||
"\n", |
||||
"cruise_activated = process(es_status_history, lambda es_status: es_status[\"Cruise_Activated\"])\n", |
||||
"cruise_throttle = process(es_distance_history, lambda es_distance: es_distance[\"Cruise_Throttle\"])\n", |
||||
"cruise_rpm = process(es_status_history, lambda es_status: es_status[\"Cruise_RPM\"])\n", |
||||
"cruise_brake = process(es_brake_history, lambda es_brake: es_brake[\"Brake_Pressure\"])\n", |
||||
"acceleration = process(acceleration_history, lambda acc: acc)" |
||||
] |
||||
}, |
||||
{ |
||||
"cell_type": "code", |
||||
"execution_count": null, |
||||
"metadata": {}, |
||||
"outputs": [], |
||||
"source": [ |
||||
"import matplotlib.pyplot as plt\n", |
||||
"\n", |
||||
"valid_brake = (cruise_activated==1) & (cruise_brake>0) # only when cruise is activated and eyesight is braking\n", |
||||
"\n", |
||||
"ax = plt.figure().add_subplot()\n", |
||||
"\n", |
||||
"ax.set_title(\"Brake_Pressure vs Acceleration\")\n", |
||||
"ax.set_xlabel(\"Brake_Pessure\")\n", |
||||
"ax.set_ylabel(\"Acceleration\")\n", |
||||
"ax.scatter(cruise_brake[valid_brake], -acceleration[valid_brake])" |
||||
] |
||||
} |
||||
], |
||||
"metadata": { |
||||
"kernelspec": { |
||||
"display_name": "Python 3", |
||||
"language": "python", |
||||
"name": "python3" |
||||
}, |
||||
"language_info": { |
||||
"codemirror_mode": { |
||||
"name": "ipython", |
||||
"version": 3 |
||||
}, |
||||
"file_extension": ".py", |
||||
"mimetype": "text/x-python", |
||||
"name": "python", |
||||
"nbconvert_exporter": "python", |
||||
"pygments_lexer": "ipython3", |
||||
"version": "3.11.4" |
||||
} |
||||
}, |
||||
"nbformat": 4, |
||||
"nbformat_minor": 2 |
||||
} |
@ -0,0 +1,110 @@ |
||||
{ |
||||
"cells": [ |
||||
{ |
||||
"cell_type": "code", |
||||
"execution_count": null, |
||||
"metadata": {}, |
||||
"outputs": [], |
||||
"source": [ |
||||
"# An example of searching through a database of segments for a specific condition, and plotting the results.\n", |
||||
"\n", |
||||
"segments = [\n", |
||||
" \"c3d1ccb52f5f9d65|2023-07-22--01-23-20/6:10\",\n", |
||||
"]\n", |
||||
"platform = \"SUBARU OUTBACK 6TH GEN\"" |
||||
] |
||||
}, |
||||
{ |
||||
"cell_type": "code", |
||||
"execution_count": null, |
||||
"metadata": {}, |
||||
"outputs": [], |
||||
"source": [ |
||||
"import copy\n", |
||||
"import matplotlib.pyplot as plt\n", |
||||
"import numpy as np\n", |
||||
"\n", |
||||
"from opendbc.can.parser import CANParser\n", |
||||
"\n", |
||||
"from openpilot.selfdrive.car.subaru.values import CanBus, DBC\n", |
||||
"from openpilot.tools.lib.logreader import LogReader\n", |
||||
"\n", |
||||
"\"\"\"\n", |
||||
"In this example, we search for positive transitions of Steer_Warning, which indicate that the EPS\n", |
||||
"has stopped responding to our messages. This analysis would allow you to find the cause of these\n", |
||||
"steer warnings and potentially work around them.\n", |
||||
"\"\"\"\n", |
||||
"\n", |
||||
"for segment in segments:\n", |
||||
" lr = LogReader(segment)\n", |
||||
"\n", |
||||
" can_msgs = [msg for msg in lr if msg.which() == \"can\"]\n", |
||||
"\n", |
||||
" messages = [\n", |
||||
" (\"Steering_Torque\", 50)\n", |
||||
" ]\n", |
||||
"\n", |
||||
" cp = CANParser(DBC[platform][\"pt\"], messages, CanBus.main)\n", |
||||
"\n", |
||||
" steering_torque_history = []\n", |
||||
" examples = []\n", |
||||
"\n", |
||||
" for msg in can_msgs:\n", |
||||
" cp.update_strings([msg.as_builder().to_bytes()])\n", |
||||
" steering_torque_history.append(copy.copy(cp.vl[\"Steering_Torque\"]))\n", |
||||
" \n", |
||||
" steer_warning_last = False\n", |
||||
" for i, steering_torque_msg in enumerate(steering_torque_history):\n", |
||||
" steer_warning = steering_torque_msg[\"Steer_Warning\"]\n", |
||||
"\n", |
||||
" steer_angle = steering_torque_msg[\"Steering_Angle\"]\n", |
||||
"\n", |
||||
" if steer_warning and not steer_warning_last: # positive transition of \"Steer_Warning\"\n", |
||||
" examples.append(i)\n", |
||||
"\n", |
||||
" steer_warning_last = steer_warning\n", |
||||
"\n", |
||||
" FRAME_DELTA = 100 # plot this many frames around the positive transition\n", |
||||
"\n", |
||||
" for example in examples:\n", |
||||
" fig, axs = plt.subplots(2)\n", |
||||
"\n", |
||||
" min_frame = int(example-FRAME_DELTA/2)\n", |
||||
" max_frame = int(example+FRAME_DELTA/2)\n", |
||||
"\n", |
||||
" steering_angle_history = [msg[\"Steering_Angle\"] for msg in steering_torque_history[min_frame:max_frame]]\n", |
||||
" steering_warning_history = [msg[\"Steer_Warning\"] for msg in steering_torque_history[min_frame:max_frame]]\n", |
||||
"\n", |
||||
" xs = np.arange(-FRAME_DELTA/2, FRAME_DELTA/2)\n", |
||||
"\n", |
||||
" axs[0].plot(xs, steering_angle_history)\n", |
||||
" axs[0].set_ylabel(\"Steering Angle (deg)\")\n", |
||||
" axs[1].plot(xs, steering_warning_history)\n", |
||||
" axs[1].set_ylabel(\"Steer Warning\")\n", |
||||
"\n", |
||||
" plt.show()\n" |
||||
] |
||||
} |
||||
], |
||||
"metadata": { |
||||
"kernelspec": { |
||||
"display_name": "Python 3", |
||||
"language": "python", |
||||
"name": "python3" |
||||
}, |
||||
"language_info": { |
||||
"codemirror_mode": { |
||||
"name": "ipython", |
||||
"version": 3 |
||||
}, |
||||
"file_extension": ".py", |
||||
"mimetype": "text/x-python", |
||||
"name": "python", |
||||
"nbconvert_exporter": "python", |
||||
"pygments_lexer": "ipython3", |
||||
"version": "3.11.4" |
||||
} |
||||
}, |
||||
"nbformat": 4, |
||||
"nbformat_minor": 2 |
||||
} |
@ -0,0 +1,86 @@ |
||||
import shutil |
||||
import tempfile |
||||
import numpy as np |
||||
import unittest |
||||
from parameterized import parameterized |
||||
import requests |
||||
from openpilot.tools.lib.logreader import LogReader, parse_indirect, parse_slice, ReadMode |
||||
from openpilot.tools.lib.route import SegmentRange |
||||
|
||||
NUM_SEGS = 17 # number of segments in the test route |
||||
ALL_SEGS = list(np.arange(NUM_SEGS)) |
||||
TEST_ROUTE = "344c5c15b34f2d8a/2024-01-03--09-37-12" |
||||
QLOG_FILE = "https://commadataci.blob.core.windows.net/openpilotci/0375fdf7b1ce594d/2019-06-13--08-32-25/3/qlog.bz2" |
||||
|
||||
class TestLogReader(unittest.TestCase): |
||||
@parameterized.expand([ |
||||
(f"{TEST_ROUTE}", ALL_SEGS), |
||||
(f"{TEST_ROUTE.replace('/', '|')}", ALL_SEGS), |
||||
(f"{TEST_ROUTE}--0", [0]), |
||||
(f"{TEST_ROUTE}--5", [5]), |
||||
(f"{TEST_ROUTE}/0", [0]), |
||||
(f"{TEST_ROUTE}/5", [5]), |
||||
(f"{TEST_ROUTE}/0:10", ALL_SEGS[0:10]), |
||||
(f"{TEST_ROUTE}/0:0", []), |
||||
(f"{TEST_ROUTE}/4:6", ALL_SEGS[4:6]), |
||||
(f"{TEST_ROUTE}/0:-1", ALL_SEGS[0:-1]), |
||||
(f"{TEST_ROUTE}/:5", ALL_SEGS[:5]), |
||||
(f"{TEST_ROUTE}/2:", ALL_SEGS[2:]), |
||||
(f"{TEST_ROUTE}/2:-1", ALL_SEGS[2:-1]), |
||||
(f"{TEST_ROUTE}/-1", [ALL_SEGS[-1]]), |
||||
(f"{TEST_ROUTE}/-2", [ALL_SEGS[-2]]), |
||||
(f"{TEST_ROUTE}/-2:-1", ALL_SEGS[-2:-1]), |
||||
(f"{TEST_ROUTE}/-4:-2", ALL_SEGS[-4:-2]), |
||||
(f"{TEST_ROUTE}/:10:2", ALL_SEGS[:10:2]), |
||||
(f"{TEST_ROUTE}/5::2", ALL_SEGS[5::2]), |
||||
(f"https://useradmin.comma.ai/?onebox={TEST_ROUTE}", ALL_SEGS), |
||||
(f"https://useradmin.comma.ai/?onebox={TEST_ROUTE.replace('/', '|')}", ALL_SEGS), |
||||
(f"https://useradmin.comma.ai/?onebox={TEST_ROUTE.replace('/', '%7C')}", ALL_SEGS), |
||||
(f"https://cabana.comma.ai/?route={TEST_ROUTE}", ALL_SEGS), |
||||
]) |
||||
def test_indirect_parsing(self, identifier, expected): |
||||
parsed, _, _ = parse_indirect(identifier) |
||||
sr = SegmentRange(parsed) |
||||
segs = parse_slice(sr) |
||||
self.assertListEqual(list(segs), expected) |
||||
|
||||
def test_direct_parsing(self): |
||||
qlog = tempfile.NamedTemporaryFile(mode='wb', delete=False) |
||||
|
||||
with requests.get(QLOG_FILE, stream=True) as r: |
||||
with qlog as f: |
||||
shutil.copyfileobj(r.raw, f) |
||||
|
||||
for f in [QLOG_FILE, qlog.name]: |
||||
l = len(list(LogReader(f))) |
||||
self.assertGreater(l, 100) |
||||
|
||||
@parameterized.expand([ |
||||
(f"{TEST_ROUTE}///",), |
||||
(f"{TEST_ROUTE}---",), |
||||
(f"{TEST_ROUTE}/-4:--2",), |
||||
(f"{TEST_ROUTE}/-a",), |
||||
(f"{TEST_ROUTE}/j",), |
||||
(f"{TEST_ROUTE}/0:1:2:3",), |
||||
(f"{TEST_ROUTE}/:::3",), |
||||
]) |
||||
def test_bad_ranges(self, segment_range): |
||||
with self.assertRaises(AssertionError): |
||||
sr = SegmentRange(segment_range) |
||||
parse_slice(sr) |
||||
|
||||
def test_modes(self): |
||||
qlog_len = len(list(LogReader(f"{TEST_ROUTE}/0", ReadMode.QLOG))) |
||||
rlog_len = len(list(LogReader(f"{TEST_ROUTE}/0", ReadMode.RLOG))) |
||||
|
||||
self.assertLess(qlog_len * 6, rlog_len) |
||||
|
||||
def test_modes_from_name(self): |
||||
qlog_len = len(list(LogReader(f"{TEST_ROUTE}/0/q"))) |
||||
rlog_len = len(list(LogReader(f"{TEST_ROUTE}/0/r"))) |
||||
|
||||
self.assertLess(qlog_len * 6, rlog_len) |
||||
|
||||
|
||||
if __name__ == "__main__": |
||||
unittest.main() |
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in new issue