parent
ee493eed79
commit
99b637c7ce
844 changed files with 5546 additions and 7054 deletions
@ -1,3 +1,3 @@ |
||||
version https://git-lfs.github.com/spec/v1 |
||||
oid sha256:8c6eff576ba07fc45d0257241e024075ac4e3d6c3391ed75d496f140d146e72b |
||||
size 2535 |
||||
oid sha256:5e6997ef9a2f37fb6783d0b41c6d85b8c275e916f0e66dcbd8b1050461892852 |
||||
size 2599 |
||||
|
@ -1,3 +1,3 @@ |
||||
version https://git-lfs.github.com/spec/v1 |
||||
oid sha256:eeb5d8af1db839a0bf1a594af50c52d0d79dc6e764569819dc8e78dbb039090c |
||||
size 159193 |
||||
oid sha256:67e035ae5f7a07977f9839dcff6ff49189f5742e2e2e92977b2cfc0e041189df |
||||
size 168833 |
||||
|
@ -1,36 +0,0 @@ |
||||
Welcome to chffrplus |
||||
====== |
||||
|
||||
[chffrplus](https://github.com/commaai/chffrplus) is an open source dashcam. |
||||
|
||||
This is the shipping reference software for the comma EON Dashcam DevKit. It keeps many of the niceities of [openpilot](https://github.com/commaai/openpilot), like high quality sensors, great camera, and good autostart and stop. Though unlike openpilot, it cannot control your car. chffrplus can interface with your car through a [panda](https://shop.comma.ai/products/panda-obd-ii-dongle), but just like our dashcam app [chffr](https://getchffr.com/), it is read only. |
||||
|
||||
It integrates with the rest of the comma ecosystem, so you can view your drives on the [chffr](https://getchffr.com/) app for Android or iOS, and reverse engineer your car with [cabana](https://community.comma.ai/cabana/?demo=1). |
||||
|
||||
|
||||
Hardware |
||||
------ |
||||
|
||||
Right now chffrplus supports the [EON Dashcam DevKit](https://shop.comma.ai/products/eon-dashcam-devkit) for hardware to run on. |
||||
|
||||
Install chffrplus on a EON device by entering ``https://chffrplus.comma.ai`` during NEOS setup. |
||||
|
||||
|
||||
User Data / chffr Account / Crash Reporting |
||||
------ |
||||
|
||||
By default chffrplus creates an account and includes a client for chffr, our dashcam app. |
||||
|
||||
It's open source software, so you are free to disable it if you wish. |
||||
|
||||
It logs the road facing camera, CAN, GPS, IMU, magnetometer, thermal sensors, crashes, and operating system logs. |
||||
It does not log the user facing camera or the microphone. |
||||
|
||||
By using it, you agree to [our privacy policy](https://beta.comma.ai/privacy.html). You understand that use of this software or its related services will generate certain types of user data, which may be logged and stored at the sole discretion of comma.ai. By accepting this agreement, you grant an irrevocable, perpetual, worldwide right to comma.ai for the use of this data. |
||||
|
||||
|
||||
Licensing |
||||
------ |
||||
|
||||
chffrplus is released under the MIT license. |
||||
|
@ -0,0 +1,212 @@ |
||||
import os |
||||
import subprocess |
||||
import sys |
||||
|
||||
AddOption('--test', |
||||
action='store_true', |
||||
help='build test files') |
||||
|
||||
AddOption('--asan', |
||||
action='store_true', |
||||
help='turn on ASAN') |
||||
|
||||
arch = subprocess.check_output(["uname", "-m"], encoding='utf8').rstrip() |
||||
|
||||
if arch == "aarch64": |
||||
lenv = { |
||||
"LD_LIBRARY_PATH": '/data/data/com.termux/files/usr/lib', |
||||
"PATH": os.environ['PATH'], |
||||
"ANDROID_DATA": os.environ['ANDROID_DATA'], |
||||
"ANDROID_ROOT": os.environ['ANDROID_ROOT'], |
||||
} |
||||
|
||||
cpppath = [ |
||||
"#phonelibs/opencl/include", |
||||
] |
||||
libpath = [ |
||||
"#phonelibs/snpe/aarch64-android-clang3.8", |
||||
"/usr/lib", |
||||
"/data/data/com.termux/files/usr/lib", |
||||
"/system/vendor/lib64", |
||||
"/system/comma/usr/lib", |
||||
"#phonelibs/yaml-cpp/lib", |
||||
"#phonelibs/nanovg", |
||||
"#phonelibs/libyuv/lib", |
||||
] |
||||
|
||||
cflags = ["-DQCOM", "-mcpu=cortex-a57"] |
||||
cxxflags = ["-DQCOM", "-mcpu=cortex-a57"] |
||||
|
||||
rpath = ["/system/vendor/lib64"] |
||||
else: |
||||
lenv = { |
||||
"PATH": "#external/bin:" + os.environ['PATH'], |
||||
} |
||||
cpppath = [ |
||||
"#phonelibs/capnp-cpp/include", |
||||
"#phonelibs/capnp-c/include", |
||||
"#phonelibs/zmq/x64/include", |
||||
] |
||||
libpath = [ |
||||
"#phonelibs/capnp-cpp/x64/lib", |
||||
"#phonelibs/capnp-c/x64/lib", |
||||
"#phonelibs/yaml-cpp/x64/lib", |
||||
"#phonelibs/snpe/x86_64-linux-clang", |
||||
"#phonelibs/zmq/x64/lib", |
||||
"#phonelibs/libyuv/x64/lib", |
||||
"#external/zmq/lib", |
||||
"#cereal", |
||||
"#selfdrive/common", |
||||
"/usr/lib", |
||||
"/usr/local/lib", |
||||
] |
||||
|
||||
rpath = ["phonelibs/capnp-cpp/x64/lib", |
||||
"cereal", |
||||
"selfdrive/common"] |
||||
|
||||
# allows shared libraries to work globally |
||||
rpath = [os.path.join(os.getcwd(), x) for x in rpath] |
||||
|
||||
cflags = [] |
||||
cxxflags = [] |
||||
|
||||
ccflags_asan = ["-fsanitize=address", "-fno-omit-frame-pointer"] if GetOption('asan') else [] |
||||
ldflags_asan = ["-fsanitize=address"] if GetOption('asan') else [] |
||||
|
||||
# change pythonpath to this |
||||
lenv["PYTHONPATH"] = Dir("#").path |
||||
|
||||
env = Environment( |
||||
ENV=lenv, |
||||
CCFLAGS=[ |
||||
"-g", |
||||
"-fPIC", |
||||
"-O2", |
||||
"-Werror=implicit-function-declaration", |
||||
"-Werror=incompatible-pointer-types", |
||||
"-Werror=int-conversion", |
||||
"-Werror=return-type", |
||||
"-Werror=format-extra-args", |
||||
] + cflags + ccflags_asan, |
||||
|
||||
CPPPATH=cpppath + [ |
||||
"#", |
||||
"#selfdrive", |
||||
"#phonelibs/bzip2", |
||||
"#phonelibs/libyuv/include", |
||||
"#phonelibs/yaml-cpp/include", |
||||
"#phonelibs/openmax/include", |
||||
"#phonelibs/json/src", |
||||
"#phonelibs/json11", |
||||
"#phonelibs/eigen", |
||||
"#phonelibs/curl/include", |
||||
"#phonelibs/opencv/include", |
||||
"#phonelibs/libgralloc/include", |
||||
"#phonelibs/android_frameworks_native/include", |
||||
"#phonelibs/android_hardware_libhardware/include", |
||||
"#phonelibs/android_system_core/include", |
||||
"#phonelibs/linux/include", |
||||
"#phonelibs/snpe/include", |
||||
"#phonelibs/nanovg", |
||||
"#selfdrive/common", |
||||
"#selfdrive/camerad", |
||||
"#selfdrive/camerad/include", |
||||
"#selfdrive/loggerd/include", |
||||
"#selfdrive/modeld", |
||||
"#cereal/messaging", |
||||
"#cereal", |
||||
"#opendbc/can", |
||||
], |
||||
|
||||
CC='clang', |
||||
CXX='clang++', |
||||
LINKFLAGS=ldflags_asan, |
||||
|
||||
RPATH=rpath, |
||||
|
||||
CFLAGS=["-std=gnu11"] + cflags, |
||||
CXXFLAGS=["-std=c++14"] + cxxflags, |
||||
LIBPATH=libpath + |
||||
[ |
||||
"#cereal", |
||||
"#selfdrive/common", |
||||
"#phonelibs", |
||||
] |
||||
) |
||||
|
||||
if os.environ.get('SCONS_CACHE'): |
||||
CacheDir('/tmp/scons_cache') |
||||
|
||||
node_interval = 5 |
||||
node_count = 0 |
||||
def progress_function(node): |
||||
global node_count |
||||
node_count += node_interval |
||||
sys.stderr.write("progress: %d\n" % node_count) |
||||
|
||||
if os.environ.get('SCONS_PROGRESS'): |
||||
Progress(progress_function, interval=node_interval) |
||||
|
||||
SHARED = False |
||||
|
||||
def abspath(x): |
||||
if arch == 'aarch64': |
||||
pth = os.path.join("/data/pythonpath", x[0].path) |
||||
env.Depends(pth, x) |
||||
return File(pth) |
||||
else: |
||||
# rpath works elsewhere |
||||
return x[0].path.rsplit("/", 1)[1][:-3] |
||||
|
||||
#zmq = 'zmq' |
||||
# still needed for apks |
||||
zmq = FindFile("libzmq.a", libpath) |
||||
Export('env', 'arch', 'zmq', 'SHARED') |
||||
|
||||
# cereal and messaging are shared with the system |
||||
SConscript(['cereal/SConscript']) |
||||
if SHARED: |
||||
cereal = abspath([File('cereal/libcereal_shared.so')]) |
||||
messaging = abspath([File('cereal/libmessaging_shared.so')]) |
||||
else: |
||||
cereal = [File('#cereal/libcereal.a')] |
||||
messaging = [File('#cereal/libmessaging.a')] |
||||
Export('cereal', 'messaging') |
||||
|
||||
SConscript(['selfdrive/common/SConscript']) |
||||
Import('_common', '_visionipc', '_gpucommon', '_gpu_libs') |
||||
|
||||
if SHARED: |
||||
common, visionipc, gpucommon = abspath(common), abspath(visionipc), abspath(gpucommon) |
||||
else: |
||||
common = [_common, 'json'] |
||||
visionipc = _visionipc |
||||
gpucommon = [_gpucommon] + _gpu_libs |
||||
|
||||
Export('common', 'visionipc', 'gpucommon') |
||||
|
||||
SConscript(['opendbc/can/SConscript']) |
||||
|
||||
SConscript(['common/SConscript']) |
||||
SConscript(['common/kalman/SConscript']) |
||||
SConscript(['phonelibs/SConscript']) |
||||
|
||||
SConscript(['selfdrive/modeld/SConscript']) |
||||
SConscript(['selfdrive/camerad/SConscript']) |
||||
SConscript(['selfdrive/controls/lib/cluster/SConscript']) |
||||
SConscript(['selfdrive/controls/lib/lateral_mpc/SConscript']) |
||||
SConscript(['selfdrive/controls/lib/longitudinal_mpc/SConscript']) |
||||
|
||||
SConscript(['selfdrive/boardd/SConscript']) |
||||
SConscript(['selfdrive/proclogd/SConscript']) |
||||
|
||||
if arch == "aarch64": |
||||
SConscript(['selfdrive/logcatd/SConscript']) |
||||
SConscript(['selfdrive/ui/SConscript']) |
||||
SConscript(['selfdrive/sensord/SConscript']) |
||||
SConscript(['selfdrive/loggerd/SConscript']) |
||||
|
||||
SConscript(['selfdrive/locationd/SConscript']) |
||||
|
||||
# TODO: finish cereal, dbcbuilder, MPC |
@ -1,3 +1,3 @@ |
||||
version https://git-lfs.github.com/spec/v1 |
||||
oid sha256:1d3e4d7e9a84cb5a507137f1d57046649ffc000f2ff0bbd047438846f07d04d5 |
||||
size 2616780 |
||||
oid sha256:8b20fb584bc1cc3637e5cb58b6688e9e250bb7f9eacb8b6e0d50a890e79d7797 |
||||
size 2848396 |
||||
|
@ -1,3 +1,3 @@ |
||||
version https://git-lfs.github.com/spec/v1 |
||||
oid sha256:930595cab227f8288a5dafaa04d3e71c98166c55cf6399e25e46f51237f1ac98 |
||||
size 17796274 |
||||
oid sha256:7a35f3ee4353210c0cfe714653920c05c588fb40c6c62088f81cd02bfc7eb6d3 |
||||
size 16150762 |
||||
|
@ -0,0 +1 @@ |
||||
*.cpp |
@ -0,0 +1,6 @@ |
||||
Import('env') |
||||
|
||||
# parser |
||||
env.Command(['common_pyx.so'], |
||||
['common_pyx_setup.py', 'clock.pyx'], |
||||
"cd common && python3 common_pyx_setup.py build_ext --inplace") |
@ -0,0 +1,73 @@ |
||||
import binascii |
||||
import itertools |
||||
import re |
||||
import struct |
||||
import subprocess |
||||
|
||||
def getprop(key): |
||||
return subprocess.check_output(["getprop", key], encoding='utf8').strip() |
||||
|
||||
def get_imei(): |
||||
ret = getprop("oem.device.imeicache") |
||||
if ret == "": |
||||
ret = "000000000000000" |
||||
return ret |
||||
|
||||
def get_serial(): |
||||
return getprop("ro.serialno") |
||||
|
||||
def get_subscriber_info(): |
||||
ret = parse_service_call_string(["iphonesubinfo", "7"]) |
||||
if ret is None or len(ret) < 8: |
||||
return "" |
||||
return ret |
||||
|
||||
def reboot(reason=None): |
||||
if reason is None: |
||||
reason_args = ["null"] |
||||
else: |
||||
reason_args = ["s16", reason] |
||||
|
||||
subprocess.check_output([ |
||||
"service", "call", "power", "16", # IPowerManager.reboot |
||||
"i32", "0", # no confirmation, |
||||
*reason_args, |
||||
"i32", "1" # wait |
||||
]) |
||||
|
||||
def parse_service_call_unpack(call, fmt): |
||||
r = parse_service_call_bytes(call) |
||||
try: |
||||
return struct.unpack(fmt, r)[0] |
||||
except Exception: |
||||
return None |
||||
|
||||
def parse_service_call_string(call): |
||||
r = parse_service_call_bytes(call) |
||||
try: |
||||
r = r[8:] # Cut off length field |
||||
r = r.decode('utf_16_be') |
||||
|
||||
# All pairs of two characters seem to be swapped. Not sure why |
||||
result = "" |
||||
for a, b, in itertools.zip_longest(r[::2], r[1::2], fillvalue='\x00'): |
||||
result += b + a |
||||
|
||||
result = result.replace('\x00', '') |
||||
|
||||
return result |
||||
except Exception: |
||||
return None |
||||
|
||||
def parse_service_call_bytes(call): |
||||
ret = subprocess.check_output(["service", "call", *call], encoding='utf8').strip() |
||||
if 'Parcel' not in ret: |
||||
return None |
||||
|
||||
try: |
||||
r = b"" |
||||
for hex_part in re.findall(r'[ (]([0-9a-f]{8})', ret): |
||||
r += binascii.unhexlify(hex_part) |
||||
return r |
||||
except Exception: |
||||
return None |
@ -0,0 +1,98 @@ |
||||
import os |
||||
import subprocess |
||||
import glob |
||||
import hashlib |
||||
import shutil |
||||
from common.basedir import BASEDIR |
||||
from selfdrive.swaglog import cloudlog |
||||
|
||||
android_packages = ("ai.comma.plus.offroad", "ai.comma.plus.frame") |
||||
|
||||
def get_installed_apks(): |
||||
dat = subprocess.check_output(["pm", "list", "packages", "-f"], encoding='utf8').strip().split("\n") |
||||
ret = {} |
||||
for x in dat: |
||||
if x.startswith("package:"): |
||||
v,k = x.split("package:")[1].split("=") |
||||
ret[k] = v |
||||
return ret |
||||
|
||||
def install_apk(path): |
||||
# can only install from world readable path |
||||
install_path = "/sdcard/%s" % os.path.basename(path) |
||||
shutil.copyfile(path, install_path) |
||||
|
||||
ret = subprocess.call(["pm", "install", "-r", install_path]) |
||||
os.remove(install_path) |
||||
return ret == 0 |
||||
|
||||
def start_frame(): |
||||
set_package_permissions() |
||||
system("am start -n ai.comma.plus.frame/.MainActivity") |
||||
|
||||
def set_package_permissions(): |
||||
pm_grant("ai.comma.plus.offroad", "android.permission.ACCESS_FINE_LOCATION") |
||||
appops_set("ai.comma.plus.offroad", "SU", "allow") |
||||
appops_set("ai.comma.plus.offroad", "WIFI_SCAN", "allow") |
||||
appops_set("ai.comma.plus.offroad", "READ_EXTERNAL_STORAGE", "allow") |
||||
appops_set("ai.comma.plus.offroad", "WRITE_EXTERNAL_STORAGE", "allow") |
||||
|
||||
def appops_set(package, op, mode): |
||||
system(f"LD_LIBRARY_PATH= appops set {package} {op} {mode}") |
||||
|
||||
def pm_grant(package, permission): |
||||
system(f"pm grant {package} {permission}") |
||||
|
||||
def system(cmd): |
||||
try: |
||||
cloudlog.info("running %s" % cmd) |
||||
subprocess.check_output(cmd, stderr=subprocess.STDOUT, shell=True) |
||||
except subprocess.CalledProcessError as e: |
||||
cloudlog.event("running failed", |
||||
cmd=e.cmd, |
||||
output=e.output[-1024:], |
||||
returncode=e.returncode) |
||||
|
||||
# *** external functions *** |
||||
|
||||
def update_apks(): |
||||
# install apks |
||||
installed = get_installed_apks() |
||||
|
||||
install_apks = glob.glob(os.path.join(BASEDIR, "apk/*.apk")) |
||||
for apk in install_apks: |
||||
app = os.path.basename(apk)[:-4] |
||||
if app not in installed: |
||||
installed[app] = None |
||||
|
||||
cloudlog.info("installed apks %s" % (str(installed), )) |
||||
|
||||
for app in installed.keys(): |
||||
apk_path = os.path.join(BASEDIR, "apk/"+app+".apk") |
||||
if not os.path.exists(apk_path): |
||||
continue |
||||
|
||||
h1 = hashlib.sha1(open(apk_path, 'rb').read()).hexdigest() |
||||
h2 = None |
||||
if installed[app] is not None: |
||||
h2 = hashlib.sha1(open(installed[app], 'rb').read()).hexdigest() |
||||
cloudlog.info("comparing version of %s %s vs %s" % (app, h1, h2)) |
||||
|
||||
if h2 is None or h1 != h2: |
||||
cloudlog.info("installing %s" % app) |
||||
|
||||
success = install_apk(apk_path) |
||||
if not success: |
||||
cloudlog.info("needing to uninstall %s" % app) |
||||
system("pm uninstall %s" % app) |
||||
success = install_apk(apk_path) |
||||
|
||||
assert success |
||||
|
||||
def pm_apply_packages(cmd): |
||||
for p in android_packages: |
||||
system("pm %s %s" % (cmd, p)) |
||||
|
||||
if __name__ == "__main__": |
||||
update_apks() |
||||
|
@ -0,0 +1,20 @@ |
||||
from distutils.core import Extension, setup # pylint: disable=import-error,no-name-in-module |
||||
from Cython.Build import cythonize |
||||
|
||||
from common.cython_hacks import BuildExtWithoutPlatformSuffix |
||||
|
||||
sourcefiles = ['clock.pyx'] |
||||
extra_compile_args = ["-std=c++11"] |
||||
|
||||
setup(name='Common', |
||||
cmdclass={'build_ext': BuildExtWithoutPlatformSuffix}, |
||||
ext_modules=cythonize( |
||||
Extension( |
||||
"common_pyx", |
||||
language="c++", |
||||
sources=sourcefiles, |
||||
extra_compile_args=extra_compile_args, |
||||
) |
||||
), |
||||
nthreads=4, |
||||
) |
@ -1,276 +0,0 @@ |
||||
import re |
||||
import os |
||||
import struct |
||||
import sys |
||||
import numbers |
||||
from collections import namedtuple, defaultdict |
||||
|
||||
def int_or_float(s): |
||||
# return number, trying to maintain int format |
||||
if s.isdigit(): |
||||
return int(s, 10) |
||||
else: |
||||
return float(s) |
||||
|
||||
DBCSignal = namedtuple( |
||||
"DBCSignal", ["name", "start_bit", "size", "is_little_endian", "is_signed", |
||||
"factor", "offset", "tmin", "tmax", "units"]) |
||||
|
||||
|
||||
class dbc(): |
||||
def __init__(self, fn): |
||||
self.name, _ = os.path.splitext(os.path.basename(fn)) |
||||
with open(fn, encoding="ascii") as f: |
||||
self.txt = f.readlines() |
||||
self._warned_addresses = set() |
||||
|
||||
# regexps from https://github.com/ebroecker/canmatrix/blob/master/canmatrix/importdbc.py |
||||
bo_regexp = re.compile(r"^BO\_ (\w+) (\w+) *: (\w+) (\w+)") |
||||
sg_regexp = re.compile(r"^SG\_ (\w+) : (\d+)\|(\d+)@(\d+)([\+|\-]) \(([0-9.+\-eE]+),([0-9.+\-eE]+)\) \[([0-9.+\-eE]+)\|([0-9.+\-eE]+)\] \"(.*)\" (.*)") |
||||
sgm_regexp = re.compile(r"^SG\_ (\w+) (\w+) *: (\d+)\|(\d+)@(\d+)([\+|\-]) \(([0-9.+\-eE]+),([0-9.+\-eE]+)\) \[([0-9.+\-eE]+)\|([0-9.+\-eE]+)\] \"(.*)\" (.*)") |
||||
val_regexp = re.compile(r"VAL\_ (\w+) (\w+) (\s*[-+]?[0-9]+\s+\".+?\"[^;]*)") |
||||
|
||||
# A dictionary which maps message ids to tuples ((name, size), signals). |
||||
# name is the ASCII name of the message. |
||||
# size is the size of the message in bytes. |
||||
# signals is a list signals contained in the message. |
||||
# signals is a list of DBCSignal in order of increasing start_bit. |
||||
self.msgs = {} |
||||
|
||||
# A dictionary which maps message ids to a list of tuples (signal name, definition value pairs) |
||||
self.def_vals = defaultdict(list) |
||||
|
||||
# lookup to bit reverse each byte |
||||
self.bits_index = [(i & ~0b111) + ((-i-1) & 0b111) for i in range(64)] |
||||
|
||||
for l in self.txt: |
||||
l = l.strip() |
||||
|
||||
if l.startswith("BO_ "): |
||||
# new group |
||||
dat = bo_regexp.match(l) |
||||
|
||||
if dat is None: |
||||
print("bad BO {0}".format(l)) |
||||
|
||||
name = dat.group(2) |
||||
size = int(dat.group(3)) |
||||
ids = int(dat.group(1), 0) # could be hex |
||||
if ids in self.msgs: |
||||
sys.exit("Duplicate address detected %d %s" % (ids, self.name)) |
||||
|
||||
self.msgs[ids] = ((name, size), []) |
||||
|
||||
if l.startswith("SG_ "): |
||||
# new signal |
||||
dat = sg_regexp.match(l) |
||||
go = 0 |
||||
if dat is None: |
||||
dat = sgm_regexp.match(l) |
||||
go = 1 |
||||
|
||||
if dat is None: |
||||
print("bad SG {0}".format(l)) |
||||
|
||||
sgname = dat.group(1) |
||||
start_bit = int(dat.group(go+2)) |
||||
signal_size = int(dat.group(go+3)) |
||||
is_little_endian = int(dat.group(go+4))==1 |
||||
is_signed = dat.group(go+5)=='-' |
||||
factor = int_or_float(dat.group(go+6)) |
||||
offset = int_or_float(dat.group(go+7)) |
||||
tmin = int_or_float(dat.group(go+8)) |
||||
tmax = int_or_float(dat.group(go+9)) |
||||
units = dat.group(go+10) |
||||
|
||||
self.msgs[ids][1].append( |
||||
DBCSignal(sgname, start_bit, signal_size, is_little_endian, |
||||
is_signed, factor, offset, tmin, tmax, units)) |
||||
|
||||
if l.startswith("VAL_ "): |
||||
# new signal value/definition |
||||
dat = val_regexp.match(l) |
||||
|
||||
if dat is None: |
||||
print("bad VAL {0}".format(l)) |
||||
|
||||
ids = int(dat.group(1), 0) # could be hex |
||||
sgname = dat.group(2) |
||||
defvals = dat.group(3) |
||||
|
||||
defvals = defvals.replace("?",r"\?") #escape sequence in C++ |
||||
defvals = defvals.split('"')[:-1] |
||||
|
||||
# convert strings to UPPER_CASE_WITH_UNDERSCORES |
||||
defvals[1::2] = [d.strip().upper().replace(" ","_") for d in defvals[1::2]] |
||||
defvals = '"'+"".join(str(i) for i in defvals)+'"' |
||||
|
||||
self.def_vals[ids].append((sgname, defvals)) |
||||
|
||||
for msg in self.msgs.values(): |
||||
msg[1].sort(key=lambda x: x.start_bit) |
||||
|
||||
self.msg_name_to_address = {} |
||||
for address, m in self.msgs.items(): |
||||
name = m[0][0] |
||||
self.msg_name_to_address[name] = address |
||||
|
||||
def lookup_msg_id(self, msg_id): |
||||
if not isinstance(msg_id, numbers.Number): |
||||
msg_id = self.msg_name_to_address[msg_id] |
||||
return msg_id |
||||
|
||||
def reverse_bytes(self, x): |
||||
return ((x & 0xff00000000000000) >> 56) | \ |
||||
((x & 0x00ff000000000000) >> 40) | \ |
||||
((x & 0x0000ff0000000000) >> 24) | \ |
||||
((x & 0x000000ff00000000) >> 8) | \ |
||||
((x & 0x00000000ff000000) << 8) | \ |
||||
((x & 0x0000000000ff0000) << 24) | \ |
||||
((x & 0x000000000000ff00) << 40) | \ |
||||
((x & 0x00000000000000ff) << 56) |
||||
|
||||
def encode(self, msg_id, dd): |
||||
"""Encode a CAN message using the dbc. |
||||
|
||||
Inputs: |
||||
msg_id: The message ID. |
||||
dd: A dictionary mapping signal name to signal data. |
||||
""" |
||||
msg_id = self.lookup_msg_id(msg_id) |
||||
|
||||
msg_def = self.msgs[msg_id] |
||||
size = msg_def[0][1] |
||||
|
||||
result = 0 |
||||
for s in msg_def[1]: |
||||
ival = dd.get(s.name) |
||||
if ival is not None: |
||||
|
||||
ival = (ival / s.factor) - s.offset |
||||
ival = int(round(ival)) |
||||
|
||||
if s.is_signed and ival < 0: |
||||
ival = (1 << s.size) + ival |
||||
|
||||
if s.is_little_endian: |
||||
shift = s.start_bit |
||||
else: |
||||
b1 = (s.start_bit // 8) * 8 + (-s.start_bit - 1) % 8 |
||||
shift = 64 - (b1 + s.size) |
||||
|
||||
mask = ((1 << s.size) - 1) << shift |
||||
dat = (ival & ((1 << s.size) - 1)) << shift |
||||
|
||||
if s.is_little_endian: |
||||
mask = self.reverse_bytes(mask) |
||||
dat = self.reverse_bytes(dat) |
||||
|
||||
result &= ~mask |
||||
result |= dat |
||||
|
||||
result = struct.pack('>Q', result) |
||||
return result[:size] |
||||
|
||||
def decode(self, x, arr=None, debug=False): |
||||
"""Decode a CAN message using the dbc. |
||||
|
||||
Inputs: |
||||
x: A collection with elements (address, time, data), where address is |
||||
the CAN address, time is the bus time, and data is the CAN data as a |
||||
hex string. |
||||
arr: Optional list of signals which should be decoded and returned. |
||||
debug: True to print debugging statements. |
||||
|
||||
Returns: |
||||
A tuple (name, data), where name is the name of the CAN message and data |
||||
is the decoded result. If arr is None, data is a dict of properties. |
||||
Otherwise data is a list of the same length as arr. |
||||
|
||||
Returns (None, None) if the message could not be decoded. |
||||
""" |
||||
|
||||
if arr is None: |
||||
out = {} |
||||
else: |
||||
out = [None]*len(arr) |
||||
|
||||
msg = self.msgs.get(x[0]) |
||||
if msg is None: |
||||
if x[0] not in self._warned_addresses: |
||||
#print("WARNING: Unknown message address {}".format(x[0])) |
||||
self._warned_addresses.add(x[0]) |
||||
return None, None |
||||
|
||||
name = msg[0][0] |
||||
if debug: |
||||
print(name) |
||||
|
||||
st = x[2].ljust(8, b'\x00') |
||||
le, be = None, None |
||||
|
||||
for s in msg[1]: |
||||
if arr is not None and s[0] not in arr: |
||||
continue |
||||
|
||||
start_bit = s[1] |
||||
signal_size = s[2] |
||||
little_endian = s[3] |
||||
signed = s[4] |
||||
factor = s[5] |
||||
offset = s[6] |
||||
|
||||
if little_endian: |
||||
if le is None: |
||||
le = struct.unpack("<Q", st)[0] |
||||
tmp = le |
||||
shift_amount = start_bit |
||||
else: |
||||
if be is None: |
||||
be = struct.unpack(">Q", st)[0] |
||||
tmp = be |
||||
b1 = (start_bit // 8) * 8 + (-start_bit - 1) % 8 |
||||
shift_amount = 64 - (b1 + signal_size) |
||||
|
||||
if shift_amount < 0: |
||||
continue |
||||
|
||||
tmp = (tmp >> shift_amount) & ((1 << signal_size) - 1) |
||||
if signed and (tmp >> (signal_size - 1)): |
||||
tmp -= (1 << signal_size) |
||||
|
||||
tmp = tmp * factor + offset |
||||
|
||||
# if debug: |
||||
# print("%40s %2d %2d %7.2f %s" % (s[0], s[1], s[2], tmp, s[-1])) |
||||
|
||||
if arr is None: |
||||
out[s[0]] = tmp |
||||
else: |
||||
out[arr.index(s[0])] = tmp |
||||
return name, out |
||||
|
||||
def get_signals(self, msg): |
||||
msg = self.lookup_msg_id(msg) |
||||
return [sgs.name for sgs in self.msgs[msg][1]] |
||||
|
||||
|
||||
if __name__ == "__main__": |
||||
from opendbc import DBC_PATH |
||||
import numpy as np |
||||
|
||||
dbc_test = dbc(os.path.join(DBC_PATH, 'toyota_prius_2017_pt_generated.dbc')) |
||||
msg = ('STEER_ANGLE_SENSOR', {'STEER_ANGLE': -6.0, 'STEER_RATE': 4, 'STEER_FRACTION': -0.2}) |
||||
encoded = dbc_test.encode(*msg) |
||||
decoded = dbc_test.decode((0x25, 0, encoded)) |
||||
assert decoded == msg |
||||
|
||||
dbc_test = dbc(os.path.join(DBC_PATH, 'hyundai_santa_fe_2019_ccan.dbc')) |
||||
decoded = dbc_test.decode((0x2b0, 0, "\xfa\xfe\x00\x07\x12")) |
||||
assert np.isclose(decoded[1]['SAS_Angle'], -26.2) |
||||
|
||||
msg = ('SAS11', {'SAS_Stat': 7.0, 'MsgCount': 0.0, 'SAS_Angle': -26.200000000000003, 'SAS_Speed': 0.0, 'CheckSum': 0.0}) |
||||
encoded = dbc_test.encode(*msg) |
||||
decoded = dbc_test.decode((0x2b0, 0, encoded)) |
||||
|
||||
assert decoded == msg |
@ -1,10 +0,0 @@ |
||||
all: simple_kalman_impl.so |
||||
|
||||
simple_kalman_impl.so: simple_kalman_impl.pyx simple_kalman_impl.pxd simple_kalman_setup.py |
||||
python3 simple_kalman_setup.py build_ext --inplace
|
||||
rm -rf build
|
||||
rm simple_kalman_impl.c
|
||||
|
||||
.PHONY: clean |
||||
clean: |
||||
rm -f simple_kalman_impl.so
|
@ -0,0 +1,6 @@ |
||||
Import('env') |
||||
|
||||
env.Command(['simple_kalman_impl.so'], |
||||
['simple_kalman_impl.pyx', 'simple_kalman_impl.pxd', 'simple_kalman_setup.py'], |
||||
"cd common/kalman && python3 simple_kalman_setup.py build_ext --inplace") |
||||
|
@ -1,9 +1,3 @@ |
||||
# pylint: skip-file |
||||
import os |
||||
import subprocess |
||||
|
||||
kalman_dir = os.path.dirname(os.path.abspath(__file__)) |
||||
subprocess.check_call(["make", "simple_kalman_impl.so"], cwd=kalman_dir) |
||||
|
||||
from .simple_kalman_impl import KF1D as KF1D |
||||
from common.kalman.simple_kalman_impl import KF1D as KF1D |
||||
assert KF1D |
||||
|
@ -1,7 +1,7 @@ |
||||
{ |
||||
"ota_url": "https://commadist.azureedge.net/neosupdate/ota-signed-7a0117425bc4a6673958d265312994e124654a566228f3cec2f0f9bc8120a9ab.zip", |
||||
"ota_hash": "7a0117425bc4a6673958d265312994e124654a566228f3cec2f0f9bc8120a9ab", |
||||
"recovery_url": "https://commadist.azureedge.net/neosupdate/recovery-3dc234d868c29a3739f6ca3bd47b1c2d3c570d9f478b6849a4fada129ee4af76.img", |
||||
"ota_url": "https://commadist.azureedge.net/neosupdate/ota-signed-07df505453684371b6c22583ffbb74ee414fcd389a46ff369ffd1b6bac75414e.zip", |
||||
"ota_hash": "07df505453684371b6c22583ffbb74ee414fcd389a46ff369ffd1b6bac75414e", |
||||
"recovery_url": "https://commadist.azureedge.net/neosupdate/recovery-3a6f973295ded6e4ff5cfff3b12e19c80d3bf45e2e8dd8699da3fc25b23ed7c6.img", |
||||
"recovery_len": 15848748, |
||||
"recovery_hash": "3dc234d868c29a3739f6ca3bd47b1c2d3c570d9f478b6849a4fada129ee4af76" |
||||
"recovery_hash": "3a6f973295ded6e4ff5cfff3b12e19c80d3bf45e2e8dd8699da3fc25b23ed7c6" |
||||
} |
||||
|
@ -1,3 +1,3 @@ |
||||
version https://git-lfs.github.com/spec/v1 |
||||
oid sha256:28d88850097f33d58fac5ed95d41c8336fdc054832f97ce3937e56e29e4796bb |
||||
size 16950543 |
||||
oid sha256:53948734c96a5aee45fcaed9fd6191056328ed3467dcd3d40d25f310d38c2297 |
||||
size 15207664 |
||||
|
@ -1,3 +0,0 @@ |
||||
version https://git-lfs.github.com/spec/v1 |
||||
oid sha256:e6ea68609a1477fb55e27b066af5c65f343b930eaf8ade38d91df68ec594c5a7 |
||||
size 158713 |
@ -0,0 +1,3 @@ |
||||
version https://git-lfs.github.com/spec/v1 |
||||
oid sha256:7f6305c44138a387e1757ed30a9bb6487d35c8919d0ae258998acb0e74b584e1 |
||||
size 186615 |
Binary file not shown.
@ -0,0 +1,3 @@ |
||||
version https://git-lfs.github.com/spec/v1 |
||||
oid sha256:a5934a6cb5c1dd3031adb8eccee3c6419c7c966882c02dd978e772130c8f397e |
||||
size 446 |
@ -0,0 +1,3 @@ |
||||
version https://git-lfs.github.com/spec/v1 |
||||
oid sha256:888d6979bad138089fa6e8f720e5b61a47f2892f746438a5731853464ea02938 |
||||
size 9280 |
@ -0,0 +1,3 @@ |
||||
version https://git-lfs.github.com/spec/v1 |
||||
oid sha256:4f877e5ae4672568ef82cfd0023e2cef4a7cf55d867ab249efc9569a7eb9e5b1 |
||||
size 1516 |
@ -0,0 +1,3 @@ |
||||
version https://git-lfs.github.com/spec/v1 |
||||
oid sha256:8ceb4b9ee5adedde47b31e975c1d90c73ad27b6b165a1dcd80c7c545eb65b903 |
||||
size 35147 |
@ -0,0 +1,3 @@ |
||||
version https://git-lfs.github.com/spec/v1 |
||||
oid sha256:dc626520dcd53a22f727af3ee42c770e56c97a64fe3adb063799d8ab032fe551 |
||||
size 26530 |
@ -0,0 +1,3 @@ |
||||
version https://git-lfs.github.com/spec/v1 |
||||
oid sha256:f5b330efdad110cdd84d585ec61220b0650461fa599e36b13e1726c9346dcfb9 |
||||
size 2246 |
@ -0,0 +1,3 @@ |
||||
version https://git-lfs.github.com/spec/v1 |
||||
oid sha256:fab3dd6bdab226f1c08630b1dd917e11fcb4ec5e1e020e2c16f83a0a13863e85 |
||||
size 16726 |
@ -0,0 +1,3 @@ |
||||
version https://git-lfs.github.com/spec/v1 |
||||
oid sha256:c83230b770f17ef1386ea1fd3681271dd98aa93646bdbfb5bff3a1b7050fff9d |
||||
size 779 |
@ -0,0 +1,3 @@ |
||||
version https://git-lfs.github.com/spec/v1 |
||||
oid sha256:e99debb6c24423664cf610d53fab092eeab1ec23076b966476ddfe73af120202 |
||||
size 694 |
@ -0,0 +1,3 @@ |
||||
version https://git-lfs.github.com/spec/v1 |
||||
oid sha256:acfe8307dc7a4732359166d24c39f3dec6a63f1534f010557851cf54b342fad8 |
||||
size 1129 |
@ -0,0 +1,3 @@ |
||||
version https://git-lfs.github.com/spec/v1 |
||||
oid sha256:26f0d1bd0677785394f1f4eb2cddf763423f703c0941b3e58373e955b0130327 |
||||
size 1900 |
@ -0,0 +1,3 @@ |
||||
version https://git-lfs.github.com/spec/v1 |
||||
oid sha256:80114769016320aac2e10fe4f80aa264ae63d5c98dc15913092ab01cee8c9956 |
||||
size 17361 |
@ -0,0 +1,3 @@ |
||||
version https://git-lfs.github.com/spec/v1 |
||||
oid sha256:5325c9d4d693e504ee012c5275ad4b10b135a701cbd87c14466d2ea21ea22ee5 |
||||
size 122 |
@ -0,0 +1,3 @@ |
||||
version https://git-lfs.github.com/spec/v1 |
||||
oid sha256:b0473b5d7c0cd9311d4a90548162e8cf160904476871f7fb749e9624e9b3dfd1 |
||||
size 35 |
@ -0,0 +1,3 @@ |
||||
version https://git-lfs.github.com/spec/v1 |
||||
oid sha256:a4d5cac960365e774da86d73410c4091d982fdc7ff7e63360e19b19985f1c18b |
||||
size 1763 |
@ -0,0 +1,3 @@ |
||||
version https://git-lfs.github.com/spec/v1 |
||||
oid sha256:31e95666fedc8e0a421f5c0e213d0916eae85ca4b427512ee124a0d717671a49 |
||||
size 2050 |
@ -0,0 +1,3 @@ |
||||
version https://git-lfs.github.com/spec/v1 |
||||
oid sha256:fae2c45c354729a373934f5104602458dbb38b3b6e61f58ef5b156b7babab749 |
||||
size 874 |
@ -0,0 +1,3 @@ |
||||
version https://git-lfs.github.com/spec/v1 |
||||
oid sha256:84ffa38ac1ef3be005bff500b78b529c1e164fc50637836ff8648fea127b74f7 |
||||
size 2083 |
@ -0,0 +1,3 @@ |
||||
version https://git-lfs.github.com/spec/v1 |
||||
oid sha256:c48a6074e2e65b3648dd8671574a74cee253d8e2b94866f5410f124a55589232 |
||||
size 939 |
@ -0,0 +1,3 @@ |
||||
version https://git-lfs.github.com/spec/v1 |
||||
oid sha256:ca8ab01c365777a73a7ca80de3f0a27a174d423ac5ae41e8f3face19ae38fc5f |
||||
size 1374 |
@ -0,0 +1,3 @@ |
||||
version https://git-lfs.github.com/spec/v1 |
||||
oid sha256:e46d51d3065962408a403480f2c9cad4e7d61fb509c1e4a695274751274969a2 |
||||
size 991 |
@ -0,0 +1,3 @@ |
||||
version https://git-lfs.github.com/spec/v1 |
||||
oid sha256:9a298753f1bc00c6591cf3d8faf2053ac9b0c8d362e71ba2136504ca8cbd8d9e |
||||
size 2483 |
@ -0,0 +1,3 @@ |
||||
version https://git-lfs.github.com/spec/v1 |
||||
oid sha256:11485398858b54ffc195cc51d31543fc65fea79a8cd901ef95da405ce790e370 |
||||
size 1676 |
@ -0,0 +1,3 @@ |
||||
version https://git-lfs.github.com/spec/v1 |
||||
oid sha256:50ec5003df399fabf518620e220f71c717ff3a03a7218b8650a974dc3ad06b15 |
||||
size 1116 |
@ -0,0 +1,3 @@ |
||||
version https://git-lfs.github.com/spec/v1 |
||||
oid sha256:a63a1d392e01ca8f1dda6573c37a4566ee10b39402df38a2463bc4415193d5ef |
||||
size 1258 |
@ -0,0 +1,3 @@ |
||||
version https://git-lfs.github.com/spec/v1 |
||||
oid sha256:6e8424527e5f68f1ddefe36a892908600601b1e68c4a2735ff256e9c038aa259 |
||||
size 940 |
@ -0,0 +1,3 @@ |
||||
version https://git-lfs.github.com/spec/v1 |
||||
oid sha256:4fa2d73b998c0794682d63dffbfff795bea41a7d18665840b1c357e66ff1c89d |
||||
size 1162 |
@ -0,0 +1,3 @@ |
||||
version https://git-lfs.github.com/spec/v1 |
||||
oid sha256:7e30c5ec54f305c75f16bb3f92204de44599126fd1d756baf94675a8f7a6b800 |
||||
size 1570 |
@ -0,0 +1,3 @@ |
||||
version https://git-lfs.github.com/spec/v1 |
||||
oid sha256:fb92e4b1c0b85d3e9292d064b1b1700bf1d488d4905c64aa23b9e40feda75478 |
||||
size 919 |
@ -0,0 +1,3 @@ |
||||
version https://git-lfs.github.com/spec/v1 |
||||
oid sha256:7fefd6f6aa714052d1a67ef9f598d47bdfa2e21f237c820c4c0e37d013b49229 |
||||
size 1371 |
@ -0,0 +1,3 @@ |
||||
version https://git-lfs.github.com/spec/v1 |
||||
oid sha256:2323c1e3179dfc6abf348161b78c1bf5ecd0caa8f452c70caca9a1cdfdd6a6ae |
||||
size 2240 |
@ -0,0 +1,3 @@ |
||||
version https://git-lfs.github.com/spec/v1 |
||||
oid sha256:cf9296fbb3c7d68329013fdc7464e1d696d1aeae9c778d9e06d370d108fad9a2 |
||||
size 1713 |
@ -0,0 +1,3 @@ |
||||
version https://git-lfs.github.com/spec/v1 |
||||
oid sha256:9756bad84167491b44d9990613ea2e889857eeb54ffca50d590410846f5c67d2 |
||||
size 1222 |
@ -0,0 +1,3 @@ |
||||
version https://git-lfs.github.com/spec/v1 |
||||
oid sha256:c1885167a447825ab7c35573422c3efa23dec62f5b630ac322ffe658e0ee5242 |
||||
size 797 |
@ -0,0 +1,3 @@ |
||||
version https://git-lfs.github.com/spec/v1 |
||||
oid sha256:03540e297ae96ed03e828d9a433bb7f5a5e93aa83c6f50a6c271c78f9283d667 |
||||
size 726 |
@ -0,0 +1,3 @@ |
||||
version https://git-lfs.github.com/spec/v1 |
||||
oid sha256:7e8b1060757a1140298ff3905df70edb59ad1083f505681df30dc9dc9963d8ab |
||||
size 803 |
@ -0,0 +1,3 @@ |
||||
version https://git-lfs.github.com/spec/v1 |
||||
oid sha256:8aa601f3fa1e6bfc03b799666bf39ebc42d44917a5fb2aaa489beafd63901f72 |
||||
size 2243 |
@ -0,0 +1,3 @@ |
||||
version https://git-lfs.github.com/spec/v1 |
||||
oid sha256:003d41fa08cde68b1c4698499fa9bf9c803cebb27ae748615851519f42dc56ac |
||||
size 1382 |
@ -0,0 +1,3 @@ |
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:8747c66f7ea0535fa3a3944554ac1395499be9d63b1cd2341163e5a4de96fce5 |
||||
size 24254 |
@ -0,0 +1,3 @@ |
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:faf39aa4957943b68e735a3f7905a0461daaeefd36594ba44e8e40960b15cc7a |
||||
size 17834 |
@ -0,0 +1,3 @@ |
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:1efc3720834ec96ba0df4a0622aa20c3a80ae48d90d60cfaad5e0bf9c1cfa04b |
||||
size 3974 |
@ -0,0 +1,3 @@ |
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:eec5b84c2c576530670fe2607d8979811025eef44eedbca03f56bbbdc777f9ae |
||||
size 22307 |
@ -0,0 +1,3 @@ |
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:9639ee6c3918b5dfacd9db51169972a9cfd654266ecb0ad6ff5159b69a785660 |
||||
size 11944 |
@ -0,0 +1,3 @@ |
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:8b0df9645c11e3aa3479ea789f0ff8c34e993631c6b7bde6a130d98af30f6eb7 |
||||
size 8107 |
@ -0,0 +1,3 @@ |
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:fa56fc43ceede869132d9e9d6e925468d4605c193ad435e5f20b9f52920cdb0d |
||||
size 6571 |
@ -0,0 +1,3 @@ |
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:3dfc6a05fef83bb7743f3d45e8028cccdff4010a5d516396c6087a45529e694e |
||||
size 2720 |
@ -0,0 +1,3 @@ |
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:648105b4058d1abc31843792cacd208061fd8e79c6f896bea62820bc52a289ae |
||||
size 38120 |
@ -0,0 +1,3 @@ |
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:7f965654d06dc2888bcb098b50eff171b0f3697cda6a432c1da7f6cc13ded862 |
||||
size 12221 |
@ -0,0 +1,3 @@ |
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:bcad8b0d59a56e9f3d4f39ee6396e27cf4143d24fbd24c91fab7e03c7415c564 |
||||
size 13910 |
@ -0,0 +1,3 @@ |
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:551858019e0ae5a66ef5638036f18124c18033b3cfe04ba7c4c934d15ebbc747 |
||||
size 18064 |
@ -0,0 +1,3 @@ |
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:a1f6b1837c81ba9056b1c73e91723282b5fd6103a38218013411d057272b2e27 |
||||
size 4249 |
@ -0,0 +1,3 @@ |
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:e21f93bef2476ffc5362644cfc669b24f94da5cf44870e2c140ffdfb93e76d59 |
||||
size 5689 |
@ -0,0 +1,3 @@ |
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:5d44345e7ccf669ba2b343f09ce32c08cd8ed04f2c40dc9a0de88daee5f83041 |
||||
size 6970 |
@ -0,0 +1,3 @@ |
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:4a20c03255610ed4c885231e39c9419d359161343b673511ba1640b86791727d |
||||
size 61293 |
@ -0,0 +1,3 @@ |
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:9aa4f2f822089d478d0ae3277716c204e3035a6bd0edd50cf3942f99342c7f91 |
||||
size 4525 |
@ -0,0 +1,3 @@ |
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:6c3a41fbda9a5c2af43a627468d4e66fdac13fc9586867a233d22e1bd764d47d |
||||
size 7593 |
@ -0,0 +1,3 @@ |
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:5531aa255cf9e26963fac152b8692ebb4b29219049ab080edd8ae8e0e5157c92 |
||||
size 30704 |
@ -0,0 +1,3 @@ |
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:97d9e03c73e1a101352d8b00ee191013b6b6acf00e90c887dd54bea4551de7be |
||||
size 8256 |
@ -0,0 +1,3 @@ |
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:16a8224c1758e79ca01692110137ee5cfeccdf8018020571569d1102cf0e5616 |
||||
size 3877 |
@ -0,0 +1,3 @@ |
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:140671a011363e16bc49eca3c9ffc808f9fcf7569e45f8580d320e2023d2037c |
||||
size 5282 |
@ -0,0 +1,3 @@ |
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:3e51c22c1080077da6f2256c3edea062464a1c93bebe7518a8dd77bc7aa746e2 |
||||
size 27367 |
@ -0,0 +1,3 @@ |
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:5e7f54eddcb464a1863e5bfb1b1060bbede58cdca60a720a3244d46844252b03 |
||||
size 24212 |
@ -0,0 +1,3 @@ |
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:0f42190688b6a803a9094344c729c9c220939274e38f0c69ecd28702965b1a8b |
||||
size 21959 |
@ -0,0 +1,3 @@ |
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:b876cccf1671adf2dbb2149961d4bc28fcdc5f152d55df952611611976e8d0d4 |
||||
size 9507 |
@ -0,0 +1,3 @@ |
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:e3cdb9bedbf570b230d8f19f3cfbb58f3342e6fc4407bb0ea24397ca094fb6c0 |
||||
size 12666 |
@ -0,0 +1,3 @@ |
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:14712906425a63d11c226826ed588553756a055bb8c72a73788eadc23c3320d8 |
||||
size 970 |
@ -0,0 +1,3 @@ |
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:611b37c1cad422d5c0e152cc0812405816196dd80fc52513879786fac5a43662 |
||||
size 11392 |
@ -0,0 +1,3 @@ |
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:a88d0ccb46e1c902a4fa66f4be3ad20aa76eb2a1b784a1b3e4c25dfdc85e1853 |
||||
size 5540 |
@ -0,0 +1,3 @@ |
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:179a8ebf0fdba78414a9e01b130db5fbf78dc5d1c7fd48c5d49dcc7ccca4aaff |
||||
size 4769 |
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in new issue