commit
ca72ba8af5
531 changed files with 29349 additions and 11827 deletions
@ -1,3 +0,0 @@ |
|||||||
[run] |
|
||||||
concurrency=multiprocessing |
|
||||||
|
|
@ -1,26 +1,31 @@ |
|||||||
--- |
--- |
||||||
name: Bug report |
name: Bug report |
||||||
about: Create a report to help us improve openpilot |
about: For issues with running openpilot on your comma device |
||||||
title: '' |
title: '' |
||||||
labels: '' |
labels: 'bug' |
||||||
assignees: '' |
assignees: '' |
||||||
|
|
||||||
--- |
--- |
||||||
|
|
||||||
**Describe the bug** |
**Describe the bug** |
||||||
A clear and concise description of what the bug is. |
|
||||||
|
<!-- A clear and concise description of what the bug is. Add the `car bug` label for vehicle/brand specific bugs and the `bug` label for all other bugs. --> |
||||||
|
|
||||||
**How to reproduce or log data** |
**How to reproduce or log data** |
||||||
Steps to reproduce the behavior, or a explorer/cabana link to the exact drive and timestamp of when the bug occurred. |
|
||||||
|
<!-- Steps to reproduce the behavior. --> |
||||||
|
|
||||||
**Expected behavior** |
**Expected behavior** |
||||||
A clear and concise description of what you expected to happen. |
|
||||||
|
<!-- A clear and concise description of what you expected to happen. --> |
||||||
|
|
||||||
**Device/Version information (please complete the following information):** |
**Device/Version information (please complete the following information):** |
||||||
- Device: [e.g. EON/EON Gold] |
- Device: [e.g. EON/EON Gold/comma two] |
||||||
- Dongle ID: [e.g. 77611a1fac303767, can be found in Settings -> Device -> Dongle ID] |
- Dongle ID: [e.g. 77611a1fac303767, can be found in Settings -> Device -> Dongle ID or my.comma.ai/useradmin] |
||||||
- Version: [e.g. 0.6.4], or commit hash when on devel |
- Route: [e.g. 77611a1fac303767|2020-05-11--16-37-07, can be found in my.comma.ai/useradmin] |
||||||
- Car make/model [e.g. Toyota Prius 2016] |
- Timestamp: [When in the route the bug occurs (e.g. 4min 30s into the drive)] |
||||||
|
- Version: [commit hash when on a non-release branch, or version number when on devel or release2 (e.g. 0.7.6)] |
||||||
|
- Car make/model: [e.g. Toyota Prius 2016] |
||||||
|
|
||||||
**Additional context** |
**Additional context** |
||||||
Add any other context about the problem here. |
|
||||||
|
<!-- Add any other context about the problem here. --> |
||||||
|
@ -0,0 +1,25 @@ |
|||||||
|
--- |
||||||
|
name: PC Bug report |
||||||
|
about: For issues with running openpilot on PC |
||||||
|
title: '' |
||||||
|
labels: 'PC' |
||||||
|
assignees: '' |
||||||
|
--- |
||||||
|
|
||||||
|
**Describe the bug** |
||||||
|
|
||||||
|
<!-- A clear and concise description of what the bug is. Add the `simulation` label if running in an environment like CARLA. --> |
||||||
|
|
||||||
|
**How to reproduce or log data** |
||||||
|
|
||||||
|
<!-- Steps to reproduce the behavior. --> |
||||||
|
|
||||||
|
**Expected behavior** |
||||||
|
|
||||||
|
<!-- A clear and concise description of what you expected to happen. --> |
||||||
|
|
||||||
|
**Additional context** |
||||||
|
|
||||||
|
<!-- Add any other context about the problem here. --> |
||||||
|
|
||||||
|
Operating system: [e.g. Ubuntu 16.04] |
@ -0,0 +1,8 @@ |
|||||||
|
blank_issues_enabled: false |
||||||
|
contact_links: |
||||||
|
- name: Community Wiki |
||||||
|
url: https://github.com/commaai/openpilot/wiki |
||||||
|
about: Check out our community wiki |
||||||
|
- name: Community Discord |
||||||
|
urli: https://discord.comma.ai |
||||||
|
about: Check out our community discord |
@ -0,0 +1,8 @@ |
|||||||
|
--- |
||||||
|
name: Enhancement |
||||||
|
about: For suggestions for openpilot enhancements |
||||||
|
title: '' |
||||||
|
labels: 'enhancement' |
||||||
|
assignees: '' |
||||||
|
--- |
||||||
|
|
@ -0,0 +1,17 @@ |
|||||||
|
--- |
||||||
|
name: Question |
||||||
|
about: For questions about openpilot |
||||||
|
title: '' |
||||||
|
labels: 'question' |
||||||
|
assignees: '' |
||||||
|
--- |
||||||
|
|
||||||
|
<!-- |
||||||
|
|
||||||
|
Consider these options before opening an issue for a question: |
||||||
|
|
||||||
|
- checking the FAQ at https://comma.ai/faq |
||||||
|
- checking the wiki at https://wiki.comma.ai |
||||||
|
- asking your question on our community discord at https://discord.comma.ai |
||||||
|
|
||||||
|
--> |
@ -0,0 +1,15 @@ |
|||||||
|
--- |
||||||
|
name: Bug fix |
||||||
|
about: For openpilot bug fixes |
||||||
|
title: '' |
||||||
|
labels: 'bugfix' |
||||||
|
assignees: '' |
||||||
|
--- |
||||||
|
|
||||||
|
**Description** |
||||||
|
|
||||||
|
<!-- A description of the bug and the fix. Also link the issue if it exists. --> |
||||||
|
|
||||||
|
**Verification** |
||||||
|
|
||||||
|
<!-- Explain how you tested this bug fix. --> |
@ -0,0 +1,19 @@ |
|||||||
|
--- |
||||||
|
name: Car Bug fix |
||||||
|
about: For vehicle/brand specifc bug fixes |
||||||
|
title: '' |
||||||
|
labels: 'car bug fix' |
||||||
|
assignees: '' |
||||||
|
--- |
||||||
|
|
||||||
|
**Description** |
||||||
|
|
||||||
|
<!-- A description of the bug and the fix. Also link the issue if it exists. --> |
||||||
|
|
||||||
|
**Verification** |
||||||
|
|
||||||
|
<!-- Explain how you tested this bug fix. --> |
||||||
|
|
||||||
|
**Route** |
||||||
|
|
||||||
|
Route: [a route with the bug fix] |
@ -0,0 +1,14 @@ |
|||||||
|
--- |
||||||
|
name: Car port |
||||||
|
about: For new car ports |
||||||
|
title: '' |
||||||
|
labels: 'car port' |
||||||
|
assignees: '' |
||||||
|
--- |
||||||
|
|
||||||
|
**Checklist** |
||||||
|
|
||||||
|
- [ ] added to README |
||||||
|
- [ ] test route added to [test_car_models](../../selfdrive/test/test_car_models.py) |
||||||
|
- [ ] route with openpilot: |
||||||
|
- [ ] route with stock system: |
@ -0,0 +1,11 @@ |
|||||||
|
--- |
||||||
|
name: Fingerprint |
||||||
|
about: For adding fingerprints to existing cars |
||||||
|
title: '' |
||||||
|
labels: 'fingerprint' |
||||||
|
assignees: '' |
||||||
|
--- |
||||||
|
|
||||||
|
Discord username: [] |
||||||
|
|
||||||
|
Route: [] |
@ -0,0 +1,15 @@ |
|||||||
|
--- |
||||||
|
name: Refactor |
||||||
|
about: For code refactors |
||||||
|
title: '' |
||||||
|
labels: 'refactor' |
||||||
|
assignees: '' |
||||||
|
--- |
||||||
|
|
||||||
|
**Description** |
||||||
|
|
||||||
|
<!-- A description of the refactor, including the goals it accomplishes. --> |
||||||
|
|
||||||
|
**Verification** |
||||||
|
|
||||||
|
<!-- Explain how you tested the refactor for regressions. --> |
@ -0,0 +1,8 @@ |
|||||||
|
version: 2 |
||||||
|
updates: |
||||||
|
- package-ecosystem: pip |
||||||
|
directory: "/" |
||||||
|
schedule: |
||||||
|
interval: daily |
||||||
|
time: '15:00' |
||||||
|
open-pull-requests-limit: 10 |
@ -0,0 +1,38 @@ |
|||||||
|
<!-- Please copy and paste the relevant template --> |
||||||
|
|
||||||
|
<!--- ***** Template: Car bug fix ***** |
||||||
|
|
||||||
|
**Description** [](A description of the bug and the fix. Also link any relevant issues.) |
||||||
|
|
||||||
|
**Verification** [](Explain how you tested this bug fix.) |
||||||
|
|
||||||
|
**Route** |
||||||
|
Route: [a route with the bug fix] |
||||||
|
|
||||||
|
--> |
||||||
|
|
||||||
|
<!--- ***** Template: Bug fix ***** |
||||||
|
|
||||||
|
**Description** [](A description of the bug and the fix. Also link any relevant issues.) |
||||||
|
|
||||||
|
**Verification** [](Explain how you tested this bug fix.) |
||||||
|
|
||||||
|
--> |
||||||
|
|
||||||
|
<!--- ***** Template: Car port ***** |
||||||
|
|
||||||
|
**Checklist** |
||||||
|
- [ ] added to README |
||||||
|
- [ ] test route added to [test_car_models](../../selfdrive/test/test_car_models.py) |
||||||
|
- [ ] route with openpilot: |
||||||
|
- [ ] route with stock system: |
||||||
|
|
||||||
|
--> |
||||||
|
|
||||||
|
<!--- ***** Template: Refactor ***** |
||||||
|
|
||||||
|
**Description** [](A description of the refactor, including the goals it accomplishes.) |
||||||
|
|
||||||
|
**Verification** [](Explain how you tested the refactor for regressions.) |
||||||
|
|
||||||
|
--> |
@ -0,0 +1,23 @@ |
|||||||
|
name: "Update Pipfile.lock" |
||||||
|
on: |
||||||
|
schedule: |
||||||
|
- cron: '00 15 * * 1' # Every monday on 15:00 UTC |
||||||
|
|
||||||
|
jobs: |
||||||
|
piplock: |
||||||
|
runs-on: ubuntu-latest |
||||||
|
steps: |
||||||
|
- uses: actions/checkout@v2 |
||||||
|
- uses: actions/setup-python@v2 |
||||||
|
- run: pip install wheel |
||||||
|
- run: pip install pipenv |
||||||
|
- run: pipenv lock |
||||||
|
- uses: actions/upload-artifact@v2 |
||||||
|
with: |
||||||
|
name: "Pipfile lock" |
||||||
|
path: Pipfile.lock |
||||||
|
- uses: peter-evans/create-pull-request@v2 |
||||||
|
with: |
||||||
|
title: "Update Pipfile.lock (dependencies)" |
||||||
|
branch: update-pipfile |
||||||
|
commit-message: "[Bot] Update Pipfile.lock dependencies" |
@ -0,0 +1,47 @@ |
|||||||
|
repos: |
||||||
|
- repo: https://github.com/pre-commit/pre-commit-hooks |
||||||
|
rev: master |
||||||
|
hooks: |
||||||
|
- id: check-ast |
||||||
|
- id: check-json |
||||||
|
- id: check-xml |
||||||
|
- id: check-yaml |
||||||
|
- id: check-merge-conflict |
||||||
|
- id: check-symlinks |
||||||
|
- repo: https://github.com/pre-commit/mirrors-mypy |
||||||
|
rev: master |
||||||
|
hooks: |
||||||
|
- id: mypy |
||||||
|
exclude: '^(pyextra)|(external)|(cereal)|(rednose)|(panda)|(laika)|(opendbc)|(laika_repo)|(rednose_repo)/' |
||||||
|
additional_dependencies: ['git+https://github.com/numpy/numpy-stubs'] |
||||||
|
- repo: https://github.com/PyCQA/flake8 |
||||||
|
rev: master |
||||||
|
hooks: |
||||||
|
- id: flake8 |
||||||
|
exclude: '^(pyextra)|(external)|(cereal)|(rednose)|(panda)|(laika)|(opendbc)|(laika_repo)|(rednose_repo)|(selfdrive/debug)/' |
||||||
|
args: |
||||||
|
- --select=F,E112,E113,E304,E501,E502,E701,E702,E703,E71,E72,E731,W191,W6 |
||||||
|
- --max-line-length=240 |
||||||
|
- --statistics |
||||||
|
- repo: local |
||||||
|
hooks: |
||||||
|
- id: pylint |
||||||
|
name: pylint |
||||||
|
entry: pylint |
||||||
|
language: system |
||||||
|
types: [python] |
||||||
|
exclude: '^(pyextra)|(external)|(cereal)|(rednose)|(panda)|(laika)|(laika_repo)|(rednose_repo)/' |
||||||
|
- repo: local |
||||||
|
hooks: |
||||||
|
- id: cppcheck |
||||||
|
name: cppcheck |
||||||
|
entry: cppcheck |
||||||
|
language: system |
||||||
|
types: [c++] |
||||||
|
exclude: '^(phonelibs)|(external)|(cereal)|(opendbc)|(panda)|(tools)|(selfdrive/modeld/thneed/debug)|(selfdrive/modeld/test)|(selfdrive/camerad/test)/|(installer)' |
||||||
|
args: |
||||||
|
- --error-exitcode=1 |
||||||
|
- --language=c++ |
||||||
|
- --quiet |
||||||
|
- --force |
||||||
|
- -j8 |
File diff suppressed because it is too large
Load Diff
@ -1,3 +1,3 @@ |
|||||||
version https://git-lfs.github.com/spec/v1 |
version https://git-lfs.github.com/spec/v1 |
||||||
oid sha256:d7b79038dccaa97d84bd38544573d3a52929770b76a259b25a27311464230e22 |
oid sha256:a198491887ed6029bffdf7f4dc28c4f9a6ba5f9d2235710fc11a1378893491d7 |
||||||
size 13732809 |
size 13702777 |
||||||
|
@ -1 +0,0 @@ |
|||||||
Subproject commit f5d2c1715c9482d898062110ce4c612093aa5d4f |
|
@ -1,6 +1,6 @@ |
|||||||
Import('env') |
Import('env', 'cython_dependencies') |
||||||
|
|
||||||
# parser |
# Build cython clock module |
||||||
env.Command(['common_pyx.so'], |
env.Command(['common_pyx.so', 'clock.cpp'], |
||||||
['common_pyx_setup.py', 'clock.pyx'], |
cython_dependencies + ['common_pyx_setup.py', 'clock.pyx'], |
||||||
"cd common && python3 common_pyx_setup.py build_ext --inplace") |
"cd common && python3 common_pyx_setup.py build_ext --inplace") |
||||||
|
@ -1,3 +0,0 @@ |
|||||||
# py2,3 compatiblity helpers |
|
||||||
|
|
||||||
basestring = (str, bytes) |
|
@ -1,6 +1,6 @@ |
|||||||
Import('env') |
Import('env', 'cython_dependencies') |
||||||
|
|
||||||
env.Command(['simple_kalman_impl.so'], |
env.Command(['simple_kalman_impl.so'], |
||||||
['simple_kalman_impl.pyx', 'simple_kalman_impl.pxd', 'simple_kalman_setup.py'], |
cython_dependencies + ['simple_kalman_impl.pyx', 'simple_kalman_impl.pxd', 'simple_kalman_setup.py'], |
||||||
"cd common/kalman && python3 simple_kalman_setup.py build_ext --inplace") |
"cd common/kalman && python3 simple_kalman_setup.py build_ext --inplace") |
||||||
|
|
||||||
|
@ -1,50 +0,0 @@ |
|||||||
def cputime_total(ct): |
|
||||||
return ct.cpuUser + ct.cpuSystem + ct.cpuChildrenUser + ct.cpuChildrenSystem |
|
||||||
|
|
||||||
|
|
||||||
def print_cpu_usage(first_proc, last_proc): |
|
||||||
r = 0 |
|
||||||
procs = [ |
|
||||||
("selfdrive.controls.controlsd", 59.46), |
|
||||||
("./_modeld", 48.94), |
|
||||||
("./loggerd", 28.49), |
|
||||||
("selfdrive.controls.plannerd", 19.77), |
|
||||||
("selfdrive.controls.radard", 9.54), |
|
||||||
("./_ui", 9.54), |
|
||||||
("./camerad", 7.07), |
|
||||||
("selfdrive.locationd.locationd", 7.13), |
|
||||||
("./_sensord", 6.17), |
|
||||||
("selfdrive.controls.dmonitoringd", 5.48), |
|
||||||
("./boardd", 3.63), |
|
||||||
("./_dmonitoringmodeld", 2.67), |
|
||||||
("selfdrive.logmessaged", 2.71), |
|
||||||
("selfdrive.thermald", 2.41), |
|
||||||
("./paramsd", 2.18), |
|
||||||
("selfdrive.locationd.calibrationd", 1.76), |
|
||||||
("./proclogd", 1.54), |
|
||||||
("./_gpsd", 0.09), |
|
||||||
("./clocksd", 0.02), |
|
||||||
("./ubloxd", 0.02), |
|
||||||
("selfdrive.tombstoned", 0), |
|
||||||
("./logcatd", 0), |
|
||||||
("selfdrive.updated", 0), |
|
||||||
] |
|
||||||
|
|
||||||
dt = (last_proc.logMonoTime - first_proc.logMonoTime) / 1e9 |
|
||||||
print("------------------------------------------------") |
|
||||||
for proc_name, normal_cpu_usage in procs: |
|
||||||
try: |
|
||||||
first = [p for p in first_proc.procLog.procs if proc_name in p.cmdline][0] |
|
||||||
last = [p for p in last_proc.procLog.procs if proc_name in p.cmdline][0] |
|
||||||
cpu_time = cputime_total(last) - cputime_total(first) |
|
||||||
cpu_usage = cpu_time / dt * 100. |
|
||||||
if cpu_usage > max(normal_cpu_usage * 1.1, normal_cpu_usage + 5.0): |
|
||||||
print(f"Warning {proc_name} using more CPU than normal") |
|
||||||
r = 1 |
|
||||||
|
|
||||||
print(f"{proc_name.ljust(35)} {cpu_usage:.2f}%") |
|
||||||
except IndexError: |
|
||||||
print(f"{proc_name.ljust(35)} NO METRICS FOUND") |
|
||||||
print("------------------------------------------------") |
|
||||||
|
|
||||||
return r |
|
@ -1,9 +0,0 @@ |
|||||||
import os |
|
||||||
from nose.tools import nottest |
|
||||||
|
|
||||||
def phone_only(x): |
|
||||||
if os.path.isfile("/init.qcom.rc"): |
|
||||||
return x |
|
||||||
else: |
|
||||||
return nottest(x) |
|
||||||
|
|
@ -0,0 +1,2 @@ |
|||||||
|
transformations |
||||||
|
transformations.cpp |
@ -0,0 +1,8 @@ |
|||||||
|
Import('env', 'cython_dependencies') |
||||||
|
|
||||||
|
d = Dir('.') |
||||||
|
|
||||||
|
env.Command(['transformations.so'], |
||||||
|
cython_dependencies + ['transformations.pxd', 'transformations.pyx', |
||||||
|
'coordinates.cc', 'orientation.cc', 'coordinates.hpp', 'orientation.hpp'], |
||||||
|
'cd ' + d.path + ' && python3 setup.py build_ext --inplace') |
@ -0,0 +1,104 @@ |
|||||||
|
#define _USE_MATH_DEFINES |
||||||
|
|
||||||
|
#include <iostream> |
||||||
|
#include <cmath> |
||||||
|
#include <eigen3/Eigen/Dense> |
||||||
|
|
||||||
|
#include "coordinates.hpp" |
||||||
|
|
||||||
|
#define DEG2RAD(x) ((x) * M_PI / 180.0) |
||||||
|
#define RAD2DEG(x) ((x) * 180.0 / M_PI) |
||||||
|
|
||||||
|
|
||||||
|
double a = 6378137; // lgtm [cpp/short-global-name]
|
||||||
|
double b = 6356752.3142; // lgtm [cpp/short-global-name]
|
||||||
|
double esq = 6.69437999014 * 0.001; // lgtm [cpp/short-global-name]
|
||||||
|
double e1sq = 6.73949674228 * 0.001; |
||||||
|
|
||||||
|
|
||||||
|
static Geodetic to_degrees(Geodetic geodetic){ |
||||||
|
geodetic.lat = RAD2DEG(geodetic.lat); |
||||||
|
geodetic.lon = RAD2DEG(geodetic.lon); |
||||||
|
return geodetic; |
||||||
|
} |
||||||
|
|
||||||
|
static Geodetic to_radians(Geodetic geodetic){ |
||||||
|
geodetic.lat = DEG2RAD(geodetic.lat); |
||||||
|
geodetic.lon = DEG2RAD(geodetic.lon); |
||||||
|
return geodetic; |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
ECEF geodetic2ecef(Geodetic g){ |
||||||
|
g = to_radians(g); |
||||||
|
double xi = sqrt(1.0 - esq * pow(sin(g.lat), 2)); |
||||||
|
double x = (a / xi + g.alt) * cos(g.lat) * cos(g.lon); |
||||||
|
double y = (a / xi + g.alt) * cos(g.lat) * sin(g.lon); |
||||||
|
double z = (a / xi * (1.0 - esq) + g.alt) * sin(g.lat); |
||||||
|
return {x, y, z}; |
||||||
|
} |
||||||
|
|
||||||
|
Geodetic ecef2geodetic(ECEF e){ |
||||||
|
// Convert from ECEF to geodetic using Ferrari's methods
|
||||||
|
// https://en.wikipedia.org/wiki/Geographic_coordinate_conversion#Ferrari.27s_solution
|
||||||
|
double x = e.x; |
||||||
|
double y = e.y; |
||||||
|
double z = e.z; |
||||||
|
|
||||||
|
double r = sqrt(x * x + y * y); |
||||||
|
double Esq = a * a - b * b; |
||||||
|
double F = 54 * b * b * z * z; |
||||||
|
double G = r * r + (1 - esq) * z * z - esq * Esq; |
||||||
|
double C = (esq * esq * F * r * r) / (pow(G, 3)); |
||||||
|
double S = cbrt(1 + C + sqrt(C * C + 2 * C)); |
||||||
|
double P = F / (3 * pow((S + 1 / S + 1), 2) * G * G); |
||||||
|
double Q = sqrt(1 + 2 * esq * esq * P); |
||||||
|
double r_0 = -(P * esq * r) / (1 + Q) + sqrt(0.5 * a * a*(1 + 1.0 / Q) - P * (1 - esq) * z * z / (Q * (1 + Q)) - 0.5 * P * r * r); |
||||||
|
double U = sqrt(pow((r - esq * r_0), 2) + z * z); |
||||||
|
double V = sqrt(pow((r - esq * r_0), 2) + (1 - esq) * z * z); |
||||||
|
double Z_0 = b * b * z / (a * V); |
||||||
|
double h = U * (1 - b * b / (a * V)); |
||||||
|
|
||||||
|
double lat = atan((z + e1sq * Z_0) / r); |
||||||
|
double lon = atan2(y, x); |
||||||
|
|
||||||
|
return to_degrees({lat, lon, h}); |
||||||
|
} |
||||||
|
|
||||||
|
LocalCoord::LocalCoord(Geodetic g, ECEF e){ |
||||||
|
init_ecef << e.x, e.y, e.z; |
||||||
|
|
||||||
|
g = to_radians(g); |
||||||
|
|
||||||
|
ned2ecef_matrix << |
||||||
|
-sin(g.lat)*cos(g.lon), -sin(g.lon), -cos(g.lat)*cos(g.lon), |
||||||
|
-sin(g.lat)*sin(g.lon), cos(g.lon), -cos(g.lat)*sin(g.lon), |
||||||
|
cos(g.lat), 0, -sin(g.lat); |
||||||
|
ecef2ned_matrix = ned2ecef_matrix.transpose(); |
||||||
|
} |
||||||
|
|
||||||
|
NED LocalCoord::ecef2ned(ECEF e) { |
||||||
|
Eigen::Vector3d ecef; |
||||||
|
ecef << e.x, e.y, e.z; |
||||||
|
|
||||||
|
Eigen::Vector3d ned = (ecef2ned_matrix * (ecef - init_ecef)); |
||||||
|
return {ned[0], ned[1], ned[2]}; |
||||||
|
} |
||||||
|
|
||||||
|
ECEF LocalCoord::ned2ecef(NED n) { |
||||||
|
Eigen::Vector3d ned; |
||||||
|
ned << n.n, n.e, n.d; |
||||||
|
|
||||||
|
Eigen::Vector3d ecef = (ned2ecef_matrix * ned) + init_ecef; |
||||||
|
return {ecef[0], ecef[1], ecef[2]}; |
||||||
|
} |
||||||
|
|
||||||
|
NED LocalCoord::geodetic2ned(Geodetic g) { |
||||||
|
ECEF e = ::geodetic2ecef(g); |
||||||
|
return ecef2ned(e); |
||||||
|
} |
||||||
|
|
||||||
|
Geodetic LocalCoord::ned2geodetic(NED n){ |
||||||
|
ECEF e = ned2ecef(n); |
||||||
|
return ::ecef2geodetic(e); |
||||||
|
} |
@ -0,0 +1,35 @@ |
|||||||
|
#pragma once |
||||||
|
|
||||||
|
struct ECEF { |
||||||
|
double x, y, z; |
||||||
|
Eigen::Vector3d to_vector(){ |
||||||
|
return Eigen::Vector3d(x, y, z); |
||||||
|
} |
||||||
|
}; |
||||||
|
|
||||||
|
struct NED { |
||||||
|
double n, e, d; |
||||||
|
}; |
||||||
|
|
||||||
|
struct Geodetic { |
||||||
|
double lat, lon, alt; |
||||||
|
bool radians=false; |
||||||
|
}; |
||||||
|
|
||||||
|
ECEF geodetic2ecef(Geodetic g); |
||||||
|
Geodetic ecef2geodetic(ECEF e); |
||||||
|
|
||||||
|
class LocalCoord { |
||||||
|
public: |
||||||
|
Eigen::Matrix3d ned2ecef_matrix; |
||||||
|
Eigen::Matrix3d ecef2ned_matrix; |
||||||
|
Eigen::Vector3d init_ecef; |
||||||
|
LocalCoord(Geodetic g, ECEF e); |
||||||
|
LocalCoord(Geodetic g) : LocalCoord(g, ::geodetic2ecef(g)) {} |
||||||
|
LocalCoord(ECEF e) : LocalCoord(::ecef2geodetic(e), e) {} |
||||||
|
|
||||||
|
NED ecef2ned(ECEF e); |
||||||
|
ECEF ned2ecef(NED n); |
||||||
|
NED geodetic2ned(Geodetic g); |
||||||
|
Geodetic ned2geodetic(NED n); |
||||||
|
}; |
@ -1,108 +1,19 @@ |
|||||||
import numpy as np |
# pylint: skip-file |
||||||
""" |
from common.transformations.orientation import numpy_wrap |
||||||
Coordinate transformation module. All methods accept arrays as input |
from common.transformations.transformations import (ecef2geodetic_single, |
||||||
with each row as a position. |
geodetic2ecef_single) |
||||||
""" |
from common.transformations.transformations import LocalCoord as LocalCoord_single |
||||||
|
|
||||||
|
|
||||||
|
class LocalCoord(LocalCoord_single): |
||||||
|
ecef2ned = numpy_wrap(LocalCoord_single.ecef2ned_single, (3,), (3,)) |
||||||
|
ned2ecef = numpy_wrap(LocalCoord_single.ned2ecef_single, (3,), (3,)) |
||||||
|
geodetic2ned = numpy_wrap(LocalCoord_single.geodetic2ned_single, (3,), (3,)) |
||||||
|
ned2geodetic = numpy_wrap(LocalCoord_single.ned2geodetic_single, (3,), (3,)) |
||||||
|
|
||||||
a = 6378137 |
|
||||||
b = 6356752.3142 |
|
||||||
esq = 6.69437999014 * 0.001 |
|
||||||
e1sq = 6.73949674228 * 0.001 |
|
||||||
|
|
||||||
|
geodetic2ecef = numpy_wrap(geodetic2ecef_single, (3,), (3,)) |
||||||
|
ecef2geodetic = numpy_wrap(ecef2geodetic_single, (3,), (3,)) |
||||||
|
|
||||||
def geodetic2ecef(geodetic, radians=False): |
geodetic_from_ecef = ecef2geodetic |
||||||
geodetic = np.array(geodetic) |
ecef_from_geodetic = geodetic2ecef |
||||||
input_shape = geodetic.shape |
|
||||||
geodetic = np.atleast_2d(geodetic) |
|
||||||
|
|
||||||
ratio = 1.0 if radians else (np.pi / 180.0) |
|
||||||
lat = ratio*geodetic[:,0] |
|
||||||
lon = ratio*geodetic[:,1] |
|
||||||
alt = geodetic[:,2] |
|
||||||
|
|
||||||
xi = np.sqrt(1 - esq * np.sin(lat)**2) |
|
||||||
x = (a / xi + alt) * np.cos(lat) * np.cos(lon) |
|
||||||
y = (a / xi + alt) * np.cos(lat) * np.sin(lon) |
|
||||||
z = (a / xi * (1 - esq) + alt) * np.sin(lat) |
|
||||||
ecef = np.array([x, y, z]).T |
|
||||||
return ecef.reshape(input_shape) |
|
||||||
|
|
||||||
|
|
||||||
def ecef2geodetic(ecef, radians=False): |
|
||||||
""" |
|
||||||
Convert ECEF coordinates to geodetic using ferrari's method |
|
||||||
""" |
|
||||||
# Save shape and export column |
|
||||||
ecef = np.atleast_1d(ecef) |
|
||||||
input_shape = ecef.shape |
|
||||||
ecef = np.atleast_2d(ecef) |
|
||||||
x, y, z = ecef[:, 0], ecef[:, 1], ecef[:, 2] |
|
||||||
|
|
||||||
ratio = 1.0 if radians else (180.0 / np.pi) |
|
||||||
|
|
||||||
# Conver from ECEF to geodetic using Ferrari's methods |
|
||||||
# https://en.wikipedia.org/wiki/Geographic_coordinate_conversion#Ferrari.27s_solution |
|
||||||
r = np.sqrt(x * x + y * y) |
|
||||||
Esq = a * a - b * b |
|
||||||
F = 54 * b * b * z * z |
|
||||||
G = r * r + (1 - esq) * z * z - esq * Esq |
|
||||||
C = (esq * esq * F * r * r) / (pow(G, 3)) |
|
||||||
S = np.cbrt(1 + C + np.sqrt(C * C + 2 * C)) |
|
||||||
P = F / (3 * pow((S + 1 / S + 1), 2) * G * G) |
|
||||||
Q = np.sqrt(1 + 2 * esq * esq * P) |
|
||||||
r_0 = -(P * esq * r) / (1 + Q) + np.sqrt(0.5 * a * a*(1 + 1.0 / Q) - \ |
|
||||||
P * (1 - esq) * z * z / (Q * (1 + Q)) - 0.5 * P * r * r) |
|
||||||
U = np.sqrt(pow((r - esq * r_0), 2) + z * z) |
|
||||||
V = np.sqrt(pow((r - esq * r_0), 2) + (1 - esq) * z * z) |
|
||||||
Z_0 = b * b * z / (a * V) |
|
||||||
h = U * (1 - b * b / (a * V)) |
|
||||||
lat = ratio*np.arctan((z + e1sq * Z_0) / r) |
|
||||||
lon = ratio*np.arctan2(y, x) |
|
||||||
|
|
||||||
# stack the new columns and return to the original shape |
|
||||||
geodetic = np.column_stack((lat, lon, h)) |
|
||||||
return geodetic.reshape(input_shape) |
|
||||||
|
|
||||||
class LocalCoord(): |
|
||||||
""" |
|
||||||
Allows conversions to local frames. In this case NED. |
|
||||||
That is: North East Down from the start position in |
|
||||||
meters. |
|
||||||
""" |
|
||||||
def __init__(self, init_geodetic, init_ecef): |
|
||||||
self.init_ecef = init_ecef |
|
||||||
lat, lon, _ = (np.pi/180)*np.array(init_geodetic) |
|
||||||
self.ned2ecef_matrix = np.array([[-np.sin(lat)*np.cos(lon), -np.sin(lon), -np.cos(lat)*np.cos(lon)], |
|
||||||
[-np.sin(lat)*np.sin(lon), np.cos(lon), -np.cos(lat)*np.sin(lon)], |
|
||||||
[np.cos(lat), 0, -np.sin(lat)]]) |
|
||||||
self.ecef2ned_matrix = self.ned2ecef_matrix.T |
|
||||||
|
|
||||||
@classmethod |
|
||||||
def from_geodetic(cls, init_geodetic): |
|
||||||
init_ecef = geodetic2ecef(init_geodetic) |
|
||||||
return LocalCoord(init_geodetic, init_ecef) |
|
||||||
|
|
||||||
@classmethod |
|
||||||
def from_ecef(cls, init_ecef): |
|
||||||
init_geodetic = ecef2geodetic(init_ecef) |
|
||||||
return LocalCoord(init_geodetic, init_ecef) |
|
||||||
|
|
||||||
|
|
||||||
def ecef2ned(self, ecef): |
|
||||||
ecef = np.array(ecef) |
|
||||||
return np.dot(self.ecef2ned_matrix, (ecef - self.init_ecef).T).T |
|
||||||
|
|
||||||
def ned2ecef(self, ned): |
|
||||||
ned = np.array(ned) |
|
||||||
# Transpose so that init_ecef will broadcast correctly for 1d or 2d ned. |
|
||||||
return (np.dot(self.ned2ecef_matrix, ned.T).T + self.init_ecef) |
|
||||||
|
|
||||||
def geodetic2ned(self, geodetic): |
|
||||||
ecef = geodetic2ecef(geodetic) |
|
||||||
return self.ecef2ned(ecef) |
|
||||||
|
|
||||||
def ned2geodetic(self, ned): |
|
||||||
ecef = self.ned2ecef(ned) |
|
||||||
return ecef2geodetic(ecef) |
|
||||||
|
@ -0,0 +1,147 @@ |
|||||||
|
#define _USE_MATH_DEFINES |
||||||
|
|
||||||
|
#include <iostream> |
||||||
|
#include <cmath> |
||||||
|
#include <eigen3/Eigen/Dense> |
||||||
|
|
||||||
|
#include "orientation.hpp" |
||||||
|
#include "coordinates.hpp" |
||||||
|
|
||||||
|
Eigen::Quaterniond ensure_unique(Eigen::Quaterniond quat){ |
||||||
|
if (quat.w() > 0){ |
||||||
|
return quat; |
||||||
|
} else { |
||||||
|
return Eigen::Quaterniond(-quat.w(), -quat.x(), -quat.y(), -quat.z()); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
Eigen::Quaterniond euler2quat(Eigen::Vector3d euler){ |
||||||
|
Eigen::Quaterniond q; |
||||||
|
|
||||||
|
q = Eigen::AngleAxisd(euler(2), Eigen::Vector3d::UnitZ()) |
||||||
|
* Eigen::AngleAxisd(euler(1), Eigen::Vector3d::UnitY()) |
||||||
|
* Eigen::AngleAxisd(euler(0), Eigen::Vector3d::UnitX()); |
||||||
|
return ensure_unique(q); |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
Eigen::Vector3d quat2euler(Eigen::Quaterniond quat){ |
||||||
|
// TODO: switch to eigen implementation if the range of the Euler angles doesn't matter anymore
|
||||||
|
// Eigen::Vector3d euler = quat.toRotationMatrix().eulerAngles(2, 1, 0);
|
||||||
|
// return {euler(2), euler(1), euler(0)};
|
||||||
|
double gamma = atan2(2 * (quat.w() * quat.x() + quat.y() * quat.z()), 1 - 2 * (quat.x()*quat.x() + quat.y()*quat.y())); |
||||||
|
double theta = asin(2 * (quat.w() * quat.y() - quat.z() * quat.x())); |
||||||
|
double psi = atan2(2 * (quat.w() * quat.z() + quat.x() * quat.y()), 1 - 2 * (quat.y()*quat.y() + quat.z()*quat.z())); |
||||||
|
return {gamma, theta, psi}; |
||||||
|
} |
||||||
|
|
||||||
|
Eigen::Matrix3d quat2rot(Eigen::Quaterniond quat){ |
||||||
|
return quat.toRotationMatrix(); |
||||||
|
} |
||||||
|
|
||||||
|
Eigen::Quaterniond rot2quat(Eigen::Matrix3d rot){ |
||||||
|
return ensure_unique(Eigen::Quaterniond(rot)); |
||||||
|
} |
||||||
|
|
||||||
|
Eigen::Matrix3d euler2rot(Eigen::Vector3d euler){ |
||||||
|
return quat2rot(euler2quat(euler)); |
||||||
|
} |
||||||
|
|
||||||
|
Eigen::Vector3d rot2euler(Eigen::Matrix3d rot){ |
||||||
|
return quat2euler(rot2quat(rot)); |
||||||
|
} |
||||||
|
|
||||||
|
Eigen::Matrix3d rot_matrix(double roll, double pitch, double yaw){ |
||||||
|
return euler2rot({roll, pitch, yaw}); |
||||||
|
} |
||||||
|
|
||||||
|
Eigen::Matrix3d rot(Eigen::Vector3d axis, double angle){ |
||||||
|
Eigen::Quaterniond q; |
||||||
|
q = Eigen::AngleAxisd(angle, axis); |
||||||
|
return q.toRotationMatrix(); |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
Eigen::Vector3d ecef_euler_from_ned(ECEF ecef_init, Eigen::Vector3d ned_pose) { |
||||||
|
/*
|
||||||
|
Using Rotations to Build Aerospace Coordinate Systems |
||||||
|
Don Koks |
||||||
|
https://apps.dtic.mil/dtic/tr/fulltext/u2/a484864.pdf
|
||||||
|
*/ |
||||||
|
LocalCoord converter = LocalCoord(ecef_init); |
||||||
|
Eigen::Vector3d zero = ecef_init.to_vector(); |
||||||
|
|
||||||
|
Eigen::Vector3d x0 = converter.ned2ecef({1, 0, 0}).to_vector() - zero; |
||||||
|
Eigen::Vector3d y0 = converter.ned2ecef({0, 1, 0}).to_vector() - zero; |
||||||
|
Eigen::Vector3d z0 = converter.ned2ecef({0, 0, 1}).to_vector() - zero; |
||||||
|
|
||||||
|
Eigen::Vector3d x1 = rot(z0, ned_pose(2)) * x0; |
||||||
|
Eigen::Vector3d y1 = rot(z0, ned_pose(2)) * y0; |
||||||
|
Eigen::Vector3d z1 = rot(z0, ned_pose(2)) * z0; |
||||||
|
|
||||||
|
Eigen::Vector3d x2 = rot(y1, ned_pose(1)) * x1; |
||||||
|
Eigen::Vector3d y2 = rot(y1, ned_pose(1)) * y1; |
||||||
|
Eigen::Vector3d z2 = rot(y1, ned_pose(1)) * z1; |
||||||
|
|
||||||
|
Eigen::Vector3d x3 = rot(x2, ned_pose(0)) * x2; |
||||||
|
Eigen::Vector3d y3 = rot(x2, ned_pose(0)) * y2; |
||||||
|
|
||||||
|
|
||||||
|
x0 = Eigen::Vector3d(1, 0, 0); |
||||||
|
y0 = Eigen::Vector3d(0, 1, 0); |
||||||
|
z0 = Eigen::Vector3d(0, 0, 1); |
||||||
|
|
||||||
|
double psi = atan2(x3.dot(y0), x3.dot(x0)); |
||||||
|
double theta = atan2(-x3.dot(z0), sqrt(pow(x3.dot(x0), 2) + pow(x3.dot(y0), 2))); |
||||||
|
|
||||||
|
y2 = rot(z0, psi) * y0; |
||||||
|
z2 = rot(y2, theta) * z0; |
||||||
|
|
||||||
|
double phi = atan2(y3.dot(z2), y3.dot(y2)); |
||||||
|
|
||||||
|
return {phi, theta, psi}; |
||||||
|
} |
||||||
|
|
||||||
|
Eigen::Vector3d ned_euler_from_ecef(ECEF ecef_init, Eigen::Vector3d ecef_pose){ |
||||||
|
/*
|
||||||
|
Using Rotations to Build Aerospace Coordinate Systems |
||||||
|
Don Koks |
||||||
|
https://apps.dtic.mil/dtic/tr/fulltext/u2/a484864.pdf
|
||||||
|
*/ |
||||||
|
LocalCoord converter = LocalCoord(ecef_init); |
||||||
|
|
||||||
|
Eigen::Vector3d x0 = Eigen::Vector3d(1, 0, 0); |
||||||
|
Eigen::Vector3d y0 = Eigen::Vector3d(0, 1, 0); |
||||||
|
Eigen::Vector3d z0 = Eigen::Vector3d(0, 0, 1); |
||||||
|
|
||||||
|
Eigen::Vector3d x1 = rot(z0, ecef_pose(2)) * x0; |
||||||
|
Eigen::Vector3d y1 = rot(z0, ecef_pose(2)) * y0; |
||||||
|
Eigen::Vector3d z1 = rot(z0, ecef_pose(2)) * z0; |
||||||
|
|
||||||
|
Eigen::Vector3d x2 = rot(y1, ecef_pose(1)) * x1; |
||||||
|
Eigen::Vector3d y2 = rot(y1, ecef_pose(1)) * y1; |
||||||
|
Eigen::Vector3d z2 = rot(y1, ecef_pose(1)) * z1; |
||||||
|
|
||||||
|
Eigen::Vector3d x3 = rot(x2, ecef_pose(0)) * x2; |
||||||
|
Eigen::Vector3d y3 = rot(x2, ecef_pose(0)) * y2; |
||||||
|
|
||||||
|
Eigen::Vector3d zero = ecef_init.to_vector(); |
||||||
|
x0 = converter.ned2ecef({1, 0, 0}).to_vector() - zero; |
||||||
|
y0 = converter.ned2ecef({0, 1, 0}).to_vector() - zero; |
||||||
|
z0 = converter.ned2ecef({0, 0, 1}).to_vector() - zero; |
||||||
|
|
||||||
|
double psi = atan2(x3.dot(y0), x3.dot(x0)); |
||||||
|
double theta = atan2(-x3.dot(z0), sqrt(pow(x3.dot(x0), 2) + pow(x3.dot(y0), 2))); |
||||||
|
|
||||||
|
y2 = rot(z0, psi) * y0; |
||||||
|
z2 = rot(y2, theta) * z0; |
||||||
|
|
||||||
|
double phi = atan2(y3.dot(z2), y3.dot(y2)); |
||||||
|
|
||||||
|
return {phi, theta, psi}; |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
int main(void){ |
||||||
|
} |
@ -0,0 +1,17 @@ |
|||||||
|
#pragma once |
||||||
|
#include <eigen3/Eigen/Dense> |
||||||
|
#include "coordinates.hpp" |
||||||
|
|
||||||
|
|
||||||
|
Eigen::Quaterniond ensure_unique(Eigen::Quaterniond quat); |
||||||
|
|
||||||
|
Eigen::Quaterniond euler2quat(Eigen::Vector3d euler); |
||||||
|
Eigen::Vector3d quat2euler(Eigen::Quaterniond quat); |
||||||
|
Eigen::Matrix3d quat2rot(Eigen::Quaterniond quat); |
||||||
|
Eigen::Quaterniond rot2quat(Eigen::Matrix3d rot); |
||||||
|
Eigen::Matrix3d euler2rot(Eigen::Vector3d euler); |
||||||
|
Eigen::Vector3d rot2euler(Eigen::Matrix3d rot); |
||||||
|
Eigen::Matrix3d rot_matrix(double roll, double pitch, double yaw); |
||||||
|
Eigen::Matrix3d rot(Eigen::Vector3d axis, double angle); |
||||||
|
Eigen::Vector3d ecef_euler_from_ned(ECEF ecef_init, Eigen::Vector3d ned_pose); |
||||||
|
Eigen::Vector3d ned_euler_from_ecef(ECEF ecef_init, Eigen::Vector3d ecef_pose); |
@ -0,0 +1,42 @@ |
|||||||
|
import os |
||||||
|
import numpy |
||||||
|
import sysconfig |
||||||
|
|
||||||
|
from Cython.Build import cythonize |
||||||
|
from Cython.Distutils import build_ext |
||||||
|
from distutils.core import Extension, setup # pylint: disable=import-error,no-name-in-module |
||||||
|
|
||||||
|
def get_ext_filename_without_platform_suffix(filename): |
||||||
|
name, ext = os.path.splitext(filename) |
||||||
|
ext_suffix = sysconfig.get_config_var('EXT_SUFFIX') |
||||||
|
|
||||||
|
if ext_suffix == ext: |
||||||
|
return filename |
||||||
|
|
||||||
|
ext_suffix = ext_suffix.replace(ext, '') |
||||||
|
idx = name.find(ext_suffix) |
||||||
|
|
||||||
|
if idx == -1: |
||||||
|
return filename |
||||||
|
else: |
||||||
|
return name[:idx] + ext |
||||||
|
|
||||||
|
|
||||||
|
class BuildExtWithoutPlatformSuffix(build_ext): |
||||||
|
def get_ext_filename(self, ext_name): |
||||||
|
filename = super().get_ext_filename(ext_name) |
||||||
|
return get_ext_filename_without_platform_suffix(filename) |
||||||
|
|
||||||
|
|
||||||
|
setup( |
||||||
|
name='Cython transformations wrapper', |
||||||
|
cmdclass={'build_ext': BuildExtWithoutPlatformSuffix}, |
||||||
|
ext_modules=cythonize( |
||||||
|
Extension( |
||||||
|
"transformations", |
||||||
|
sources=["transformations.pyx"], |
||||||
|
language="c++", |
||||||
|
extra_compile_args=["-std=c++14"], |
||||||
|
include_dirs=[numpy.get_include()], |
||||||
|
) |
||||||
|
)) |
@ -0,0 +1,71 @@ |
|||||||
|
from libcpp cimport bool |
||||||
|
|
||||||
|
cdef extern from "orientation.cc": |
||||||
|
pass |
||||||
|
|
||||||
|
cdef extern from "orientation.hpp": |
||||||
|
cdef cppclass Quaternion "Eigen::Quaterniond": |
||||||
|
Quaternion() |
||||||
|
Quaternion(double, double, double, double) |
||||||
|
double w() |
||||||
|
double x() |
||||||
|
double y() |
||||||
|
double z() |
||||||
|
|
||||||
|
cdef cppclass Vector3 "Eigen::Vector3d": |
||||||
|
Vector3() |
||||||
|
Vector3(double, double, double) |
||||||
|
double operator()(int) |
||||||
|
|
||||||
|
cdef cppclass Matrix3 "Eigen::Matrix3d": |
||||||
|
Matrix3() |
||||||
|
Matrix3(double*) |
||||||
|
|
||||||
|
double operator()(int, int) |
||||||
|
|
||||||
|
Quaternion euler2quat(Vector3) |
||||||
|
Vector3 quat2euler(Quaternion) |
||||||
|
Matrix3 quat2rot(Quaternion) |
||||||
|
Quaternion rot2quat(Matrix3) |
||||||
|
Vector3 rot2euler(Matrix3) |
||||||
|
Matrix3 euler2rot(Vector3) |
||||||
|
Matrix3 rot_matrix(double, double, double) |
||||||
|
Vector3 ecef_euler_from_ned(ECEF, Vector3) |
||||||
|
Vector3 ned_euler_from_ecef(ECEF, Vector3) |
||||||
|
|
||||||
|
|
||||||
|
cdef extern from "coordinates.cc": |
||||||
|
cdef struct ECEF: |
||||||
|
double x |
||||||
|
double y |
||||||
|
double z |
||||||
|
|
||||||
|
cdef struct NED: |
||||||
|
double n |
||||||
|
double e |
||||||
|
double d |
||||||
|
|
||||||
|
cdef struct Geodetic: |
||||||
|
double lat |
||||||
|
double lon |
||||||
|
double alt |
||||||
|
bool radians |
||||||
|
|
||||||
|
ECEF geodetic2ecef(Geodetic) |
||||||
|
Geodetic ecef2geodetic(ECEF) |
||||||
|
|
||||||
|
cdef cppclass LocalCoord_c "LocalCoord": |
||||||
|
Matrix3 ned2ecef_matrix |
||||||
|
Matrix3 ecef2ned_matrix |
||||||
|
|
||||||
|
LocalCoord_c(Geodetic, ECEF) |
||||||
|
LocalCoord_c(Geodetic) |
||||||
|
LocalCoord_c(ECEF) |
||||||
|
|
||||||
|
NED ecef2ned(ECEF) |
||||||
|
ECEF ned2ecef(NED) |
||||||
|
NED geodetic2ned(Geodetic) |
||||||
|
Geodetic ned2geodetic(NED) |
||||||
|
|
||||||
|
cdef extern from "coordinates.hpp": |
||||||
|
pass |
@ -0,0 +1,172 @@ |
|||||||
|
from transformations cimport Matrix3, Vector3, Quaternion |
||||||
|
from transformations cimport ECEF, NED, Geodetic |
||||||
|
|
||||||
|
from transformations cimport euler2quat as euler2quat_c |
||||||
|
from transformations cimport quat2euler as quat2euler_c |
||||||
|
from transformations cimport quat2rot as quat2rot_c |
||||||
|
from transformations cimport rot2quat as rot2quat_c |
||||||
|
from transformations cimport euler2rot as euler2rot_c |
||||||
|
from transformations cimport rot2euler as rot2euler_c |
||||||
|
from transformations cimport rot_matrix as rot_matrix_c |
||||||
|
from transformations cimport ecef_euler_from_ned as ecef_euler_from_ned_c |
||||||
|
from transformations cimport ned_euler_from_ecef as ned_euler_from_ecef_c |
||||||
|
from transformations cimport geodetic2ecef as geodetic2ecef_c |
||||||
|
from transformations cimport ecef2geodetic as ecef2geodetic_c |
||||||
|
from transformations cimport LocalCoord_c |
||||||
|
|
||||||
|
|
||||||
|
import cython |
||||||
|
import numpy as np |
||||||
|
cimport numpy as np |
||||||
|
|
||||||
|
cdef np.ndarray[double, ndim=2] matrix2numpy(Matrix3 m): |
||||||
|
return np.array([ |
||||||
|
[m(0, 0), m(0, 1), m(0, 2)], |
||||||
|
[m(1, 0), m(1, 1), m(1, 2)], |
||||||
|
[m(2, 0), m(2, 1), m(2, 2)], |
||||||
|
]) |
||||||
|
|
||||||
|
cdef Matrix3 numpy2matrix (np.ndarray[double, ndim=2, mode="fortran"] m): |
||||||
|
assert m.shape[0] == 3 |
||||||
|
assert m.shape[1] == 3 |
||||||
|
return Matrix3(<double*>m.data) |
||||||
|
|
||||||
|
cdef ECEF list2ecef(ecef): |
||||||
|
cdef ECEF e; |
||||||
|
e.x = ecef[0] |
||||||
|
e.y = ecef[1] |
||||||
|
e.z = ecef[2] |
||||||
|
return e |
||||||
|
|
||||||
|
cdef NED list2ned(ned): |
||||||
|
cdef NED n; |
||||||
|
n.n = ned[0] |
||||||
|
n.e = ned[1] |
||||||
|
n.d = ned[2] |
||||||
|
return n |
||||||
|
|
||||||
|
cdef Geodetic list2geodetic(geodetic): |
||||||
|
cdef Geodetic g |
||||||
|
g.lat = geodetic[0] |
||||||
|
g.lon = geodetic[1] |
||||||
|
g.alt = geodetic[2] |
||||||
|
return g |
||||||
|
|
||||||
|
def euler2quat_single(euler): |
||||||
|
cdef Vector3 e = Vector3(euler[0], euler[1], euler[2]) |
||||||
|
cdef Quaternion q = euler2quat_c(e) |
||||||
|
return [q.w(), q.x(), q.y(), q.z()] |
||||||
|
|
||||||
|
def quat2euler_single(quat): |
||||||
|
cdef Quaternion q = Quaternion(quat[0], quat[1], quat[2], quat[3]) |
||||||
|
cdef Vector3 e = quat2euler_c(q); |
||||||
|
return [e(0), e(1), e(2)] |
||||||
|
|
||||||
|
def quat2rot_single(quat): |
||||||
|
cdef Quaternion q = Quaternion(quat[0], quat[1], quat[2], quat[3]) |
||||||
|
cdef Matrix3 r = quat2rot_c(q) |
||||||
|
return matrix2numpy(r) |
||||||
|
|
||||||
|
def rot2quat_single(rot): |
||||||
|
cdef Matrix3 r = numpy2matrix(np.asfortranarray(rot, dtype=np.double)) |
||||||
|
cdef Quaternion q = rot2quat_c(r) |
||||||
|
return [q.w(), q.x(), q.y(), q.z()] |
||||||
|
|
||||||
|
def euler2rot_single(euler): |
||||||
|
cdef Vector3 e = Vector3(euler[0], euler[1], euler[2]) |
||||||
|
cdef Matrix3 r = euler2rot_c(e) |
||||||
|
return matrix2numpy(r) |
||||||
|
|
||||||
|
def rot2euler_single(rot): |
||||||
|
cdef Matrix3 r = numpy2matrix(np.asfortranarray(rot, dtype=np.double)) |
||||||
|
cdef Vector3 e = rot2euler_c(r) |
||||||
|
return [e(0), e(1), e(2)] |
||||||
|
|
||||||
|
def rot_matrix(roll, pitch, yaw): |
||||||
|
return matrix2numpy(rot_matrix_c(roll, pitch, yaw)) |
||||||
|
|
||||||
|
def ecef_euler_from_ned_single(ecef_init, ned_pose): |
||||||
|
cdef ECEF init = list2ecef(ecef_init) |
||||||
|
cdef Vector3 pose = Vector3(ned_pose[0], ned_pose[1], ned_pose[2]) |
||||||
|
|
||||||
|
cdef Vector3 e = ecef_euler_from_ned_c(init, pose) |
||||||
|
return [e(0), e(1), e(2)] |
||||||
|
|
||||||
|
def ned_euler_from_ecef_single(ecef_init, ecef_pose): |
||||||
|
cdef ECEF init = list2ecef(ecef_init) |
||||||
|
cdef Vector3 pose = Vector3(ecef_pose[0], ecef_pose[1], ecef_pose[2]) |
||||||
|
|
||||||
|
cdef Vector3 e = ned_euler_from_ecef_c(init, pose) |
||||||
|
return [e(0), e(1), e(2)] |
||||||
|
|
||||||
|
def geodetic2ecef_single(geodetic): |
||||||
|
cdef Geodetic g = list2geodetic(geodetic) |
||||||
|
cdef ECEF e = geodetic2ecef_c(g) |
||||||
|
return [e.x, e.y, e.z] |
||||||
|
|
||||||
|
def ecef2geodetic_single(ecef): |
||||||
|
cdef ECEF e = list2ecef(ecef) |
||||||
|
cdef Geodetic g = ecef2geodetic_c(e) |
||||||
|
return [g.lat, g.lon, g.alt] |
||||||
|
|
||||||
|
|
||||||
|
cdef class LocalCoord: |
||||||
|
cdef LocalCoord_c * lc |
||||||
|
|
||||||
|
def __init__(self, geodetic=None, ecef=None): |
||||||
|
assert (geodetic is not None) or (ecef is not None) |
||||||
|
if geodetic is not None: |
||||||
|
self.lc = new LocalCoord_c(list2geodetic(geodetic)) |
||||||
|
elif ecef is not None: |
||||||
|
self.lc = new LocalCoord_c(list2ecef(ecef)) |
||||||
|
|
||||||
|
@property |
||||||
|
def ned2ecef_matrix(self): |
||||||
|
return matrix2numpy(self.lc.ned2ecef_matrix) |
||||||
|
|
||||||
|
@property |
||||||
|
def ecef2ned_matrix(self): |
||||||
|
return matrix2numpy(self.lc.ecef2ned_matrix) |
||||||
|
|
||||||
|
@property |
||||||
|
def ned_from_ecef_matrix(self): |
||||||
|
return self.ecef2ned_matrix |
||||||
|
|
||||||
|
@property |
||||||
|
def ecef_from_ned_matrix(self): |
||||||
|
return self.ned2ecef_matrix |
||||||
|
|
||||||
|
@classmethod |
||||||
|
def from_geodetic(cls, geodetic): |
||||||
|
return cls(geodetic=geodetic) |
||||||
|
|
||||||
|
@classmethod |
||||||
|
def from_ecef(cls, ecef): |
||||||
|
return cls(ecef=ecef) |
||||||
|
|
||||||
|
def ecef2ned_single(self, ecef): |
||||||
|
assert self.lc |
||||||
|
cdef ECEF e = list2ecef(ecef) |
||||||
|
cdef NED n = self.lc.ecef2ned(e) |
||||||
|
return [n.n, n.e, n.d] |
||||||
|
|
||||||
|
def ned2ecef_single(self, ned): |
||||||
|
assert self.lc |
||||||
|
cdef NED n = list2ned(ned) |
||||||
|
cdef ECEF e = self.lc.ned2ecef(n) |
||||||
|
return [e.x, e.y, e.z] |
||||||
|
|
||||||
|
def geodetic2ned_single(self, geodetic): |
||||||
|
assert self.lc |
||||||
|
cdef Geodetic g = list2geodetic(geodetic) |
||||||
|
cdef NED n = self.lc.geodetic2ned(g) |
||||||
|
return [n.n, n.e, n.d] |
||||||
|
|
||||||
|
def ned2geodetic_single(self, ned): |
||||||
|
assert self.lc |
||||||
|
cdef NED n = list2ned(ned) |
||||||
|
cdef Geodetic g = self.lc.ned2geodetic(n) |
||||||
|
return [g.lat, g.lon, g.alt] |
||||||
|
|
||||||
|
def __dealloc__(self): |
||||||
|
del self.lc |
Binary file not shown.
@ -1,3 +0,0 @@ |
|||||||
version https://git-lfs.github.com/spec/v1 |
|
||||||
oid sha256:e3c75edadf2e267be8a18870cafc911ea23d3b52dcff07fbe025edf815c06f5d |
|
||||||
size 68732 |
|
@ -1 +0,0 @@ |
|||||||
/opt/intel/opencl-1.2-6.4.0.37/lib64/libintelocl.so |
|
@ -1,3 +0,0 @@ |
|||||||
version https://git-lfs.github.com/spec/v1 |
|
||||||
oid sha256:381814ea42344b895624597bf31c24ebc13f3449aaaaf65245f4a041d953b4c6 |
|
||||||
size 17276236 |
|
@ -1,3 +0,0 @@ |
|||||||
version https://git-lfs.github.com/spec/v1 |
|
||||||
oid sha256:25aaa33f5c338b6dcc33436fdebb5e6ad727cf85a9fae921be8d3b834166ab01 |
|
||||||
size 11432 |
|
@ -1,3 +0,0 @@ |
|||||||
version https://git-lfs.github.com/spec/v1 |
|
||||||
oid sha256:f7300ebee63820b519c54a50c93847516dcaf37765a698826fde666990747459 |
|
||||||
size 20290860 |
|
@ -1,9 +0,0 @@ |
|||||||
#!/usr/bin/env bash |
|
||||||
|
|
||||||
# only pyflakes check (--select=F) |
|
||||||
RESULT=$(python3 -m flake8 --select=F $(eval echo $(cat <(find cereal) <(find opendbc) release/files_common release/files_common | tr '\n' ' ') | tr ' ' '\n' | grep "\.py$")) |
|
||||||
if [[ $RESULT ]]; then |
|
||||||
echo "Pyflakes found errors in the code. Please fix and try again" |
|
||||||
echo "$RESULT" |
|
||||||
exit 1 |
|
||||||
fi |
|
@ -0,0 +1,82 @@ |
|||||||
|
#!/usr/bin/env python3 |
||||||
|
import os |
||||||
|
import shutil |
||||||
|
import subprocess |
||||||
|
import tempfile |
||||||
|
import time |
||||||
|
import unittest |
||||||
|
|
||||||
|
from common.basedir import BASEDIR |
||||||
|
|
||||||
|
UPDATER_PATH = os.path.join(BASEDIR, "installer/updater") |
||||||
|
UPDATER = os.path.join(UPDATER_PATH, "updater") |
||||||
|
UPDATE_MANIFEST = os.path.join(UPDATER_PATH, "update.json") |
||||||
|
|
||||||
|
|
||||||
|
class TestUpdater(unittest.TestCase): |
||||||
|
|
||||||
|
@classmethod |
||||||
|
def setUpClass(cls): |
||||||
|
# test that the updater builds |
||||||
|
cls.assertTrue(f"cd {UPDATER_PATH} && make clean && make", "updater failed to build") |
||||||
|
|
||||||
|
# restore the checked-in version, since that's what actually runs on devices |
||||||
|
os.system(f"git reset --hard {UPDATER_PATH}") |
||||||
|
|
||||||
|
def setUp(self): |
||||||
|
self._clear_dir() |
||||||
|
|
||||||
|
def tearDown(self): |
||||||
|
self._clear_dir() |
||||||
|
|
||||||
|
def _clear_dir(self): |
||||||
|
if os.path.isdir("/data/neoupdate"): |
||||||
|
shutil.rmtree("/data/neoupdate") |
||||||
|
|
||||||
|
def _assert_ok(self, cmd, msg=None): |
||||||
|
self.assertTrue(os.system(cmd) == 0, msg) |
||||||
|
|
||||||
|
def _assert_fails(self, cmd): |
||||||
|
self.assertFalse(os.system(cmd) == 0) |
||||||
|
|
||||||
|
def test_background_download(self): |
||||||
|
self._assert_ok(f"{UPDATER} bgcache 'file://{UPDATE_MANIFEST}'") |
||||||
|
|
||||||
|
def test_background_download_bad_manifest(self): |
||||||
|
# update with bad manifest should fail |
||||||
|
with tempfile.NamedTemporaryFile(mode="w", suffix=".json") as f: |
||||||
|
f.write("{}") |
||||||
|
self._assert_fails(f"{UPDATER} bgcache 'file://{f.name}'") |
||||||
|
|
||||||
|
def test_cache_resume(self): |
||||||
|
self._assert_ok(f"{UPDATER} bgcache 'file://{UPDATE_MANIFEST}'") |
||||||
|
# a full download takes >1m, but resuming from fully cached should only be a few seconds |
||||||
|
start_time = time.monotonic() |
||||||
|
self._assert_ok(f"{UPDATER} bgcache 'file://{UPDATE_MANIFEST}'") |
||||||
|
self.assertLess(time.monotonic() - start_time, 10) |
||||||
|
|
||||||
|
# make sure we can recover from corrupt downloads |
||||||
|
def test_recover_from_corrupt(self): |
||||||
|
# download the whole update |
||||||
|
self._assert_ok(f"{UPDATER} bgcache 'file://{UPDATE_MANIFEST}'") |
||||||
|
|
||||||
|
# write some random bytes |
||||||
|
for f in os.listdir("/data/neoupdate"): |
||||||
|
with open(os.path.join("/data/neoupdate", f), "ab") as f: |
||||||
|
f.write(b"\xab"*20) |
||||||
|
|
||||||
|
# this attempt should fail, then it unlinks |
||||||
|
self._assert_fails(f"{UPDATER} bgcache 'file://{UPDATE_MANIFEST}'") |
||||||
|
|
||||||
|
# now it should pass |
||||||
|
self._assert_ok(f"{UPDATER} bgcache 'file://{UPDATE_MANIFEST}'") |
||||||
|
|
||||||
|
# simple test that the updater doesn't crash in UI mode |
||||||
|
def test_ui_init(self): |
||||||
|
with subprocess.Popen(UPDATER) as proc: |
||||||
|
time.sleep(5) |
||||||
|
self.assertTrue(proc.poll() is None) |
||||||
|
proc.terminate() |
||||||
|
|
||||||
|
if __name__ == "__main__": |
||||||
|
unittest.main() |
@ -0,0 +1,7 @@ |
|||||||
|
{ |
||||||
|
"ota_url": "https://commadist.azureedge.net/neosupdate/ota-signed-3bd2b3bdd6a501569e00b8f12786d65e0fd2788c0dd238f8c986e3e2e504683a-kernel.zip", |
||||||
|
"ota_hash": "3bd2b3bdd6a501569e00b8f12786d65e0fd2788c0dd238f8c986e3e2e504683a", |
||||||
|
"recovery_url": "https://commadist.azureedge.net/neosupdate/recovery-97c27e6ed04ed6bb0608b845a2d4100912093f9380c3f2ba6b56bccd608e5f6e.img", |
||||||
|
"recovery_len": 15861036, |
||||||
|
"recovery_hash": "97c27e6ed04ed6bb0608b845a2d4100912093f9380c3f2ba6b56bccd608e5f6e" |
||||||
|
} |
Binary file not shown.
@ -1 +1 @@ |
|||||||
Subproject commit d172b27f4f346e642802a4c7cbf14405a4161d35 |
Subproject commit 765c6584c3d7f27e1af0f1180cc29766d5319f09 |
@ -0,0 +1,17 @@ |
|||||||
|
#!/usr/bin/bash |
||||||
|
|
||||||
|
export OMP_NUM_THREADS=1 |
||||||
|
export MKL_NUM_THREADS=1 |
||||||
|
export NUMEXPR_NUM_THREADS=1 |
||||||
|
export OPENBLAS_NUM_THREADS=1 |
||||||
|
export VECLIB_MAXIMUM_THREADS=1 |
||||||
|
|
||||||
|
if [ -z "$REQUIRED_NEOS_VERSION" ]; then |
||||||
|
export REQUIRED_NEOS_VERSION="14" |
||||||
|
fi |
||||||
|
|
||||||
|
if [ -z "$PASSIVE" ]; then |
||||||
|
export PASSIVE="1" |
||||||
|
fi |
||||||
|
|
||||||
|
export STAGING_ROOT="/data/safe_staging" |
@ -1 +1 @@ |
|||||||
43221d85-46fd-40b9-bff0-2b1b18a86b07 |
e96f9be6-5741-42ea-bdcd-0be6515b4230 |
@ -1,3 +1,3 @@ |
|||||||
version https://git-lfs.github.com/spec/v1 |
version https://git-lfs.github.com/spec/v1 |
||||||
oid sha256:5c39a2096f7058541b5339ec36bc4c468955e67285078080ed6d8802fed06c1d |
oid sha256:09aa11a17a5a8173e231071898c499f9ea632e6e64285586122828b1bbc70d41 |
||||||
size 814176 |
size 4165968 |
||||||
|
@ -1,3 +1,3 @@ |
|||||||
version https://git-lfs.github.com/spec/v1 |
version https://git-lfs.github.com/spec/v1 |
||||||
oid sha256:29504dfd101ba2a0b48550fac2f86f9d0b8d1245af3d2d8d658247b4a73077a2 |
oid sha256:beecf140ddc5da96cbdae3b869ebb3f5453dcd8e61e09d7d079c91e006b6df98 |
||||||
size 230121 |
size 1134208 |
||||||
|
@ -0,0 +1,4 @@ |
|||||||
|
[mypy] |
||||||
|
python_version = 3.8 |
||||||
|
ignore_missing_imports = True |
||||||
|
|
@ -1 +1 @@ |
|||||||
Subproject commit 45c0d9ecce255e028163539e22e9a169735de69d |
Subproject commit b7cf1a67bc71b674e6793ba1f2fff5d29fee1e6b |
@ -1 +1 @@ |
|||||||
Subproject commit 6b19fa4961d5dc6e6ea77987eb3a99ce28b0f5cd |
Subproject commit ecef0a19d0f72d8fd3151593b7bd1a112d5f63e2 |
@ -1 +0,0 @@ |
|||||||
from .utils import LogentriesHandler |
|
@ -1,49 +0,0 @@ |
|||||||
|
|
||||||
""" This file contains some helpers methods in both Python2 and 3 """ |
|
||||||
import sys |
|
||||||
import re |
|
||||||
|
|
||||||
if sys.version < '3': |
|
||||||
# Python2.x imports |
|
||||||
import Queue |
|
||||||
import codecs |
|
||||||
else: |
|
||||||
# Python 3.x imports |
|
||||||
import queue |
|
||||||
|
|
||||||
|
|
||||||
def check_token(token): |
|
||||||
""" Checks if the given token is a valid UUID.""" |
|
||||||
valid = re.compile(r"^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-" |
|
||||||
r"[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$") |
|
||||||
|
|
||||||
return valid.match(token) |
|
||||||
|
|
||||||
# We need to do some things different pending if its Python 2.x or 3.x |
|
||||||
if sys.version < '3': |
|
||||||
def to_unicode(ch): |
|
||||||
return codecs.unicode_escape_decode(ch)[0] |
|
||||||
|
|
||||||
def is_unicode(ch): |
|
||||||
return isinstance(ch, unicode) |
|
||||||
|
|
||||||
def create_unicode(ch): |
|
||||||
try: |
|
||||||
return unicode(ch, 'utf-8') |
|
||||||
except UnicodeDecodeError as e: |
|
||||||
return str(e) |
|
||||||
|
|
||||||
def create_queue(max_size): |
|
||||||
return Queue.Queue(max_size) |
|
||||||
else: |
|
||||||
def to_unicode(ch): |
|
||||||
return ch |
|
||||||
|
|
||||||
def is_unicode(ch): |
|
||||||
return isinstance(ch, str) |
|
||||||
|
|
||||||
def create_unicode(ch): |
|
||||||
return str(ch) |
|
||||||
|
|
||||||
def create_queue(max_size): |
|
||||||
return queue.Queue(max_size) |
|
@ -1,57 +0,0 @@ |
|||||||
from logentries import LogentriesHandler |
|
||||||
from threading import Lock |
|
||||||
from functools import wraps |
|
||||||
import logging |
|
||||||
import time |
|
||||||
import sys |
|
||||||
import psutil |
|
||||||
|
|
||||||
glob_time = 0 |
|
||||||
glob_name = 0 |
|
||||||
|
|
||||||
log = logging.getLogger('logentries') |
|
||||||
log.setLevel(logging.INFO) |
|
||||||
|
|
||||||
class Metric(object): |
|
||||||
|
|
||||||
def __init__(self, token): |
|
||||||
self._count = 0.0 |
|
||||||
self._sum = 0.0 |
|
||||||
self._lock = Lock() |
|
||||||
self.token = token |
|
||||||
handler = LogentriesHandler(token) |
|
||||||
log.addHandler(handler) |
|
||||||
|
|
||||||
def observe(self, amount): |
|
||||||
with self._lock: |
|
||||||
self._count += 1 |
|
||||||
self._sum += amount |
|
||||||
|
|
||||||
def metric(self): |
|
||||||
'''Mesaure function execution time in seconds |
|
||||||
and forward it to Logentries''' |
|
||||||
|
|
||||||
class Timer(object): |
|
||||||
|
|
||||||
def __init__(self, summary): |
|
||||||
self._summary = summary |
|
||||||
|
|
||||||
def __enter__(self): |
|
||||||
self._start = time.time() |
|
||||||
|
|
||||||
def __exit__(self, typ, value, traceback): |
|
||||||
global glob_time |
|
||||||
self._summary.observe(max(time.time() - self._start, 0)) |
|
||||||
glob_time = time.time()- self._start |
|
||||||
log.info("function_name=" + glob_name + " " + "execution_time=" + str(glob_time) + " " + "cpu=" + str(psutil.cpu_percent(interval=None)) + " " + "cpu_count=" + str(psutil.cpu_count())+ " " + "memory=" + str(psutil.virtual_memory()) ) |
|
||||||
|
|
||||||
def __call__(self, f): |
|
||||||
@wraps(f) |
|
||||||
def wrapped(*args, **kwargs): |
|
||||||
with self: |
|
||||||
global glob_name |
|
||||||
glob_name = f.__name__ |
|
||||||
|
|
||||||
return f(*args, **kwargs) |
|
||||||
return wrapped |
|
||||||
return Timer(self) |
|
@ -1,218 +0,0 @@ |
|||||||
# coding: utf-8 |
|
||||||
# vim: set ts=4 sw=4 et: |
|
||||||
""" This file contains some utils for connecting to Logentries |
|
||||||
as well as storing logs in a queue and sending them.""" |
|
||||||
|
|
||||||
VERSION = '2.0.7' |
|
||||||
|
|
||||||
from logentries import helpers as le_helpers |
|
||||||
|
|
||||||
import logging |
|
||||||
import threading |
|
||||||
import socket |
|
||||||
import random |
|
||||||
import time |
|
||||||
import sys |
|
||||||
|
|
||||||
import certifi |
|
||||||
|
|
||||||
|
|
||||||
# Size of the internal event queue |
|
||||||
QUEUE_SIZE = 32768 |
|
||||||
# Logentries API server address |
|
||||||
LE_API_DEFAULT = "data.logentries.com" |
|
||||||
# Port number for token logging to Logentries API server |
|
||||||
LE_PORT_DEFAULT = 80 |
|
||||||
LE_TLS_PORT_DEFAULT = 443 |
|
||||||
# Minimal delay between attempts to reconnect in seconds |
|
||||||
MIN_DELAY = 0.1 |
|
||||||
# Maximal delay between attempts to recconect in seconds |
|
||||||
MAX_DELAY = 10 |
|
||||||
# Unicode Line separator character \u2028 |
|
||||||
LINE_SEP = le_helpers.to_unicode('\u2028') |
|
||||||
|
|
||||||
|
|
||||||
# LE appender signature - used for debugging messages |
|
||||||
LE = "LE: " |
|
||||||
# Error message displayed when an incorrect Token has been detected |
|
||||||
INVALID_TOKEN = ("\n\nIt appears the LOGENTRIES_TOKEN " |
|
||||||
"parameter you entered is incorrect!\n\n") |
|
||||||
|
|
||||||
|
|
||||||
def dbg(msg): |
|
||||||
print(LE + msg) |
|
||||||
|
|
||||||
|
|
||||||
class PlainTextSocketAppender(threading.Thread): |
|
||||||
def __init__(self, verbose=True, le_api=LE_API_DEFAULT, le_port=LE_PORT_DEFAULT, le_tls_port=LE_TLS_PORT_DEFAULT): |
|
||||||
threading.Thread.__init__(self) |
|
||||||
|
|
||||||
# Logentries API server address |
|
||||||
self.le_api = le_api |
|
||||||
|
|
||||||
# Port number for token logging to Logentries API server |
|
||||||
self.le_port = le_port |
|
||||||
self.le_tls_port = le_tls_port |
|
||||||
|
|
||||||
self.daemon = True |
|
||||||
self.verbose = verbose |
|
||||||
self._conn = None |
|
||||||
self._queue = le_helpers.create_queue(QUEUE_SIZE) |
|
||||||
|
|
||||||
def empty(self): |
|
||||||
return self._queue.empty() |
|
||||||
|
|
||||||
def open_connection(self): |
|
||||||
self._conn = socket.socket(socket.AF_INET, socket.SOCK_STREAM) |
|
||||||
self._conn.connect((self.le_api, self.le_port)) |
|
||||||
|
|
||||||
def reopen_connection(self): |
|
||||||
self.close_connection() |
|
||||||
|
|
||||||
root_delay = MIN_DELAY |
|
||||||
while True: |
|
||||||
try: |
|
||||||
self.open_connection() |
|
||||||
return |
|
||||||
except Exception: |
|
||||||
if self.verbose: |
|
||||||
dbg("Unable to connect to Logentries") |
|
||||||
|
|
||||||
root_delay *= 2 |
|
||||||
if(root_delay > MAX_DELAY): |
|
||||||
root_delay = MAX_DELAY |
|
||||||
|
|
||||||
wait_for = root_delay + random.uniform(0, root_delay) |
|
||||||
|
|
||||||
try: |
|
||||||
time.sleep(wait_for) |
|
||||||
except KeyboardInterrupt: |
|
||||||
raise |
|
||||||
|
|
||||||
def close_connection(self): |
|
||||||
if self._conn is not None: |
|
||||||
self._conn.close() |
|
||||||
|
|
||||||
def run(self): |
|
||||||
try: |
|
||||||
# Open connection |
|
||||||
self.reopen_connection() |
|
||||||
|
|
||||||
# Send data in queue |
|
||||||
while True: |
|
||||||
# Take data from queue |
|
||||||
data = self._queue.get(block=True) |
|
||||||
|
|
||||||
# Replace newlines with Unicode line separator |
|
||||||
# for multi-line events |
|
||||||
if not le_helpers.is_unicode(data): |
|
||||||
multiline = le_helpers.create_unicode(data).replace( |
|
||||||
'\n', LINE_SEP) |
|
||||||
else: |
|
||||||
multiline = data.replace('\n', LINE_SEP) |
|
||||||
multiline += "\n" |
|
||||||
# Send data, reconnect if needed |
|
||||||
while True: |
|
||||||
try: |
|
||||||
self._conn.send(multiline.encode('utf-8')) |
|
||||||
except socket.error: |
|
||||||
self.reopen_connection() |
|
||||||
continue |
|
||||||
break |
|
||||||
except KeyboardInterrupt: |
|
||||||
if self.verbose: |
|
||||||
dbg("Logentries asynchronous socket client interrupted") |
|
||||||
|
|
||||||
self.close_connection() |
|
||||||
|
|
||||||
SocketAppender = PlainTextSocketAppender |
|
||||||
|
|
||||||
try: |
|
||||||
import ssl |
|
||||||
ssl_enabled = True |
|
||||||
except ImportError: # for systems without TLS support. |
|
||||||
ssl_enabled = False |
|
||||||
dbg("Unable to import ssl module. Will send over port 80.") |
|
||||||
else: |
|
||||||
class TLSSocketAppender(PlainTextSocketAppender): |
|
||||||
|
|
||||||
def open_connection(self): |
|
||||||
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) |
|
||||||
sock = ssl.wrap_socket( |
|
||||||
sock=sock, |
|
||||||
keyfile=None, |
|
||||||
certfile=None, |
|
||||||
server_side=False, |
|
||||||
cert_reqs=ssl.CERT_REQUIRED, |
|
||||||
ssl_version=getattr( |
|
||||||
ssl, |
|
||||||
'PROTOCOL_TLSv1_2', |
|
||||||
ssl.PROTOCOL_TLSv1 |
|
||||||
), |
|
||||||
ca_certs=certifi.where(), |
|
||||||
do_handshake_on_connect=True, |
|
||||||
suppress_ragged_eofs=True, |
|
||||||
) |
|
||||||
|
|
||||||
sock.connect((self.le_api, self.le_tls_port)) |
|
||||||
self._conn = sock |
|
||||||
|
|
||||||
|
|
||||||
class LogentriesHandler(logging.Handler): |
|
||||||
def __init__(self, token, use_tls=True, verbose=True, format=None, le_api=LE_API_DEFAULT, le_port=LE_PORT_DEFAULT, le_tls_port=LE_TLS_PORT_DEFAULT): |
|
||||||
logging.Handler.__init__(self) |
|
||||||
self.token = token |
|
||||||
self.good_config = True |
|
||||||
self.verbose = verbose |
|
||||||
# give the socket 10 seconds to flush, |
|
||||||
# otherwise drop logs |
|
||||||
self.timeout = 10 |
|
||||||
if not le_helpers.check_token(token): |
|
||||||
if self.verbose: |
|
||||||
dbg(INVALID_TOKEN) |
|
||||||
self.good_config = False |
|
||||||
if format is None: |
|
||||||
format = logging.Formatter('%(asctime)s : %(levelname)s, %(message)s', |
|
||||||
'%a %b %d %H:%M:%S %Z %Y') |
|
||||||
self.setFormatter(format) |
|
||||||
self.setLevel(logging.DEBUG) |
|
||||||
if use_tls and ssl_enabled: |
|
||||||
self._thread = TLSSocketAppender(verbose=verbose, le_api=le_api, le_port=le_port, le_tls_port=le_tls_port) |
|
||||||
else: |
|
||||||
self._thread = SocketAppender(verbose=verbose, le_api=le_api, le_port=le_port, le_tls_port=le_tls_port) |
|
||||||
|
|
||||||
def flush(self): |
|
||||||
# wait for all queued logs to be send |
|
||||||
now = time.time() |
|
||||||
while not self._thread.empty(): |
|
||||||
time.sleep(0.2) |
|
||||||
if time.time() - now > self.timeout: |
|
||||||
break |
|
||||||
|
|
||||||
def emit_raw(self, msg): |
|
||||||
if self.good_config and not self._thread.is_alive(): |
|
||||||
try: |
|
||||||
self._thread.start() |
|
||||||
if self.verbose: |
|
||||||
dbg("Starting Logentries Asynchronous Socket Appender") |
|
||||||
except RuntimeError: # It's already started. |
|
||||||
pass |
|
||||||
|
|
||||||
msg = self.token + msg |
|
||||||
try: |
|
||||||
self._thread._queue.put_nowait(msg) |
|
||||||
except Exception: |
|
||||||
# Queue is full, try to remove the oldest message and put again |
|
||||||
try: |
|
||||||
self._thread._queue.get_nowait() |
|
||||||
self._thread._queue.put_nowait(msg) |
|
||||||
except Exception: |
|
||||||
# Race condition, no need for any action here |
|
||||||
pass |
|
||||||
|
|
||||||
def emit(self, record): |
|
||||||
msg = self.format(record).rstrip('\n') |
|
||||||
self.emit_raw(msg) |
|
||||||
|
|
||||||
def close(self): |
|
||||||
logging.Handler.close(self) |
|
@ -1,11 +0,0 @@ |
|||||||
#!/usr/bin/env bash |
|
||||||
|
|
||||||
python3 -m pylint --disable=R,C,W $(eval echo <(find cereal) <(find opendbc) $(cat release/files_common release/files_common | tr '\n' ' ') | tr ' ' '\n' | grep "\.py$") |
|
||||||
|
|
||||||
exit_status=$? |
|
||||||
(( res = exit_status & 3 )) |
|
||||||
|
|
||||||
if [[ $res != 0 ]]; then |
|
||||||
echo "Pylint found errors in the code. Please fix and try again" |
|
||||||
exit 1 |
|
||||||
fi |
|
@ -1 +1 @@ |
|||||||
Subproject commit d3a79c6a421b4eec952eeb8d1546a4c3c3ff030e |
Subproject commit 2e556b8219185708ed974a4b6502796607d7ce0d |
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in new issue