parent
790732bea3
commit
2f92d577f9
99 changed files with 2756 additions and 1619 deletions
Binary file not shown.
Binary file not shown.
@ -0,0 +1,76 @@ |
|||||||
|
#from common.numpy_fast import clip |
||||||
|
from common.realtime import sec_since_boot |
||||||
|
from selfdrive.boardd.boardd import can_list_to_can_capnp |
||||||
|
from selfdrive.car import apply_std_steer_torque_limits |
||||||
|
from selfdrive.car.subaru import subarucan |
||||||
|
from selfdrive.car.subaru.values import CAR, DBC |
||||||
|
from selfdrive.can.packer import CANPacker |
||||||
|
|
||||||
|
|
||||||
|
class CarControllerParams(): |
||||||
|
def __init__(self, car_fingerprint): |
||||||
|
self.STEER_MAX = 2047 # max_steer 4095 |
||||||
|
self.STEER_STEP = 2 # how often we update the steer cmd |
||||||
|
self.STEER_DELTA_UP = 50 # torque increase per refresh, 0.8s to max |
||||||
|
self.STEER_DELTA_DOWN = 70 # torque decrease per refresh |
||||||
|
if car_fingerprint == CAR.IMPREZA: |
||||||
|
self.STEER_DRIVER_ALLOWANCE = 60 # allowed driver torque before start limiting |
||||||
|
self.STEER_DRIVER_MULTIPLIER = 10 # weight driver torque heavily |
||||||
|
self.STEER_DRIVER_FACTOR = 1 # from dbc |
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
class CarController(object): |
||||||
|
def __init__(self, car_fingerprint): |
||||||
|
self.start_time = sec_since_boot() |
||||||
|
self.lkas_active = False |
||||||
|
self.steer_idx = 0 |
||||||
|
self.apply_steer_last = 0 |
||||||
|
self.car_fingerprint = car_fingerprint |
||||||
|
self.es_distance_cnt = -1 |
||||||
|
self.es_lkas_cnt = -1 |
||||||
|
|
||||||
|
# Setup detection helper. Routes commands to |
||||||
|
# an appropriate CAN bus number. |
||||||
|
self.params = CarControllerParams(car_fingerprint) |
||||||
|
print(DBC) |
||||||
|
self.packer = CANPacker(DBC[car_fingerprint]['pt']) |
||||||
|
|
||||||
|
def update(self, sendcan, enabled, CS, frame, actuators, pcm_cancel_cmd, visual_alert): |
||||||
|
""" Controls thread """ |
||||||
|
|
||||||
|
P = self.params |
||||||
|
|
||||||
|
# Send CAN commands. |
||||||
|
can_sends = [] |
||||||
|
|
||||||
|
### STEER ### |
||||||
|
|
||||||
|
if (frame % P.STEER_STEP) == 0: |
||||||
|
|
||||||
|
final_steer = actuators.steer if enabled else 0. |
||||||
|
apply_steer = int(round(final_steer * P.STEER_MAX)) |
||||||
|
|
||||||
|
# limits due to driver torque |
||||||
|
|
||||||
|
apply_steer = int(round(apply_steer)) |
||||||
|
apply_steer = apply_std_steer_torque_limits(apply_steer, self.apply_steer_last, CS.steer_torque_driver, P) |
||||||
|
|
||||||
|
lkas_enabled = enabled and not CS.steer_not_allowed |
||||||
|
|
||||||
|
if not lkas_enabled: |
||||||
|
apply_steer = 0 |
||||||
|
|
||||||
|
can_sends.append(subarucan.create_steering_control(self.packer, CS.CP.carFingerprint, apply_steer, frame, P.STEER_STEP)) |
||||||
|
|
||||||
|
self.apply_steer_last = apply_steer |
||||||
|
|
||||||
|
if self.es_distance_cnt != CS.es_distance_msg["Counter"]: |
||||||
|
can_sends.append(subarucan.create_es_distance(self.packer, CS.es_distance_msg, pcm_cancel_cmd)) |
||||||
|
self.es_distance_cnt = CS.es_distance_msg["Counter"] |
||||||
|
|
||||||
|
if self.es_lkas_cnt != CS.es_lkas_msg["Counter"]: |
||||||
|
can_sends.append(subarucan.create_es_lkas(self.packer, CS.es_lkas_msg, visual_alert)) |
||||||
|
self.es_lkas_cnt = CS.es_lkas_msg["Counter"] |
||||||
|
|
||||||
|
sendcan.send(can_list_to_can_capnp(can_sends, msgtype='sendcan').to_bytes()) |
@ -0,0 +1,54 @@ |
|||||||
|
import copy |
||||||
|
from cereal import car |
||||||
|
from selfdrive.car.subaru.values import CAR |
||||||
|
|
||||||
|
VisualAlert = car.CarControl.HUDControl.VisualAlert |
||||||
|
|
||||||
|
def subaru_checksum(packer, values, addr): |
||||||
|
dat = packer.make_can_msg(addr, 0, values)[2] |
||||||
|
dat = [ord(i) for i in dat] |
||||||
|
return (sum(dat[1:]) + (addr >> 8) + addr) & 0xff |
||||||
|
|
||||||
|
def create_steering_control(packer, car_fingerprint, apply_steer, frame, steer_step): |
||||||
|
|
||||||
|
if car_fingerprint == CAR.IMPREZA: |
||||||
|
#counts from 0 to 15 then back to 0 + 16 for enable bit |
||||||
|
idx = ((frame / steer_step) % 16) |
||||||
|
|
||||||
|
values = { |
||||||
|
"Counter": idx, |
||||||
|
"LKAS_Output": apply_steer, |
||||||
|
"LKAS_Request": 1 if apply_steer != 0 else 0, |
||||||
|
"SET_1": 1 |
||||||
|
} |
||||||
|
values["Checksum"] = subaru_checksum(packer, values, 0x122) |
||||||
|
|
||||||
|
return packer.make_can_msg("ES_LKAS", 0, values) |
||||||
|
|
||||||
|
def create_steering_status(packer, car_fingerprint, apply_steer, frame, steer_step): |
||||||
|
|
||||||
|
if car_fingerprint == CAR.IMPREZA: |
||||||
|
values = {} |
||||||
|
values["Checksum"] = subaru_checksum(packer, {}, 0x322) |
||||||
|
|
||||||
|
return packer.make_can_msg("ES_LKAS_State", 0, values) |
||||||
|
|
||||||
|
def create_es_distance(packer, es_distance_msg, pcm_cancel_cmd): |
||||||
|
|
||||||
|
values = copy.copy(es_distance_msg) |
||||||
|
if pcm_cancel_cmd: |
||||||
|
values["Main"] = 1 |
||||||
|
|
||||||
|
values["Checksum"] = subaru_checksum(packer, values, 545) |
||||||
|
|
||||||
|
return packer.make_can_msg("ES_Distance", 0, values) |
||||||
|
|
||||||
|
def create_es_lkas(packer, es_lkas_msg, visual_alert): |
||||||
|
|
||||||
|
values = copy.copy(es_lkas_msg) |
||||||
|
if visual_alert == VisualAlert.steerRequired: |
||||||
|
values["Keep_Hands_On_Wheel"] = 1 |
||||||
|
|
||||||
|
values["Checksum"] = subaru_checksum(packer, values, 802) |
||||||
|
|
||||||
|
return packer.make_can_msg("ES_LKAS_State", 0, values) |
@ -1 +1 @@ |
|||||||
#define COMMA_VERSION "0.5.10-release" |
#define COMMA_VERSION "0.5.11-release" |
||||||
|
@ -0,0 +1,99 @@ |
|||||||
|
import psutil |
||||||
|
import time |
||||||
|
import os |
||||||
|
import sys |
||||||
|
import numpy as np |
||||||
|
import argparse |
||||||
|
import re |
||||||
|
|
||||||
|
''' |
||||||
|
System tools like top/htop can only show current cpu usage values, so I write this script to do statistics jobs. |
||||||
|
Features: |
||||||
|
Use psutil library to sample cpu usage(avergage for all cores) of OpenPilot processes, at a rate of 5 samples/sec. |
||||||
|
Do cpu usage statistics periodically, 5 seconds as a cycle. |
||||||
|
Caculate the average cpu usage within this cycle. |
||||||
|
Caculate minumium/maximium/accumulated_average cpu usage as long term inspections. |
||||||
|
Monitor multiple processes simuteneously. |
||||||
|
Sample usage: |
||||||
|
root@localhost:/data/openpilot$ python selfdrive/debug/cpu_usage_stat.py boardd,ubloxd |
||||||
|
('Add monitored proc:', './boardd') |
||||||
|
('Add monitored proc:', 'python locationd/ubloxd.py') |
||||||
|
boardd: 1.96%, min: 1.96%, max: 1.96%, acc: 1.96% |
||||||
|
ubloxd.py: 0.39%, min: 0.39%, max: 0.39%, acc: 0.39% |
||||||
|
''' |
||||||
|
|
||||||
|
# Do statistics every 5 seconds |
||||||
|
PRINT_INTERVAL = 5 |
||||||
|
SLEEP_INTERVAL = 0.2 |
||||||
|
|
||||||
|
monitored_proc_names = [ |
||||||
|
'ubloxd', 'thermald', 'uploader', 'controlsd', 'plannerd', 'radard', 'mapd', 'loggerd' , 'logmessaged', 'tombstoned', |
||||||
|
'logcatd', 'proclogd', 'boardd', 'pandad', './ui', 'calibrationd', 'locationd', 'visiond', 'sensord', 'updated', 'gpsd', 'athena'] |
||||||
|
|
||||||
|
|
||||||
|
def get_arg_parser(): |
||||||
|
parser = argparse.ArgumentParser( |
||||||
|
description="Unlogger and UI", |
||||||
|
formatter_class=argparse.ArgumentDefaultsHelpFormatter) |
||||||
|
|
||||||
|
parser.add_argument("proc_names", nargs="?", default='', |
||||||
|
help="Process names to be monitored, comma seperated") |
||||||
|
parser.add_argument("--list_all", nargs="?", type=bool, default=False, |
||||||
|
help="Show all running processes' cmdline") |
||||||
|
return parser |
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__": |
||||||
|
args = get_arg_parser().parse_args(sys.argv[1:]) |
||||||
|
if args.list_all: |
||||||
|
for p in psutil.process_iter(): |
||||||
|
print('cmdline', p.cmdline(), 'name', p.name()) |
||||||
|
sys.exit(0) |
||||||
|
|
||||||
|
if len(args.proc_names) > 0: |
||||||
|
monitored_proc_names = args.proc_names.split(',') |
||||||
|
monitored_procs = [] |
||||||
|
stats = {} |
||||||
|
for p in psutil.process_iter(): |
||||||
|
if p == psutil.Process(): |
||||||
|
continue |
||||||
|
matched = any([l for l in p.cmdline() if any([pn for pn in monitored_proc_names if re.match(r'.*{}.*'.format(pn), l, re.M | re.I)])]) |
||||||
|
if matched: |
||||||
|
k = ' '.join(p.cmdline()) |
||||||
|
print('Add monitored proc:', k) |
||||||
|
stats[k] = {'cpu_samples': [], 'avg_cpu': None, 'min': None, 'max': None} |
||||||
|
monitored_procs.append(p) |
||||||
|
i = 0 |
||||||
|
interval_int = int(PRINT_INTERVAL / SLEEP_INTERVAL) |
||||||
|
while True: |
||||||
|
for p in monitored_procs: |
||||||
|
k = ' '.join(p.cmdline()) |
||||||
|
stats[k]['cpu_samples'].append(p.cpu_percent()) |
||||||
|
time.sleep(SLEEP_INTERVAL) |
||||||
|
i += 1 |
||||||
|
if i % interval_int == 0: |
||||||
|
l = [] |
||||||
|
avg_cpus = [] |
||||||
|
for k, stat in stats.items(): |
||||||
|
if len(stat['cpu_samples']) <= 0: |
||||||
|
continue |
||||||
|
avg_cpu = np.array(stat['cpu_samples']).mean() |
||||||
|
c = len(stat['cpu_samples']) |
||||||
|
stat['cpu_samples'] = [] |
||||||
|
if not stat['avg_cpu']: |
||||||
|
stat['avg_cpu'] = avg_cpu |
||||||
|
else: |
||||||
|
stat['avg_cpu'] = (stat['avg_cpu'] * (c + i) + avg_cpu * c) / (c + i + c) |
||||||
|
if not stat['min'] or avg_cpu < stat['min']: |
||||||
|
stat['min'] = avg_cpu |
||||||
|
if not stat['max'] or avg_cpu > stat['max']: |
||||||
|
stat['max'] = avg_cpu |
||||||
|
msg = 'avg: {1:.2f}%, min: {2:.2f}%, max: {3:.2f}% {0}'.format(os.path.basename(k), stat['avg_cpu'], stat['min'], stat['max']) |
||||||
|
l.append((os.path.basename(k), avg_cpu, msg)) |
||||||
|
avg_cpus.append(avg_cpu) |
||||||
|
l.sort(key= lambda x: -x[1]) |
||||||
|
for x in l: |
||||||
|
print(x[2]) |
||||||
|
print('avg sum: {0:.2f}%\n'.format( |
||||||
|
sum([stat['avg_cpu'] for k, stat in stats.items()]) |
||||||
|
)) |
@ -0,0 +1,86 @@ |
|||||||
|
CC = clang
|
||||||
|
CXX = clang++
|
||||||
|
|
||||||
|
ARCH := $(shell uname -m)
|
||||||
|
OS := $(shell uname -o)
|
||||||
|
|
||||||
|
BASEDIR = ../..
|
||||||
|
PHONELIBS = ../../phonelibs
|
||||||
|
|
||||||
|
WARN_FLAGS = -Werror=implicit-function-declaration \
|
||||||
|
-Werror=incompatible-pointer-types \
|
||||||
|
-Werror=int-conversion \
|
||||||
|
-Werror=return-type \
|
||||||
|
-Werror=format-extra-args
|
||||||
|
|
||||||
|
CFLAGS = -std=gnu11 -g -fPIC -I../ -I../../ -O2 $(WARN_FLAGS)
|
||||||
|
CXXFLAGS = -std=c++11 -g -fPIC -I../ -I../../ -O2 $(WARN_FLAGS)
|
||||||
|
|
||||||
|
ZMQ_FLAGS = -I$(PHONELIBS)/zmq/aarch64/include
|
||||||
|
ZMQ_LIBS = -L$(PHONELIBS)/zmq/aarch64/lib \
|
||||||
|
-l:libczmq.a -l:libzmq.a \
|
||||||
|
-lgnustl_shared
|
||||||
|
|
||||||
|
JSON_FLAGS = -I$(PHONELIBS)/json/src
|
||||||
|
|
||||||
|
EXTRA_LIBS = -lpthread
|
||||||
|
|
||||||
|
ifeq ($(ARCH),x86_64) |
||||||
|
ZMQ_LIBS = -L$(BASEDIR)/external/zmq/lib \
|
||||||
|
-l:libczmq.a -l:libzmq.a
|
||||||
|
endif |
||||||
|
|
||||||
|
.PHONY: all |
||||||
|
all: ubloxd |
||||||
|
|
||||||
|
include ../common/cereal.mk |
||||||
|
|
||||||
|
OBJS = ublox_msg.o \
|
||||||
|
ubloxd_main.o \
|
||||||
|
../common/swaglog.o \
|
||||||
|
../common/params.o \
|
||||||
|
../common/util.o \
|
||||||
|
$(PHONELIBS)/json/src/json.o \
|
||||||
|
$(CEREAL_OBJS)
|
||||||
|
|
||||||
|
DEPS := $(OBJS:.o=.d) ubloxd.d ubloxd_test.d
|
||||||
|
|
||||||
|
ubloxd: ubloxd.o $(OBJS) |
||||||
|
@echo "[ LINK ] $@"
|
||||||
|
$(CXX) -fPIC -o '$@' $^ \
|
||||||
|
$(CEREAL_LIBS) \
|
||||||
|
$(ZMQ_LIBS) \
|
||||||
|
$(EXTRA_LIBS)
|
||||||
|
|
||||||
|
ubloxd_test: ubloxd_test.o $(OBJS) |
||||||
|
@echo "[ LINK ] $@"
|
||||||
|
$(CXX) -fPIC -o '$@' $^ \
|
||||||
|
$(CEREAL_LIBS) \
|
||||||
|
$(ZMQ_LIBS) \
|
||||||
|
$(EXTRA_LIBS)
|
||||||
|
|
||||||
|
%.o: %.cc |
||||||
|
@echo "[ CXX ] $@"
|
||||||
|
$(CXX) $(CXXFLAGS) -MMD \
|
||||||
|
-Iinclude -I.. -I../.. \
|
||||||
|
$(CEREAL_CXXFLAGS) \
|
||||||
|
$(ZMQ_FLAGS) \
|
||||||
|
$(JSON_FLAGS) \
|
||||||
|
-I../ \
|
||||||
|
-I../../ \
|
||||||
|
-c -o '$@' '$<'
|
||||||
|
|
||||||
|
%.o: %.c |
||||||
|
@echo "[ CC ] $@"
|
||||||
|
$(CC) $(CFLAGS) -MMD \
|
||||||
|
-Iinclude -I.. -I../.. \
|
||||||
|
$(CEREAL_CFLAGS) \
|
||||||
|
$(ZMQ_FLAGS) \
|
||||||
|
$(JSON_FLAGS) \
|
||||||
|
-c -o '$@' '$<'
|
||||||
|
|
||||||
|
.PHONY: clean |
||||||
|
clean: |
||||||
|
rm -f ubloxd ubloxd.d ubloxd.o ubloxd_test ubloxd_test.o ubloxd_test.d $(OBJS) $(DEPS)
|
||||||
|
|
||||||
|
-include $(DEPS) |
@ -0,0 +1,82 @@ |
|||||||
|
#!/usr/bin/env python2 |
||||||
|
import subprocess |
||||||
|
import os |
||||||
|
import sys |
||||||
|
import argparse |
||||||
|
import tempfile |
||||||
|
|
||||||
|
from ubloxd_py_test import parser_test |
||||||
|
from ubloxd_regression_test import compare_results |
||||||
|
|
||||||
|
|
||||||
|
def mkdirs_exists_ok(path): |
||||||
|
try: |
||||||
|
os.makedirs(path) |
||||||
|
except OSError: |
||||||
|
if not os.path.isdir(path): |
||||||
|
raise |
||||||
|
|
||||||
|
|
||||||
|
def main(args): |
||||||
|
cur_dir = os.path.dirname(os.path.realpath(__file__)) |
||||||
|
ubloxd_dir = os.path.join(cur_dir, '../') |
||||||
|
|
||||||
|
cc_output_dir = os.path.join(args.output_dir, 'cc') |
||||||
|
mkdirs_exists_ok(cc_output_dir) |
||||||
|
|
||||||
|
py_output_dir = os.path.join(args.output_dir, 'py') |
||||||
|
mkdirs_exists_ok(py_output_dir) |
||||||
|
|
||||||
|
archive_file = os.path.join(cur_dir, args.stream_gz_file) |
||||||
|
|
||||||
|
try: |
||||||
|
print('Extracting stream file') |
||||||
|
subprocess.check_call(['tar', 'zxf', archive_file], cwd=tempfile.gettempdir()) |
||||||
|
stream_file_path = os.path.join(tempfile.gettempdir(), 'ubloxRaw.stream') |
||||||
|
|
||||||
|
if not os.path.isfile(stream_file_path): |
||||||
|
print('Extract file failed') |
||||||
|
sys.exit(-3) |
||||||
|
|
||||||
|
print('Compiling test app...') |
||||||
|
subprocess.check_call(["make", "ubloxd_test"], cwd=ubloxd_dir) |
||||||
|
|
||||||
|
print('Run regression test - CC parser...') |
||||||
|
if args.valgrind: |
||||||
|
subprocess.check_call(["valgrind", "--leak-check=full", os.path.join(ubloxd_dir, 'ubloxd_test'), stream_file_path, cc_output_dir]) |
||||||
|
else: |
||||||
|
subprocess.check_call([os.path.join(ubloxd_dir, 'ubloxd_test'), stream_file_path, cc_output_dir]) |
||||||
|
|
||||||
|
print('Running regression test - py parser...') |
||||||
|
parser_test(stream_file_path, py_output_dir) |
||||||
|
|
||||||
|
print('Running regression test - compare result...') |
||||||
|
r = compare_results(cc_output_dir, py_output_dir) |
||||||
|
|
||||||
|
print('All done!') |
||||||
|
|
||||||
|
subprocess.check_call(["rm", stream_file_path]) |
||||||
|
subprocess.check_call(["rm", '-rf', cc_output_dir]) |
||||||
|
subprocess.check_call(["rm", '-rf', py_output_dir]) |
||||||
|
sys.exit(r) |
||||||
|
|
||||||
|
except subprocess.CalledProcessError as e: |
||||||
|
print('CI test failed with {}'.format(e.returncode)) |
||||||
|
sys.exit(e.returncode) |
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__": |
||||||
|
parser = argparse.ArgumentParser(description="Ubloxd CI test", |
||||||
|
formatter_class=argparse.ArgumentDefaultsHelpFormatter) |
||||||
|
|
||||||
|
parser.add_argument("stream_gz_file", nargs='?', default='ubloxRaw.tar.gz', |
||||||
|
help="UbloxRaw data stream zip file") |
||||||
|
|
||||||
|
parser.add_argument("output_dir", nargs='?', default='out', |
||||||
|
help="Output events temp directory") |
||||||
|
|
||||||
|
parser.add_argument("--valgrind", default=False, action='store_true', |
||||||
|
help="Run in valgrind") |
||||||
|
|
||||||
|
args = parser.parse_args() |
||||||
|
main(args) |
@ -0,0 +1,55 @@ |
|||||||
|
#!/usr/bin/env python |
||||||
|
import os |
||||||
|
import ublox |
||||||
|
from common import realtime |
||||||
|
from ubloxd import gen_raw, gen_solution |
||||||
|
import zmq |
||||||
|
import selfdrive.messaging as messaging |
||||||
|
from selfdrive.services import service_list |
||||||
|
|
||||||
|
|
||||||
|
unlogger = os.getenv("UNLOGGER") is not None # debug prints |
||||||
|
|
||||||
|
def main(gctx=None): |
||||||
|
context = zmq.Context() |
||||||
|
poller = zmq.Poller() |
||||||
|
|
||||||
|
context = zmq.Context() |
||||||
|
gpsLocationExternal = messaging.pub_sock(context, service_list['gpsLocationExternal'].port) |
||||||
|
ubloxGnss = messaging.pub_sock(context, service_list['ubloxGnss'].port) |
||||||
|
|
||||||
|
# ubloxRaw = messaging.sub_sock(context, service_list['ubloxRaw'].port, poller) |
||||||
|
|
||||||
|
# buffer with all the messages that still need to be input into the kalman |
||||||
|
while 1: |
||||||
|
polld = poller.poll(timeout=1000) |
||||||
|
for sock, mode in polld: |
||||||
|
if mode != zmq.POLLIN: |
||||||
|
continue |
||||||
|
logs = messaging.drain_sock(sock) |
||||||
|
for log in logs: |
||||||
|
buff = log.ubloxRaw |
||||||
|
time = log.logMonoTime |
||||||
|
msg = ublox.UBloxMessage() |
||||||
|
msg.add(buff) |
||||||
|
if msg.valid(): |
||||||
|
if msg.name() == 'NAV_PVT': |
||||||
|
sol = gen_solution(msg) |
||||||
|
if unlogger: |
||||||
|
sol.logMonoTime = time |
||||||
|
else: |
||||||
|
sol.logMonoTime = int(realtime.sec_since_boot() * 1e9) |
||||||
|
gpsLocationExternal.send(sol.to_bytes()) |
||||||
|
elif msg.name() == 'RXM_RAW': |
||||||
|
raw = gen_raw(msg) |
||||||
|
if unlogger: |
||||||
|
raw.logMonoTime = time |
||||||
|
else: |
||||||
|
raw.logMonoTime = int(realtime.sec_since_boot() * 1e9) |
||||||
|
ubloxGnss.send(raw.to_bytes()) |
||||||
|
else: |
||||||
|
print "INVALID MESSAGE" |
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__": |
||||||
|
main() |
@ -0,0 +1,77 @@ |
|||||||
|
import sys |
||||||
|
import os |
||||||
|
|
||||||
|
from ublox import UBloxMessage |
||||||
|
from ubloxd import gen_solution, gen_raw, gen_nav_data |
||||||
|
from common import realtime |
||||||
|
|
||||||
|
|
||||||
|
def mkdirs_exists_ok(path): |
||||||
|
try: |
||||||
|
os.makedirs(path) |
||||||
|
except OSError: |
||||||
|
if not os.path.isdir(path): |
||||||
|
raise |
||||||
|
|
||||||
|
|
||||||
|
def parser_test(fn, prefix): |
||||||
|
nav_frame_buffer = {} |
||||||
|
nav_frame_buffer[0] = {} |
||||||
|
for i in xrange(1, 33): |
||||||
|
nav_frame_buffer[0][i] = {} |
||||||
|
|
||||||
|
if not os.path.exists(prefix): |
||||||
|
print('Prefix invalid') |
||||||
|
sys.exit(-1) |
||||||
|
|
||||||
|
with open(fn, 'rb') as f: |
||||||
|
i = 0 |
||||||
|
saved_i = 0 |
||||||
|
msg = UBloxMessage() |
||||||
|
while True: |
||||||
|
n = msg.needed_bytes() |
||||||
|
b = f.read(n) |
||||||
|
if not b: |
||||||
|
break |
||||||
|
msg.add(b) |
||||||
|
if msg.valid(): |
||||||
|
i += 1 |
||||||
|
if msg.name() == 'NAV_PVT': |
||||||
|
sol = gen_solution(msg) |
||||||
|
sol.logMonoTime = int(realtime.sec_since_boot() * 1e9) |
||||||
|
with open(os.path.join(prefix, str(saved_i)), 'wb') as f1: |
||||||
|
f1.write(sol.to_bytes()) |
||||||
|
saved_i += 1 |
||||||
|
elif msg.name() == 'RXM_RAW': |
||||||
|
raw = gen_raw(msg) |
||||||
|
raw.logMonoTime = int(realtime.sec_since_boot() * 1e9) |
||||||
|
with open(os.path.join(prefix, str(saved_i)), 'wb') as f1: |
||||||
|
f1.write(raw.to_bytes()) |
||||||
|
saved_i += 1 |
||||||
|
elif msg.name() == 'RXM_SFRBX': |
||||||
|
nav = gen_nav_data(msg, nav_frame_buffer) |
||||||
|
if nav is not None: |
||||||
|
nav.logMonoTime = int(realtime.sec_since_boot() * 1e9) |
||||||
|
with open(os.path.join(prefix, str(saved_i)), 'wb') as f1: |
||||||
|
f1.write(nav.to_bytes()) |
||||||
|
saved_i += 1 |
||||||
|
|
||||||
|
msg = UBloxMessage() |
||||||
|
msg.debug_level = 0 |
||||||
|
print('Parsed {} msgs'.format(i)) |
||||||
|
print('Generated {} cereal events'.format(saved_i)) |
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__": |
||||||
|
if len(sys.argv) < 3: |
||||||
|
print('Format: ubloxd_py_test.py file_path prefix') |
||||||
|
sys.exit(0) |
||||||
|
|
||||||
|
fn = sys.argv[1] |
||||||
|
if not os.path.isfile(fn): |
||||||
|
print('File path invalid') |
||||||
|
sys.exit(0) |
||||||
|
|
||||||
|
prefix = sys.argv[2] |
||||||
|
mkdirs_exists_ok(prefix) |
||||||
|
parser_test(fn, prefix) |
@ -0,0 +1,96 @@ |
|||||||
|
#!/usr/bin/env python |
||||||
|
import os |
||||||
|
import sys |
||||||
|
import argparse |
||||||
|
|
||||||
|
from cereal import log |
||||||
|
from common.basedir import BASEDIR |
||||||
|
os.environ['BASEDIR'] = BASEDIR |
||||||
|
|
||||||
|
|
||||||
|
def get_arg_parser(): |
||||||
|
parser = argparse.ArgumentParser( |
||||||
|
description="Compare two result files", |
||||||
|
formatter_class=argparse.ArgumentDefaultsHelpFormatter) |
||||||
|
|
||||||
|
parser.add_argument("dir1", nargs='?', default='/data/ubloxdc', |
||||||
|
help="Directory path 1 from which events are loaded") |
||||||
|
|
||||||
|
parser.add_argument("dir2", nargs='?', default='/data/ubloxdpy', |
||||||
|
help="Directory path 2 from which msgs are loaded") |
||||||
|
|
||||||
|
return parser |
||||||
|
|
||||||
|
|
||||||
|
def read_file(fn): |
||||||
|
with open(fn, 'rb') as f: |
||||||
|
return f.read() |
||||||
|
|
||||||
|
|
||||||
|
def compare_results(dir1, dir2): |
||||||
|
onlyfiles1 = [f for f in os.listdir(dir1) if os.path.isfile(os.path.join(dir1, f))] |
||||||
|
onlyfiles1.sort() |
||||||
|
|
||||||
|
onlyfiles2 = [f for f in os.listdir(dir2) if os.path.isfile(os.path.join(dir2, f))] |
||||||
|
onlyfiles2.sort() |
||||||
|
|
||||||
|
if len(onlyfiles1) != len(onlyfiles2): |
||||||
|
print('len mismatch: {} != {}'.format(len(onlyfiles1), len(onlyfiles2))) |
||||||
|
return -1 |
||||||
|
events1 = [log.Event.from_bytes(read_file(os.path.join(dir1, f))) for f in onlyfiles1] |
||||||
|
events2 = [log.Event.from_bytes(read_file(os.path.join(dir2, f))) for f in onlyfiles2] |
||||||
|
|
||||||
|
for i in range(len(events1)): |
||||||
|
if events1[i].which() != events2[i].which(): |
||||||
|
print('event {} type mismatch: {} != {}'.format(i, events1[i].which(), events2[i].which())) |
||||||
|
return -2 |
||||||
|
if events1[i].which() == 'gpsLocationExternal': |
||||||
|
old_gps = events1[i].gpsLocationExternal |
||||||
|
gps = events2[i].gpsLocationExternal |
||||||
|
# print(gps, old_gps) |
||||||
|
attrs = ['flags', 'latitude', 'longitude', 'altitude', 'speed', 'bearing', |
||||||
|
'accuracy', 'timestamp', 'source', 'vNED', 'verticalAccuracy', 'bearingAccuracy', 'speedAccuracy'] |
||||||
|
for attr in attrs: |
||||||
|
o = getattr(old_gps, attr) |
||||||
|
n = getattr(gps, attr) |
||||||
|
if attr == 'vNED': |
||||||
|
if len(o) != len(n): |
||||||
|
print('Gps vNED len mismatch', o, n) |
||||||
|
return -3 |
||||||
|
else: |
||||||
|
for i in range(len(o)): |
||||||
|
if abs(o[i] - n[i]) > 1e-3: |
||||||
|
print('Gps vNED mismatch', o, n) |
||||||
|
return |
||||||
|
elif o != n: |
||||||
|
print('Gps mismatch', attr, o, n) |
||||||
|
return -4 |
||||||
|
elif events1[i].which() == 'ubloxGnss': |
||||||
|
old_gnss = events1[i].ubloxGnss |
||||||
|
gnss = events2[i].ubloxGnss |
||||||
|
if old_gnss.which() == 'measurementReport' and gnss.which() == 'measurementReport': |
||||||
|
attrs = ['gpsWeek', 'leapSeconds', 'measurements', 'numMeas', 'rcvTow', 'receiverStatus', 'schema'] |
||||||
|
for attr in attrs: |
||||||
|
o = getattr(old_gnss.measurementReport, attr) |
||||||
|
n = getattr(gnss.measurementReport, attr) |
||||||
|
if str(o) != str(n): |
||||||
|
print('measurementReport {} mismatched'.format(attr)) |
||||||
|
return -5 |
||||||
|
if not (str(old_gnss.measurementReport) == str(gnss.measurementReport)): |
||||||
|
print('Gnss measurementReport mismatched!') |
||||||
|
print('gnss measurementReport old', old_gnss.measurementReport.measurements) |
||||||
|
print('gnss measurementReport new', gnss.measurementReport.measurements) |
||||||
|
return -6 |
||||||
|
elif old_gnss.which() == 'ephemeris' and gnss.which() == 'ephemeris': |
||||||
|
if not (str(old_gnss.ephemeris) == str(gnss.ephemeris)): |
||||||
|
print('Gnss ephemeris mismatched!') |
||||||
|
print('gnss ephemeris old', old_gnss.ephemeris) |
||||||
|
print('gnss ephemeris new', gnss.ephemeris) |
||||||
|
return -7 |
||||||
|
print('All {} events matched!'.format(len(events1))) |
||||||
|
return 0 |
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__": |
||||||
|
args = get_arg_parser().parse_args(sys.argv[1:]) |
||||||
|
compare_results(args.dir1, args.dir2) |
@ -0,0 +1,375 @@ |
|||||||
|
#include <stdio.h> |
||||||
|
#include <stdlib.h> |
||||||
|
#include <string.h> |
||||||
|
#include <signal.h> |
||||||
|
#include <unistd.h> |
||||||
|
#include <sched.h> |
||||||
|
#include <sys/time.h> |
||||||
|
#include <sys/cdefs.h> |
||||||
|
#include <sys/types.h> |
||||||
|
#include <sys/time.h> |
||||||
|
#include <assert.h> |
||||||
|
#include <math.h> |
||||||
|
#include <ctime> |
||||||
|
#include <chrono> |
||||||
|
#include <map> |
||||||
|
#include <vector> |
||||||
|
#include <algorithm> |
||||||
|
|
||||||
|
#include <zmq.h> |
||||||
|
#include <capnp/serialize.h> |
||||||
|
#include "cereal/gen/cpp/log.capnp.h" |
||||||
|
|
||||||
|
#include "common/params.h" |
||||||
|
#include "common/swaglog.h" |
||||||
|
#include "common/timing.h" |
||||||
|
|
||||||
|
#include "ublox_msg.h" |
||||||
|
|
||||||
|
#define UBLOX_MSG_SIZE(hdr) (*(uint16_t *)&hdr[4]) |
||||||
|
#define GET_FIELD_U(w, nb, pos) (((w) >> (pos)) & ((1<<(nb))-1)) |
||||||
|
|
||||||
|
namespace ublox { |
||||||
|
|
||||||
|
inline int twos_complement(uint32_t v, uint32_t nb) { |
||||||
|
int sign = v >> (nb - 1); |
||||||
|
int value = v; |
||||||
|
if(sign != 0) |
||||||
|
value = value - (1 << nb); |
||||||
|
return value; |
||||||
|
} |
||||||
|
|
||||||
|
inline int GET_FIELD_S(uint32_t w, uint32_t nb, uint32_t pos) { |
||||||
|
int v = GET_FIELD_U(w, nb, pos); |
||||||
|
return twos_complement(v, nb); |
||||||
|
} |
||||||
|
|
||||||
|
class EphemerisData { |
||||||
|
public: |
||||||
|
EphemerisData(uint8_t svId, subframes_map subframes) { |
||||||
|
this->svId = svId; |
||||||
|
int week_no = GET_FIELD_U(subframes[1][2+0], 10, 20); |
||||||
|
int t_gd = GET_FIELD_S(subframes[1][2+4], 8, 6); |
||||||
|
int iodc = (GET_FIELD_U(subframes[1][2+0], 2, 6) << 8) | GET_FIELD_U( |
||||||
|
subframes[1][2+5], 8, 22); |
||||||
|
|
||||||
|
int t_oc = GET_FIELD_U(subframes[1][2+5], 16, 6); |
||||||
|
int a_f2 = GET_FIELD_S(subframes[1][2+6], 8, 22); |
||||||
|
int a_f1 = GET_FIELD_S(subframes[1][2+6], 16, 6); |
||||||
|
int a_f0 = GET_FIELD_S(subframes[1][2+7], 22, 8); |
||||||
|
|
||||||
|
int c_rs = GET_FIELD_S(subframes[2][2+0], 16, 6); |
||||||
|
int delta_n = GET_FIELD_S(subframes[2][2+1], 16, 14); |
||||||
|
int m_0 = (GET_FIELD_S(subframes[2][2+1], 8, 6) << 24) | GET_FIELD_U( |
||||||
|
subframes[2][2+2], 24, 6); |
||||||
|
int c_uc = GET_FIELD_S(subframes[2][2+3], 16, 14); |
||||||
|
int e = (GET_FIELD_U(subframes[2][2+3], 8, 6) << 24) | GET_FIELD_U(subframes[2][2+4], 24, 6); |
||||||
|
int c_us = GET_FIELD_S(subframes[2][2+5], 16, 14); |
||||||
|
uint32_t a_powhalf = (GET_FIELD_U(subframes[2][2+5], 8, 6) << 24) | GET_FIELD_U( |
||||||
|
subframes[2][2+6], 24, 6); |
||||||
|
int t_oe = GET_FIELD_U(subframes[2][2+7], 16, 14); |
||||||
|
|
||||||
|
int c_ic = GET_FIELD_S(subframes[3][2+0], 16, 14); |
||||||
|
int omega_0 = (GET_FIELD_S(subframes[3][2+0], 8, 6) << 24) | GET_FIELD_U( |
||||||
|
subframes[3][2+1], 24, 6); |
||||||
|
int c_is = GET_FIELD_S(subframes[3][2+2], 16, 14); |
||||||
|
int i_0 = (GET_FIELD_S(subframes[3][2+2], 8, 6) << 24) | GET_FIELD_U( |
||||||
|
subframes[3][2+3], 24, 6); |
||||||
|
int c_rc = GET_FIELD_S(subframes[3][2+4], 16, 14); |
||||||
|
int w = (GET_FIELD_S(subframes[3][2+4], 8, 6) << 24) | GET_FIELD_U(subframes[3][5], 24, 6); |
||||||
|
int omega_dot = GET_FIELD_S(subframes[3][2+6], 24, 6); |
||||||
|
int idot = GET_FIELD_S(subframes[3][2+7], 14, 8); |
||||||
|
|
||||||
|
this->_rsvd1 = GET_FIELD_U(subframes[1][2+1], 23, 6); |
||||||
|
this->_rsvd2 = GET_FIELD_U(subframes[1][2+2], 24, 6); |
||||||
|
this->_rsvd3 = GET_FIELD_U(subframes[1][2+3], 24, 6); |
||||||
|
this->_rsvd4 = GET_FIELD_U(subframes[1][2+4], 16, 14); |
||||||
|
this->aodo = GET_FIELD_U(subframes[2][2+7], 5, 8); |
||||||
|
|
||||||
|
double gpsPi = 3.1415926535898; |
||||||
|
|
||||||
|
// now form variables in radians, meters and seconds etc
|
||||||
|
this->Tgd = t_gd * pow(2, -31); |
||||||
|
this->A = pow(a_powhalf * pow(2, -19), 2.0); |
||||||
|
this->cic = c_ic * pow(2, -29); |
||||||
|
this->cis = c_is * pow(2, -29); |
||||||
|
this->crc = c_rc * pow(2, -5); |
||||||
|
this->crs = c_rs * pow(2, -5); |
||||||
|
this->cuc = c_uc * pow(2, -29); |
||||||
|
this->cus = c_us * pow(2, -29); |
||||||
|
this->deltaN = delta_n * pow(2, -43) * gpsPi; |
||||||
|
this->ecc = e * pow(2, -33); |
||||||
|
this->i0 = i_0 * pow(2, -31) * gpsPi; |
||||||
|
this->idot = idot * pow(2, -43) * gpsPi; |
||||||
|
this->M0 = m_0 * pow(2, -31) * gpsPi; |
||||||
|
this->omega = w * pow(2, -31) * gpsPi; |
||||||
|
this->omega_dot = omega_dot * pow(2, -43) * gpsPi; |
||||||
|
this->omega0 = omega_0 * pow(2, -31) * gpsPi; |
||||||
|
this->toe = t_oe * pow(2, 4); |
||||||
|
|
||||||
|
this->toc = t_oc * pow(2, 4); |
||||||
|
this->gpsWeek = week_no; |
||||||
|
this->af0 = a_f0 * pow(2, -31); |
||||||
|
this->af1 = a_f1 * pow(2, -43); |
||||||
|
this->af2 = a_f2 * pow(2, -55); |
||||||
|
|
||||||
|
uint32_t iode1 = GET_FIELD_U(subframes[2][2+0], 8, 22); |
||||||
|
uint32_t iode2 = GET_FIELD_U(subframes[3][2+7], 8, 22); |
||||||
|
this->valid = (iode1 == iode2) && (iode1 == (iodc & 0xff)); |
||||||
|
this->iode = iode1; |
||||||
|
|
||||||
|
if (GET_FIELD_U(subframes[4][2+0], 6, 22) == 56 && |
||||||
|
GET_FIELD_U(subframes[4][2+0], 2, 28) == 1 && |
||||||
|
GET_FIELD_U(subframes[5][2+0], 2, 28) == 1) { |
||||||
|
double a0 = GET_FIELD_S(subframes[4][2], 8, 14) * pow(2, -30); |
||||||
|
double a1 = GET_FIELD_S(subframes[4][2], 8, 6) * pow(2, -27); |
||||||
|
double a2 = GET_FIELD_S(subframes[4][3], 8, 22) * pow(2, -24); |
||||||
|
double a3 = GET_FIELD_S(subframes[4][3], 8, 14) * pow(2, -24); |
||||||
|
double b0 = GET_FIELD_S(subframes[4][3], 8, 6) * pow(2, 11); |
||||||
|
double b1 = GET_FIELD_S(subframes[4][4], 8, 22) * pow(2, 14); |
||||||
|
double b2 = GET_FIELD_S(subframes[4][4], 8, 14) * pow(2, 16); |
||||||
|
double b3 = GET_FIELD_S(subframes[4][4], 8, 6) * pow(2, 16); |
||||||
|
this->ionoAlpha[0] = a0;this->ionoAlpha[1] = a1;this->ionoAlpha[2] = a2;this->ionoAlpha[3] = a3; |
||||||
|
this->ionoBeta[0] = b0;this->ionoBeta[1] = b1;this->ionoBeta[2] = b2;this->ionoBeta[3] = b3; |
||||||
|
this->ionoCoeffsValid = true; |
||||||
|
} else { |
||||||
|
this->ionoCoeffsValid = false; |
||||||
|
} |
||||||
|
} |
||||||
|
uint16_t svId; |
||||||
|
double Tgd, A, cic, cis, crc, crs, cuc, cus, deltaN, ecc, i0, idot, M0, omega, omega_dot, omega0, toe, toc; |
||||||
|
uint32_t gpsWeek, iode, _rsvd1, _rsvd2, _rsvd3, _rsvd4, aodo; |
||||||
|
double af0, af1, af2; |
||||||
|
bool valid; |
||||||
|
double ionoAlpha[4], ionoBeta[4]; |
||||||
|
bool ionoCoeffsValid; |
||||||
|
}; |
||||||
|
|
||||||
|
UbloxMsgParser::UbloxMsgParser() :bytes_in_parse_buf(0) { |
||||||
|
nav_frame_buffer[0U] = std::map<uint8_t, subframes_map>(); |
||||||
|
for(int i = 1;i < 33;i++) |
||||||
|
nav_frame_buffer[0U][i] = subframes_map(); |
||||||
|
} |
||||||
|
|
||||||
|
inline int UbloxMsgParser::needed_bytes() { |
||||||
|
// Msg header incomplete?
|
||||||
|
if(bytes_in_parse_buf < UBLOX_HEADER_SIZE) |
||||||
|
return UBLOX_HEADER_SIZE + UBLOX_CHECKSUM_SIZE - bytes_in_parse_buf; |
||||||
|
uint16_t needed = UBLOX_MSG_SIZE(msg_parse_buf) + UBLOX_HEADER_SIZE + UBLOX_CHECKSUM_SIZE; |
||||||
|
// too much data
|
||||||
|
if(needed < (uint16_t)bytes_in_parse_buf) |
||||||
|
return -1; |
||||||
|
return needed - (uint16_t)bytes_in_parse_buf; |
||||||
|
} |
||||||
|
|
||||||
|
inline bool UbloxMsgParser::valid_cheksum() { |
||||||
|
uint8_t ck_a = 0, ck_b = 0; |
||||||
|
for(int i = 2; i < bytes_in_parse_buf - UBLOX_CHECKSUM_SIZE;i++) { |
||||||
|
ck_a = (ck_a + msg_parse_buf[i]) & 0xFF; |
||||||
|
ck_b = (ck_b + ck_a) & 0xFF; |
||||||
|
} |
||||||
|
if(ck_a != msg_parse_buf[bytes_in_parse_buf - 2]) { |
||||||
|
LOGD("Checksum a mismtach: %02X, %02X", ck_a, msg_parse_buf[6]); |
||||||
|
return false; |
||||||
|
} |
||||||
|
if(ck_b != msg_parse_buf[bytes_in_parse_buf - 1]) { |
||||||
|
LOGD("Checksum b mismtach: %02X, %02X", ck_b, msg_parse_buf[7]); |
||||||
|
return false; |
||||||
|
} |
||||||
|
return true; |
||||||
|
} |
||||||
|
|
||||||
|
inline bool UbloxMsgParser::valid() { |
||||||
|
return bytes_in_parse_buf >= UBLOX_HEADER_SIZE + UBLOX_CHECKSUM_SIZE && |
||||||
|
needed_bytes() == 0 && |
||||||
|
valid_cheksum(); |
||||||
|
} |
||||||
|
|
||||||
|
inline bool UbloxMsgParser::valid_so_far() { |
||||||
|
if(bytes_in_parse_buf > 0 && msg_parse_buf[0] != PREAMBLE1) { |
||||||
|
//LOGD("PREAMBLE1 invalid, %02X.", msg_parse_buf[0]);
|
||||||
|
return false; |
||||||
|
} |
||||||
|
if(bytes_in_parse_buf > 1 && msg_parse_buf[1] != PREAMBLE2) { |
||||||
|
//LOGD("PREAMBLE2 invalid, %02X.", msg_parse_buf[1]);
|
||||||
|
return false; |
||||||
|
} |
||||||
|
if(needed_bytes() == 0 && !valid()) |
||||||
|
return false; |
||||||
|
return true; |
||||||
|
} |
||||||
|
|
||||||
|
kj::Array<capnp::word> UbloxMsgParser::gen_solution() { |
||||||
|
nav_pvt_msg *msg = (nav_pvt_msg *)&msg_parse_buf[UBLOX_HEADER_SIZE]; |
||||||
|
capnp::MallocMessageBuilder msg_builder; |
||||||
|
cereal::Event::Builder event = msg_builder.initRoot<cereal::Event>(); |
||||||
|
event.setLogMonoTime(nanos_since_boot()); |
||||||
|
auto gpsLoc = event.initGpsLocationExternal(); |
||||||
|
gpsLoc.setSource(cereal::GpsLocationData::SensorSource::UBLOX); |
||||||
|
gpsLoc.setFlags(msg->flags); |
||||||
|
gpsLoc.setLatitude(msg->lat * 1e-07); |
||||||
|
gpsLoc.setLongitude(msg->lon * 1e-07); |
||||||
|
gpsLoc.setAltitude(msg->height * 1e-03); |
||||||
|
gpsLoc.setSpeed(msg->gSpeed * 1e-03); |
||||||
|
gpsLoc.setBearing(msg->headMot * 1e-5); |
||||||
|
gpsLoc.setAccuracy(msg->hAcc * 1e-03); |
||||||
|
std::tm timeinfo = std::tm(); |
||||||
|
timeinfo.tm_year = msg->year - 1900; |
||||||
|
timeinfo.tm_mon = msg->month - 1; |
||||||
|
timeinfo.tm_mday = msg->day; |
||||||
|
timeinfo.tm_hour = msg->hour; |
||||||
|
timeinfo.tm_min = msg->min; |
||||||
|
timeinfo.tm_sec = msg->sec; |
||||||
|
std::time_t utc_tt = timegm(&timeinfo); |
||||||
|
gpsLoc.setTimestamp(utc_tt * 1e+03 + msg->nano * 1e-06); |
||||||
|
float f[] = { msg->velN * 1e-03f, msg->velE * 1e-03f, msg->velD * 1e-03f }; |
||||||
|
kj::ArrayPtr<const float> ap(&f[0], sizeof(f) / sizeof(f[0])); |
||||||
|
gpsLoc.setVNED(ap); |
||||||
|
gpsLoc.setVerticalAccuracy(msg->vAcc * 1e-03); |
||||||
|
gpsLoc.setSpeedAccuracy(msg->sAcc * 1e-03); |
||||||
|
gpsLoc.setBearingAccuracy(msg->headAcc * 1e-05); |
||||||
|
return capnp::messageToFlatArray(msg_builder); |
||||||
|
} |
||||||
|
|
||||||
|
inline bool bit_to_bool(uint8_t val, int shifts) { |
||||||
|
return (val & (1 << shifts)) ? true : false; |
||||||
|
} |
||||||
|
|
||||||
|
kj::Array<capnp::word> UbloxMsgParser::gen_raw() { |
||||||
|
rxm_raw_msg *msg = (rxm_raw_msg *)&msg_parse_buf[UBLOX_HEADER_SIZE]; |
||||||
|
if(bytes_in_parse_buf != ( |
||||||
|
UBLOX_HEADER_SIZE + sizeof(rxm_raw_msg) + msg->numMeas * sizeof(rxm_raw_msg_extra) + UBLOX_CHECKSUM_SIZE |
||||||
|
)) { |
||||||
|
LOGD("Invalid measurement size %u, %u, %u, %u", msg->numMeas, bytes_in_parse_buf, sizeof(rxm_raw_msg_extra), sizeof(rxm_raw_msg)); |
||||||
|
return kj::Array<capnp::word>(); |
||||||
|
} |
||||||
|
rxm_raw_msg_extra *measurements = (rxm_raw_msg_extra *)&msg_parse_buf[UBLOX_HEADER_SIZE + sizeof(rxm_raw_msg)]; |
||||||
|
capnp::MallocMessageBuilder msg_builder; |
||||||
|
cereal::Event::Builder event = msg_builder.initRoot<cereal::Event>(); |
||||||
|
event.setLogMonoTime(nanos_since_boot()); |
||||||
|
auto gnss = event.initUbloxGnss(); |
||||||
|
auto mr = gnss.initMeasurementReport(); |
||||||
|
mr.setRcvTow(msg->rcvTow); |
||||||
|
mr.setGpsWeek(msg->week); |
||||||
|
mr.setLeapSeconds(msg->leapS); |
||||||
|
mr.setGpsWeek(msg->week); |
||||||
|
auto mb = mr.initMeasurements(msg->numMeas); |
||||||
|
for(int8_t i = 0; i < msg->numMeas; i++) { |
||||||
|
mb[i].setSvId(measurements[i].svId); |
||||||
|
mb[i].setSigId(measurements[i].sigId); |
||||||
|
mb[i].setPseudorange(measurements[i].prMes); |
||||||
|
mb[i].setCarrierCycles(measurements[i].cpMes); |
||||||
|
mb[i].setDoppler(measurements[i].doMes); |
||||||
|
mb[i].setGnssId(measurements[i].gnssId); |
||||||
|
mb[i].setGlonassFrequencyIndex(measurements[i].freqId); |
||||||
|
mb[i].setLocktime(measurements[i].locktime); |
||||||
|
mb[i].setCno(measurements[i].cno); |
||||||
|
mb[i].setPseudorangeStdev(0.01*(pow(2, (measurements[i].prStdev & 15)))); // weird scaling, might be wrong
|
||||||
|
mb[i].setCarrierPhaseStdev(0.004*(measurements[i].cpStdev & 15)); |
||||||
|
mb[i].setDopplerStdev(0.002*(pow(2, (measurements[i].doStdev & 15)))); // weird scaling, might be wrong
|
||||||
|
auto ts = mb[i].initTrackingStatus(); |
||||||
|
ts.setPseudorangeValid(bit_to_bool(measurements[i].trkStat, 0)); |
||||||
|
ts.setCarrierPhaseValid(bit_to_bool(measurements[i].trkStat, 1)); |
||||||
|
ts.setHalfCycleValid(bit_to_bool(measurements[i].trkStat, 2)); |
||||||
|
ts.setHalfCycleSubtracted(bit_to_bool(measurements[i].trkStat, 3)); |
||||||
|
} |
||||||
|
|
||||||
|
mr.setNumMeas(msg->numMeas); |
||||||
|
auto rs = mr.initReceiverStatus(); |
||||||
|
rs.setLeapSecValid(bit_to_bool(msg->recStat, 0)); |
||||||
|
rs.setClkReset(bit_to_bool(msg->recStat, 2)); |
||||||
|
return capnp::messageToFlatArray(msg_builder); |
||||||
|
} |
||||||
|
|
||||||
|
kj::Array<capnp::word> UbloxMsgParser::gen_nav_data() { |
||||||
|
rxm_sfrbx_msg *msg = (rxm_sfrbx_msg *)&msg_parse_buf[UBLOX_HEADER_SIZE]; |
||||||
|
if(bytes_in_parse_buf != ( |
||||||
|
UBLOX_HEADER_SIZE + sizeof(rxm_sfrbx_msg) + msg->numWords * sizeof(rxm_sfrbx_msg_extra) + UBLOX_CHECKSUM_SIZE |
||||||
|
)) { |
||||||
|
LOGD("Invalid sfrbx words size %u, %u, %u, %u", msg->numWords, bytes_in_parse_buf, sizeof(rxm_raw_msg_extra), sizeof(rxm_raw_msg)); |
||||||
|
return kj::Array<capnp::word>(); |
||||||
|
} |
||||||
|
rxm_sfrbx_msg_extra *measurements = (rxm_sfrbx_msg_extra *)&msg_parse_buf[UBLOX_HEADER_SIZE + sizeof(rxm_sfrbx_msg)]; |
||||||
|
if(msg->gnssId == 0) { |
||||||
|
uint8_t subframeId = GET_FIELD_U(measurements[1].dwrd, 3, 8); |
||||||
|
std::vector<uint32_t> words; |
||||||
|
for(int i = 0; i < msg->numWords;i++) |
||||||
|
words.push_back(measurements[i].dwrd); |
||||||
|
|
||||||
|
if(subframeId == 1) { |
||||||
|
nav_frame_buffer[msg->gnssId][msg->svid] = subframes_map(); |
||||||
|
nav_frame_buffer[msg->gnssId][msg->svid][subframeId] = words; |
||||||
|
} else if(nav_frame_buffer[msg->gnssId][msg->svid].find(subframeId-1) != nav_frame_buffer[msg->gnssId][msg->svid].end()) |
||||||
|
nav_frame_buffer[msg->gnssId][msg->svid][subframeId] = words; |
||||||
|
if(nav_frame_buffer[msg->gnssId][msg->svid].size() == 5) { |
||||||
|
EphemerisData ephem_data(msg->svid, nav_frame_buffer[msg->gnssId][msg->svid]); |
||||||
|
capnp::MallocMessageBuilder msg_builder; |
||||||
|
cereal::Event::Builder event = msg_builder.initRoot<cereal::Event>(); |
||||||
|
event.setLogMonoTime(nanos_since_boot()); |
||||||
|
auto gnss = event.initUbloxGnss(); |
||||||
|
auto eph = gnss.initEphemeris(); |
||||||
|
eph.setSvId(ephem_data.svId); |
||||||
|
eph.setToc(ephem_data.toc); |
||||||
|
eph.setGpsWeek(ephem_data.gpsWeek); |
||||||
|
eph.setAf0(ephem_data.af0); |
||||||
|
eph.setAf1(ephem_data.af1); |
||||||
|
eph.setAf2(ephem_data.af2); |
||||||
|
eph.setIode(ephem_data.iode); |
||||||
|
eph.setCrs(ephem_data.crs); |
||||||
|
eph.setDeltaN(ephem_data.deltaN); |
||||||
|
eph.setM0(ephem_data.M0); |
||||||
|
eph.setCuc(ephem_data.cuc); |
||||||
|
eph.setEcc(ephem_data.ecc); |
||||||
|
eph.setCus(ephem_data.cus); |
||||||
|
eph.setA(ephem_data.A); |
||||||
|
eph.setToe(ephem_data.toe); |
||||||
|
eph.setCic(ephem_data.cic); |
||||||
|
eph.setOmega0(ephem_data.omega0); |
||||||
|
eph.setCis(ephem_data.cis); |
||||||
|
eph.setI0(ephem_data.i0); |
||||||
|
eph.setCrc(ephem_data.crc); |
||||||
|
eph.setOmega(ephem_data.omega); |
||||||
|
eph.setOmegaDot(ephem_data.omega_dot); |
||||||
|
eph.setIDot(ephem_data.idot); |
||||||
|
eph.setTgd(ephem_data.Tgd); |
||||||
|
eph.setIonoCoeffsValid(ephem_data.ionoCoeffsValid); |
||||||
|
if(ephem_data.ionoCoeffsValid) { |
||||||
|
kj::ArrayPtr<const double> apa(&ephem_data.ionoAlpha[0], sizeof(ephem_data.ionoAlpha) / sizeof(ephem_data.ionoAlpha[0])); |
||||||
|
eph.setIonoAlpha(apa); |
||||||
|
kj::ArrayPtr<const double> apb(&ephem_data.ionoBeta[0], sizeof(ephem_data.ionoBeta) / sizeof(ephem_data.ionoBeta[0])); |
||||||
|
eph.setIonoBeta(apb); |
||||||
|
} else { |
||||||
|
eph.setIonoAlpha(kj::ArrayPtr<const double>()); |
||||||
|
eph.setIonoBeta(kj::ArrayPtr<const double>()); |
||||||
|
} |
||||||
|
return capnp::messageToFlatArray(msg_builder); |
||||||
|
} |
||||||
|
} |
||||||
|
return kj::Array<capnp::word>(); |
||||||
|
} |
||||||
|
|
||||||
|
bool UbloxMsgParser::add_data(const uint8_t *incoming_data, uint32_t incoming_data_len, size_t &bytes_consumed) { |
||||||
|
int needed = needed_bytes(); |
||||||
|
if(needed > 0) { |
||||||
|
bytes_consumed = min((size_t)needed, incoming_data_len ); |
||||||
|
// Add data to buffer
|
||||||
|
memcpy(msg_parse_buf + bytes_in_parse_buf, incoming_data, bytes_consumed); |
||||||
|
bytes_in_parse_buf += bytes_consumed; |
||||||
|
} else { |
||||||
|
bytes_consumed = incoming_data_len; |
||||||
|
} |
||||||
|
// Validate msg format, detect invalid header and invalid checksum.
|
||||||
|
while(!valid_so_far() && bytes_in_parse_buf != 0) { |
||||||
|
//LOGD("Drop corrupt data, remained in buf: %u", bytes_in_parse_buf);
|
||||||
|
// Corrupted msg, drop a byte.
|
||||||
|
bytes_in_parse_buf -= 1; |
||||||
|
if(bytes_in_parse_buf > 0) |
||||||
|
memmove(&msg_parse_buf[0], &msg_parse_buf[1], bytes_in_parse_buf); |
||||||
|
} |
||||||
|
// There is redundant data at the end of buffer, reset the buffer.
|
||||||
|
if(needed_bytes() == -1) |
||||||
|
bytes_in_parse_buf = 0; |
||||||
|
return valid(); |
||||||
|
} |
||||||
|
|
||||||
|
} |
@ -0,0 +1,149 @@ |
|||||||
|
#pragma once |
||||||
|
|
||||||
|
#include <stdint.h> |
||||||
|
|
||||||
|
#define min(x, y) ((x) <= (y) ? (x) : (y)) |
||||||
|
|
||||||
|
// NAV_PVT
|
||||||
|
typedef struct __attribute__((packed)) { |
||||||
|
uint32_t iTOW; |
||||||
|
uint16_t year; |
||||||
|
int8_t month; |
||||||
|
int8_t day; |
||||||
|
int8_t hour; |
||||||
|
int8_t min; |
||||||
|
int8_t sec; |
||||||
|
int8_t valid; |
||||||
|
uint32_t tAcc; |
||||||
|
int32_t nano; |
||||||
|
int8_t fixType; |
||||||
|
int8_t flags; |
||||||
|
int8_t flags2; |
||||||
|
int8_t numSV; |
||||||
|
int32_t lon; |
||||||
|
int32_t lat; |
||||||
|
int32_t height; |
||||||
|
int32_t hMSL; |
||||||
|
uint32_t hAcc; |
||||||
|
uint32_t vAcc; |
||||||
|
int32_t velN; |
||||||
|
int32_t velE; |
||||||
|
int32_t velD; |
||||||
|
int32_t gSpeed; |
||||||
|
int32_t headMot; |
||||||
|
uint32_t sAcc; |
||||||
|
uint32_t headAcc; |
||||||
|
uint16_t pDOP; |
||||||
|
int8_t reserverd1[6]; |
||||||
|
int32_t headVeh; |
||||||
|
int16_t magDec; |
||||||
|
uint16_t magAcc; |
||||||
|
} nav_pvt_msg; |
||||||
|
|
||||||
|
// RXM_RAW
|
||||||
|
typedef struct __attribute__((packed)) { |
||||||
|
double rcvTow; |
||||||
|
uint16_t week; |
||||||
|
int8_t leapS; |
||||||
|
int8_t numMeas; |
||||||
|
int8_t recStat; |
||||||
|
int8_t reserved1[3]; |
||||||
|
} rxm_raw_msg; |
||||||
|
|
||||||
|
// Extra data count is in numMeas
|
||||||
|
typedef struct __attribute__((packed)) { |
||||||
|
double prMes; |
||||||
|
double cpMes; |
||||||
|
float doMes; |
||||||
|
int8_t gnssId; |
||||||
|
int8_t svId; |
||||||
|
int8_t sigId; |
||||||
|
int8_t freqId; |
||||||
|
uint16_t locktime; |
||||||
|
int8_t cno; |
||||||
|
int8_t prStdev; |
||||||
|
int8_t cpStdev; |
||||||
|
int8_t doStdev; |
||||||
|
int8_t trkStat; |
||||||
|
int8_t reserved3; |
||||||
|
} rxm_raw_msg_extra; |
||||||
|
// RXM_SFRBX
|
||||||
|
typedef struct __attribute__((packed)) { |
||||||
|
int8_t gnssId; |
||||||
|
int8_t svid; |
||||||
|
int8_t reserved1; |
||||||
|
int8_t freqId; |
||||||
|
int8_t numWords; |
||||||
|
int8_t reserved2; |
||||||
|
int8_t version; |
||||||
|
int8_t reserved3; |
||||||
|
} rxm_sfrbx_msg; |
||||||
|
|
||||||
|
// Extra data count is in numWords
|
||||||
|
typedef struct __attribute__((packed)) { |
||||||
|
uint32_t dwrd; |
||||||
|
} rxm_sfrbx_msg_extra; |
||||||
|
|
||||||
|
namespace ublox { |
||||||
|
// protocol constants
|
||||||
|
const uint8_t PREAMBLE1 = 0xb5; |
||||||
|
const uint8_t PREAMBLE2 = 0x62; |
||||||
|
|
||||||
|
// message classes
|
||||||
|
const uint8_t CLASS_NAV = 0x01; |
||||||
|
const uint8_t CLASS_RXM = 0x02; |
||||||
|
|
||||||
|
// NAV messages
|
||||||
|
const uint8_t MSG_NAV_PVT = 0x7; |
||||||
|
|
||||||
|
// RXM messages
|
||||||
|
const uint8_t MSG_RXM_RAW = 0x15; |
||||||
|
const uint8_t MSG_RXM_SFRBX = 0x13; |
||||||
|
|
||||||
|
const int UBLOX_HEADER_SIZE = 6; |
||||||
|
const int UBLOX_CHECKSUM_SIZE = 2; |
||||||
|
const int UBLOX_MAX_MSG_SIZE = 65536; |
||||||
|
|
||||||
|
typedef std::map<uint8_t, std::vector<uint32_t>> subframes_map; |
||||||
|
|
||||||
|
class UbloxMsgParser { |
||||||
|
public: |
||||||
|
|
||||||
|
UbloxMsgParser(); |
||||||
|
kj::Array<capnp::word> gen_solution(); |
||||||
|
kj::Array<capnp::word> gen_raw(); |
||||||
|
|
||||||
|
kj::Array<capnp::word> gen_nav_data(); |
||||||
|
bool add_data(const uint8_t *incoming_data, uint32_t incoming_data_len, size_t &bytes_consumed); |
||||||
|
inline void reset() {bytes_in_parse_buf = 0;} |
||||||
|
inline uint8_t msg_class() { |
||||||
|
return msg_parse_buf[2]; |
||||||
|
} |
||||||
|
|
||||||
|
inline uint8_t msg_id() { |
||||||
|
return msg_parse_buf[3]; |
||||||
|
} |
||||||
|
inline int needed_bytes(); |
||||||
|
|
||||||
|
void hexdump(uint8_t *d, int l) { |
||||||
|
for (int i = 0; i < l; i++) { |
||||||
|
if (i%0x10 == 0 && i != 0) printf("\n"); |
||||||
|
printf("%02X ", d[i]); |
||||||
|
} |
||||||
|
printf("\n"); |
||||||
|
} |
||||||
|
private: |
||||||
|
inline bool valid_cheksum(); |
||||||
|
inline bool valid(); |
||||||
|
inline bool valid_so_far(); |
||||||
|
|
||||||
|
uint8_t msg_parse_buf[UBLOX_HEADER_SIZE + UBLOX_MAX_MSG_SIZE]; |
||||||
|
int bytes_in_parse_buf; |
||||||
|
std::map<uint8_t, std::map<uint8_t, subframes_map>> nav_frame_buffer; |
||||||
|
}; |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
typedef int (*poll_ubloxraw_msg_func)(void *gpsLocationExternal, void *ubloxGnss, void *subscriber, zmq_msg_t *msg); |
||||||
|
typedef int (*send_gps_event_func)(uint8_t msg_cls, uint8_t msg_id, void *s, const void *buf, size_t len, int flags); |
||||||
|
int ubloxd_main(poll_ubloxraw_msg_func poll_func, send_gps_event_func send_func); |
@ -0,0 +1,45 @@ |
|||||||
|
#include <stdio.h> |
||||||
|
#include <stdlib.h> |
||||||
|
#include <string.h> |
||||||
|
#include <signal.h> |
||||||
|
#include <unistd.h> |
||||||
|
#include <sched.h> |
||||||
|
#include <sys/time.h> |
||||||
|
#include <sys/cdefs.h> |
||||||
|
#include <sys/types.h> |
||||||
|
#include <sys/time.h> |
||||||
|
#include <assert.h> |
||||||
|
#include <math.h> |
||||||
|
#include <ctime> |
||||||
|
#include <chrono> |
||||||
|
#include <map> |
||||||
|
#include <vector> |
||||||
|
|
||||||
|
#include <zmq.h> |
||||||
|
#include <capnp/serialize.h> |
||||||
|
#include "cereal/gen/cpp/log.capnp.h" |
||||||
|
|
||||||
|
#include "common/params.h" |
||||||
|
#include "common/swaglog.h" |
||||||
|
#include "common/timing.h" |
||||||
|
|
||||||
|
#include "ublox_msg.h" |
||||||
|
|
||||||
|
const long ZMQ_POLL_TIMEOUT = 1000; // In miliseconds
|
||||||
|
|
||||||
|
int poll_ubloxraw_msg(void *gpsLocationExternal, void *ubloxGnss, void *subscriber, zmq_msg_t *msg) { |
||||||
|
int err; |
||||||
|
zmq_pollitem_t item = {.socket = subscriber, .events = ZMQ_POLLIN}; |
||||||
|
err = zmq_poll (&item, 1, ZMQ_POLL_TIMEOUT); |
||||||
|
if(err <= 0) |
||||||
|
return err; |
||||||
|
return zmq_msg_recv(msg, subscriber, 0); |
||||||
|
} |
||||||
|
|
||||||
|
int send_gps_event(uint8_t msg_cls, uint8_t msg_id, void *s, const void *buf, size_t len, int flags) { |
||||||
|
return zmq_send(s, buf, len, flags); |
||||||
|
} |
||||||
|
|
||||||
|
int main() { |
||||||
|
return ubloxd_main(poll_ubloxraw_msg, send_gps_event); |
||||||
|
} |
@ -0,0 +1,113 @@ |
|||||||
|
#include <stdio.h> |
||||||
|
#include <stdlib.h> |
||||||
|
#include <string.h> |
||||||
|
#include <signal.h> |
||||||
|
#include <unistd.h> |
||||||
|
#include <sched.h> |
||||||
|
#include <sys/time.h> |
||||||
|
#include <sys/cdefs.h> |
||||||
|
#include <sys/types.h> |
||||||
|
#include <sys/time.h> |
||||||
|
#include <assert.h> |
||||||
|
#include <math.h> |
||||||
|
#include <ctime> |
||||||
|
#include <chrono> |
||||||
|
#include <map> |
||||||
|
#include <vector> |
||||||
|
|
||||||
|
#include <zmq.h> |
||||||
|
#include <capnp/serialize.h> |
||||||
|
#include "cereal/gen/cpp/log.capnp.h" |
||||||
|
|
||||||
|
#include "common/params.h" |
||||||
|
#include "common/swaglog.h" |
||||||
|
#include "common/timing.h" |
||||||
|
|
||||||
|
#include "ublox_msg.h" |
||||||
|
|
||||||
|
volatile int do_exit = 0; // Flag for process exit on signal
|
||||||
|
|
||||||
|
void set_do_exit(int sig) { |
||||||
|
do_exit = 1; |
||||||
|
} |
||||||
|
|
||||||
|
using namespace ublox; |
||||||
|
|
||||||
|
int ubloxd_main(poll_ubloxraw_msg_func poll_func, send_gps_event_func send_func) { |
||||||
|
LOGW("starting ubloxd"); |
||||||
|
signal(SIGINT, (sighandler_t) set_do_exit); |
||||||
|
signal(SIGTERM, (sighandler_t) set_do_exit); |
||||||
|
|
||||||
|
UbloxMsgParser parser; |
||||||
|
void *context = zmq_ctx_new(); |
||||||
|
void *gpsLocationExternal = zmq_socket(context, ZMQ_PUB); |
||||||
|
zmq_bind(gpsLocationExternal, "tcp://*:8032"); |
||||||
|
void *ubloxGnss = zmq_socket(context, ZMQ_PUB); |
||||||
|
zmq_bind(ubloxGnss, "tcp://*:8033"); |
||||||
|
// ubloxRaw = 8042
|
||||||
|
void *subscriber = zmq_socket(context, ZMQ_SUB); |
||||||
|
zmq_setsockopt(subscriber, ZMQ_SUBSCRIBE, "", 0); |
||||||
|
zmq_connect(subscriber, "tcp://127.0.0.1:8042"); |
||||||
|
while (!do_exit) { |
||||||
|
zmq_msg_t msg; |
||||||
|
zmq_msg_init(&msg); |
||||||
|
int err = poll_func(gpsLocationExternal, ubloxGnss, subscriber, &msg); |
||||||
|
if(err < 0) { |
||||||
|
LOGE_100("zmq_poll error %s in %s", strerror(errno ), __FUNCTION__); |
||||||
|
break; |
||||||
|
} else if(err == 0) { |
||||||
|
continue; |
||||||
|
} |
||||||
|
// format for board, make copy due to alignment issues, will be freed on out of scope
|
||||||
|
auto amsg = kj::heapArray<capnp::word>((zmq_msg_size(&msg) / sizeof(capnp::word)) + 1); |
||||||
|
memcpy(amsg.begin(), zmq_msg_data(&msg), zmq_msg_size(&msg)); |
||||||
|
capnp::FlatArrayMessageReader cmsg(amsg); |
||||||
|
cereal::Event::Reader event = cmsg.getRoot<cereal::Event>(); |
||||||
|
const uint8_t *data = event.getUbloxRaw().begin(); |
||||||
|
size_t len = event.getUbloxRaw().size(); |
||||||
|
size_t bytes_consumed = 0; |
||||||
|
while(bytes_consumed < len && !do_exit) { |
||||||
|
size_t bytes_consumed_this_time = 0U; |
||||||
|
if(parser.add_data(data + bytes_consumed, (uint32_t)(len - bytes_consumed), bytes_consumed_this_time)) { |
||||||
|
// New message available
|
||||||
|
if(parser.msg_class() == CLASS_NAV) { |
||||||
|
if(parser.msg_id() == MSG_NAV_PVT) { |
||||||
|
LOGD("MSG_NAV_PVT"); |
||||||
|
auto words = parser.gen_solution(); |
||||||
|
if(words.size() > 0) { |
||||||
|
auto bytes = words.asBytes(); |
||||||
|
send_func(parser.msg_class(), parser.msg_id(), gpsLocationExternal, bytes.begin(), bytes.size(), 0); |
||||||
|
} |
||||||
|
} else |
||||||
|
LOGW("Unknown nav msg id: 0x%02X", parser.msg_id()); |
||||||
|
} else if(parser.msg_class() == CLASS_RXM) { |
||||||
|
if(parser.msg_id() == MSG_RXM_RAW) { |
||||||
|
LOGD("MSG_RXM_RAW"); |
||||||
|
auto words = parser.gen_raw(); |
||||||
|
if(words.size() > 0) { |
||||||
|
auto bytes = words.asBytes(); |
||||||
|
send_func(parser.msg_class(), parser.msg_id(), ubloxGnss, bytes.begin(), bytes.size(), 0); |
||||||
|
} |
||||||
|
} else if(parser.msg_id() == MSG_RXM_SFRBX) { |
||||||
|
LOGD("MSG_RXM_SFRBX"); |
||||||
|
auto words = parser.gen_nav_data(); |
||||||
|
if(words.size() > 0) { |
||||||
|
auto bytes = words.asBytes(); |
||||||
|
send_func(parser.msg_class(), parser.msg_id(), ubloxGnss, bytes.begin(), bytes.size(), 0); |
||||||
|
} |
||||||
|
} else |
||||||
|
LOGW("Unknown rxm msg id: 0x%02X", parser.msg_id()); |
||||||
|
} else |
||||||
|
LOGW("Unknown msg class: 0x%02X", parser.msg_class()); |
||||||
|
parser.reset(); |
||||||
|
} |
||||||
|
bytes_consumed += bytes_consumed_this_time; |
||||||
|
} |
||||||
|
zmq_msg_close(&msg); |
||||||
|
} |
||||||
|
zmq_close(subscriber); |
||||||
|
zmq_close(gpsLocationExternal); |
||||||
|
zmq_close(ubloxGnss); |
||||||
|
zmq_ctx_destroy(context); |
||||||
|
return 0; |
||||||
|
} |
@ -0,0 +1,103 @@ |
|||||||
|
#include <stdio.h> |
||||||
|
#include <stdlib.h> |
||||||
|
#include <string.h> |
||||||
|
#include <signal.h> |
||||||
|
#include <unistd.h> |
||||||
|
#include <sched.h> |
||||||
|
#include <sys/time.h> |
||||||
|
#include <sys/cdefs.h> |
||||||
|
#include <sys/types.h> |
||||||
|
#include <sys/time.h> |
||||||
|
#include <assert.h> |
||||||
|
#include <math.h> |
||||||
|
#include <ctime> |
||||||
|
#include <chrono> |
||||||
|
#include <map> |
||||||
|
#include <vector> |
||||||
|
#include <iostream> |
||||||
|
|
||||||
|
#include <zmq.h> |
||||||
|
#include <capnp/serialize.h> |
||||||
|
#include "cereal/gen/cpp/log.capnp.h" |
||||||
|
|
||||||
|
#include "common/params.h" |
||||||
|
#include "common/swaglog.h" |
||||||
|
#include "common/timing.h" |
||||||
|
#include "common/util.h" |
||||||
|
#include "ublox_msg.h" |
||||||
|
|
||||||
|
using namespace ublox; |
||||||
|
|
||||||
|
void write_file(std::string fpath, uint8_t *data, int len) { |
||||||
|
FILE* f = fopen(fpath.c_str(), "wb"); |
||||||
|
if (!f) { |
||||||
|
std::cout << "Open " << fpath << " failed" << std::endl; |
||||||
|
return; |
||||||
|
} |
||||||
|
fwrite(data, len, 1, f); |
||||||
|
fclose(f); |
||||||
|
} |
||||||
|
|
||||||
|
static size_t len = 0U; |
||||||
|
static size_t consumed = 0U; |
||||||
|
static uint8_t *data = NULL; |
||||||
|
static int save_idx = 0; |
||||||
|
static std::string prefix; |
||||||
|
static void *gps_sock, *ublox_gnss_sock; |
||||||
|
|
||||||
|
int poll_ubloxraw_msg(void *gpsLocationExternal, void *ubloxGnss, void *subscriber, zmq_msg_t *msg) { |
||||||
|
gps_sock = gpsLocationExternal; |
||||||
|
ublox_gnss_sock = ubloxGnss; |
||||||
|
size_t consuming = min(len - consumed, 128); |
||||||
|
if(consumed < len) { |
||||||
|
// create message
|
||||||
|
capnp::MallocMessageBuilder msg_builder; |
||||||
|
cereal::Event::Builder event = msg_builder.initRoot<cereal::Event>(); |
||||||
|
event.setLogMonoTime(nanos_since_boot()); |
||||||
|
auto ublox_raw = event.initUbloxRaw(consuming); |
||||||
|
memcpy(ublox_raw.begin(), (void *)(data + consumed), consuming); |
||||||
|
auto words = capnp::messageToFlatArray(msg_builder); |
||||||
|
auto bytes = words.asBytes(); |
||||||
|
zmq_msg_init_size (msg, bytes.size()); |
||||||
|
memcpy (zmq_msg_data(msg), (void *)bytes.begin(), bytes.size()); |
||||||
|
consumed += consuming; |
||||||
|
return 1; |
||||||
|
} else |
||||||
|
return -1; |
||||||
|
} |
||||||
|
|
||||||
|
int send_gps_event(uint8_t msg_cls, uint8_t msg_id, void *s, const void *buf, size_t len, int flags) { |
||||||
|
if(msg_cls == CLASS_NAV && msg_id == MSG_NAV_PVT) |
||||||
|
assert(s == gps_sock); |
||||||
|
else if(msg_cls == CLASS_RXM && msg_id == MSG_RXM_RAW) |
||||||
|
assert(s == ublox_gnss_sock); |
||||||
|
else if(msg_cls == CLASS_RXM && msg_id == MSG_RXM_SFRBX) |
||||||
|
assert(s == ublox_gnss_sock); |
||||||
|
else |
||||||
|
assert(0); |
||||||
|
write_file(prefix + "/" + std::to_string(save_idx), (uint8_t *)buf, len); |
||||||
|
save_idx ++; |
||||||
|
return len; |
||||||
|
} |
||||||
|
|
||||||
|
int main(int argc, char** argv) { |
||||||
|
if(argc < 3) { |
||||||
|
printf("Format: ubloxd_test stream_file_path save_prefix\n"); |
||||||
|
return 0; |
||||||
|
} |
||||||
|
// Parse 11360 msgs, generate 9452 events
|
||||||
|
data = (uint8_t *)read_file(argv[1], &len); |
||||||
|
if(data == NULL) { |
||||||
|
LOGE("Read file %s failed\n", argv[1]); |
||||||
|
return -1; |
||||||
|
} |
||||||
|
prefix = argv[2]; |
||||||
|
ubloxd_main(poll_ubloxraw_msg, send_gps_event); |
||||||
|
free(data); |
||||||
|
printf("Generated %d cereal events\n", save_idx); |
||||||
|
if(save_idx != 9452) { |
||||||
|
printf("Event count error: %d\n", save_idx); |
||||||
|
return -1; |
||||||
|
} |
||||||
|
return 0; |
||||||
|
} |
Binary file not shown.
@ -1,8 +0,0 @@ |
|||||||
orbd |
|
||||||
orbd_cpu |
|
||||||
test/turbocv_profile |
|
||||||
test/turbocv_test |
|
||||||
dspout/* |
|
||||||
dumb_test |
|
||||||
bilinear_lut.h |
|
||||||
orb_lut.h |
|
@ -1,105 +0,0 @@ |
|||||||
# CPU
|
|
||||||
|
|
||||||
CC = clang
|
|
||||||
CXX = clang++
|
|
||||||
|
|
||||||
WARN_FLAGS = -Werror=implicit-function-declaration \
|
|
||||||
-Werror=incompatible-pointer-types \
|
|
||||||
-Werror=int-conversion \
|
|
||||||
-Werror=return-type \
|
|
||||||
-Werror=format-extra-args
|
|
||||||
|
|
||||||
JSON_FLAGS = -I$(PHONELIBS)/json/src
|
|
||||||
|
|
||||||
CFLAGS = -std=gnu11 -g -O2 -fPIC $(WARN_FLAGS) -Iinclude $(JSON_FLAGS) -I.
|
|
||||||
CXXFLAGS = -std=c++11 -g -O2 -fPIC $(WARN_FLAGS) -Iinclude $(JSON_FLAGS) -I.
|
|
||||||
LDFLAGS =
|
|
||||||
|
|
||||||
# profile
|
|
||||||
# CXXFLAGS += -DTURBOCV_PROFILE=1
|
|
||||||
|
|
||||||
PHONELIBS = ../../phonelibs
|
|
||||||
BASEDIR = ../..
|
|
||||||
EXTERNAL = ../../external
|
|
||||||
PYTHONLIBS =
|
|
||||||
|
|
||||||
UNAME_M := $(shell uname -m)
|
|
||||||
|
|
||||||
ifeq ($(UNAME_M),x86_64) |
|
||||||
# computer
|
|
||||||
|
|
||||||
ZMQ_FLAGS = -I$(PHONELIBS)/zmq/aarch64/include
|
|
||||||
ZMQ_LIBS = -L$(BASEDIR)/external/zmq/lib/ \
|
|
||||||
-l:libczmq.a -l:libzmq.a -lpthread
|
|
||||||
|
|
||||||
OPENCV_LIBS = -lopencv_core -lopencv_highgui -lopencv_features2d -lopencv_imgproc
|
|
||||||
|
|
||||||
CXXFLAGS += -fopenmp
|
|
||||||
LDFLAGS += -lomp
|
|
||||||
|
|
||||||
else |
|
||||||
# phone
|
|
||||||
ZMQ_FLAGS = -I$(PHONELIBS)/zmq/aarch64/include
|
|
||||||
ZMQ_LIBS = -L$(PHONELIBS)/zmq/aarch64/lib \
|
|
||||||
-l:libczmq.a -l:libzmq.a \
|
|
||||||
-lgnustl_shared
|
|
||||||
|
|
||||||
OPENCV_FLAGS = -I$(PHONELIBS)/opencv/include
|
|
||||||
OPENCV_LIBS = -Wl,--enable-new-dtags -Wl,-rpath,/usr/local/lib/python2.7/site-packages -L/usr/local/lib/python2.7/site-packages -l:cv2.so
|
|
||||||
|
|
||||||
endif |
|
||||||
|
|
||||||
.PHONY: all |
|
||||||
all: orbd |
|
||||||
|
|
||||||
include ../common/cereal.mk |
|
||||||
|
|
||||||
DEP_OBJS = ../common/visionipc.o ../common/ipc.o ../common/swaglog.o $(PHONELIBS)/json/src/json.o
|
|
||||||
|
|
||||||
orbd: orbd_dsp.o $(DEP_OBJS) calculator_stub.o freethedsp.o |
|
||||||
@echo "[ LINK ] $@"
|
|
||||||
$(CXX) -fPIC -o '$@' $^ \
|
|
||||||
$(LDFLAGS) \
|
|
||||||
$(ZMQ_LIBS) \
|
|
||||||
$(CEREAL_LIBS) \
|
|
||||||
-L/usr/lib \
|
|
||||||
-L/system/vendor/lib64 \
|
|
||||||
-ladsprpc \
|
|
||||||
-lm -lz -llog
|
|
||||||
|
|
||||||
%.o: %.c |
|
||||||
@echo "[ CC ] $@"
|
|
||||||
$(CC) $(CFLAGS) \
|
|
||||||
$(ZMQ_FLAGS) \
|
|
||||||
-I../ \
|
|
||||||
-I../../ \
|
|
||||||
-c -o '$@' '$<'
|
|
||||||
|
|
||||||
orbd_dsp.o: orbd.cc |
|
||||||
@echo "[ CXX ] $@"
|
|
||||||
$(CXX) $(CXXFLAGS) \
|
|
||||||
$(CEREAL_CXXFLAGS) \
|
|
||||||
$(ZMQ_FLAGS) \
|
|
||||||
$(OPENCV_FLAGS) \
|
|
||||||
-DDSP \
|
|
||||||
-I../ \
|
|
||||||
-I../../ \
|
|
||||||
-I../../../ \
|
|
||||||
-I./include \
|
|
||||||
-c -o '$@' '$<'
|
|
||||||
|
|
||||||
freethedsp.o: dsp/freethedsp.c |
|
||||||
@echo "[ CC ] $@"
|
|
||||||
$(CC) $(CFLAGS) \
|
|
||||||
-c -o '$@' '$<'
|
|
||||||
|
|
||||||
calculator_stub.o: dsp/gen/calculator_stub.c |
|
||||||
@echo "[ CC ] $@"
|
|
||||||
$(CC) $(CFLAGS) -I./include -c -o '$@' '$<'
|
|
||||||
|
|
||||||
-include internal.mk |
|
||||||
|
|
||||||
.PHONY: clean |
|
||||||
clean: |
|
||||||
rm -f *.o turbocv.so orbd test/turbocv_profile test/turbocv_test test/*.o *_lut.h
|
|
||||||
|
|
@ -1,119 +0,0 @@ |
|||||||
// freethedsp by geohot
|
|
||||||
// (because the DSP should be free)
|
|
||||||
// released under MIT License
|
|
||||||
|
|
||||||
// usage instructions:
|
|
||||||
// 1. Compile an example from the Qualcomm Hexagon SDK
|
|
||||||
// 2. Try to run it on your phone
|
|
||||||
// 3. Be very sad when "adsprpc ... dlopen error: ... signature verify start failed for ..." appears in logcat
|
|
||||||
// ...here is where people would give up before freethedsp
|
|
||||||
// 4. Compile freethedsp with 'clang -shared freethedsp.c -o freethedsp.so' (or statically link it to your program)
|
|
||||||
// 5. Run your program with 'LD_PRELOAD=./freethedsp.so ./<your_prog>'
|
|
||||||
// 6. OMG THE DSP WORKS
|
|
||||||
// 7. Be happy.
|
|
||||||
|
|
||||||
// *** patch may have to change for your phone ***
|
|
||||||
|
|
||||||
// this is patching /dsp/fastrpc_shell_0
|
|
||||||
// correct if sha hash of fastrpc_shell_0 is "fbadc96848aefad99a95aa4edb560929dcdf78f8"
|
|
||||||
// patch to return 0xFFFFFFFF from is_test_enabled instead of 0
|
|
||||||
// your fastrpc_shell_0 may vary
|
|
||||||
#define PATCH_ADDR 0x5200c |
|
||||||
#define PATCH_OLD "\x40\x3f\x20\x50" |
|
||||||
#define PATCH_NEW "\x40\x3f\x00\x5a" |
|
||||||
#define PATCH_LEN (sizeof(PATCH_OLD)-1) |
|
||||||
#define _BITS_IOCTL_H_ |
|
||||||
|
|
||||||
// under 100 lines of code begins now
|
|
||||||
#include <stdio.h> |
|
||||||
#include <dlfcn.h> |
|
||||||
#include <assert.h> |
|
||||||
#include <stdlib.h> |
|
||||||
#include <unistd.h> |
|
||||||
|
|
||||||
// ioctl stuff
|
|
||||||
#define IOC_OUT 0x40000000 /* copy out parameters */ |
|
||||||
#define IOC_IN 0x80000000 /* copy in parameters */ |
|
||||||
#define IOC_INOUT (IOC_IN|IOC_OUT) |
|
||||||
#define IOCPARM_MASK 0x1fff /* parameter length, at most 13 bits */ |
|
||||||
|
|
||||||
#define _IOC(inout,group,num,len) \ |
|
||||||
(inout | ((len & IOCPARM_MASK) << 16) | ((group) << 8) | (num)) |
|
||||||
#define _IOWR(g,n,t) _IOC(IOC_INOUT, (g), (n), sizeof(t)) |
|
||||||
|
|
||||||
// ion ioctls
|
|
||||||
#include <linux/ion.h> |
|
||||||
#define ION_IOC_MSM_MAGIC 'M' |
|
||||||
#define ION_IOC_CLEAN_INV_CACHES _IOWR(ION_IOC_MSM_MAGIC, 2, \ |
|
||||||
struct ion_flush_data) |
|
||||||
|
|
||||||
struct ion_flush_data { |
|
||||||
ion_user_handle_t handle; |
|
||||||
int fd; |
|
||||||
void *vaddr; |
|
||||||
unsigned int offset; |
|
||||||
unsigned int length; |
|
||||||
}; |
|
||||||
|
|
||||||
// fastrpc ioctls
|
|
||||||
#define FASTRPC_IOCTL_INIT _IOWR('R', 6, struct fastrpc_ioctl_init) |
|
||||||
|
|
||||||
struct fastrpc_ioctl_init { |
|
||||||
uint32_t flags; /* one of FASTRPC_INIT_* macros */ |
|
||||||
uintptr_t __user file; /* pointer to elf file */ |
|
||||||
int32_t filelen; /* elf file length */ |
|
||||||
int32_t filefd; /* ION fd for the file */ |
|
||||||
uintptr_t __user mem; /* mem for the PD */ |
|
||||||
int32_t memlen; /* mem length */ |
|
||||||
int32_t memfd; /* ION fd for the mem */ |
|
||||||
}; |
|
||||||
|
|
||||||
int ioctl(int fd, unsigned long request, void *arg) { |
|
||||||
static void *handle = NULL; |
|
||||||
static int (*orig_ioctl)(int, int, void*); |
|
||||||
|
|
||||||
if (handle == NULL) { |
|
||||||
handle = dlopen("/system/lib64/libc.so", RTLD_LAZY); |
|
||||||
assert(handle != NULL); |
|
||||||
orig_ioctl = dlsym(handle, "ioctl"); |
|
||||||
} |
|
||||||
|
|
||||||
int ret = orig_ioctl(fd, request, arg); |
|
||||||
|
|
||||||
// carefully modify this one
|
|
||||||
if (request == FASTRPC_IOCTL_INIT) { |
|
||||||
struct fastrpc_ioctl_init *init = (struct fastrpc_ioctl_init *)arg; |
|
||||||
|
|
||||||
// confirm patch is correct and do the patch
|
|
||||||
assert(memcmp((void*)(init->mem+PATCH_ADDR), PATCH_OLD, PATCH_LEN) == 0); |
|
||||||
memcpy((void*)(init->mem+PATCH_ADDR), PATCH_NEW, PATCH_LEN); |
|
||||||
|
|
||||||
// flush cache
|
|
||||||
int ionfd = open("/dev/ion", O_RDONLY); |
|
||||||
assert(ionfd > 0); |
|
||||||
|
|
||||||
struct ion_fd_data fd_data; |
|
||||||
fd_data.fd = init->memfd; |
|
||||||
int ret = ioctl(ionfd, ION_IOC_IMPORT, &fd_data); |
|
||||||
assert(ret == 0); |
|
||||||
|
|
||||||
struct ion_flush_data flush_data; |
|
||||||
flush_data.handle = fd_data.handle; |
|
||||||
flush_data.vaddr = (void*)init->mem; |
|
||||||
flush_data.offset = 0; |
|
||||||
flush_data.length = init->memlen; |
|
||||||
ret = ioctl(ionfd, ION_IOC_CLEAN_INV_CACHES, &flush_data); |
|
||||||
assert(ret == 0); |
|
||||||
|
|
||||||
struct ion_handle_data handle_data; |
|
||||||
handle_data.handle = fd_data.handle; |
|
||||||
ret = ioctl(ionfd, ION_IOC_FREE, &handle_data); |
|
||||||
assert(ret == 0); |
|
||||||
|
|
||||||
// cleanup
|
|
||||||
close(ionfd); |
|
||||||
} |
|
||||||
|
|
||||||
return ret; |
|
||||||
} |
|
||||||
|
|
@ -1,39 +0,0 @@ |
|||||||
#ifndef _CALCULATOR_H |
|
||||||
#define _CALCULATOR_H |
|
||||||
|
|
||||||
#include <stdint.h> |
|
||||||
typedef uint8_t uint8; |
|
||||||
typedef uint32_t uint32; |
|
||||||
|
|
||||||
#ifndef __QAIC_HEADER |
|
||||||
#define __QAIC_HEADER(ff) ff |
|
||||||
#endif //__QAIC_HEADER
|
|
||||||
|
|
||||||
#ifndef __QAIC_HEADER_EXPORT |
|
||||||
#define __QAIC_HEADER_EXPORT |
|
||||||
#endif // __QAIC_HEADER_EXPORT
|
|
||||||
|
|
||||||
#ifndef __QAIC_HEADER_ATTRIBUTE |
|
||||||
#define __QAIC_HEADER_ATTRIBUTE |
|
||||||
#endif // __QAIC_HEADER_ATTRIBUTE
|
|
||||||
|
|
||||||
#ifndef __QAIC_IMPL |
|
||||||
#define __QAIC_IMPL(ff) ff |
|
||||||
#endif //__QAIC_IMPL
|
|
||||||
|
|
||||||
#ifndef __QAIC_IMPL_EXPORT |
|
||||||
#define __QAIC_IMPL_EXPORT |
|
||||||
#endif // __QAIC_IMPL_EXPORT
|
|
||||||
|
|
||||||
#ifndef __QAIC_IMPL_ATTRIBUTE |
|
||||||
#define __QAIC_IMPL_ATTRIBUTE |
|
||||||
#endif // __QAIC_IMPL_ATTRIBUTE
|
|
||||||
#ifdef __cplusplus |
|
||||||
extern "C" { |
|
||||||
#endif |
|
||||||
__QAIC_HEADER_EXPORT int __QAIC_HEADER(calculator_init)(uint32* leet) __QAIC_HEADER_ATTRIBUTE; |
|
||||||
__QAIC_HEADER_EXPORT int __QAIC_HEADER(calculator_extract_and_match)(const uint8* img, int imgLen, uint8* features, int featuresLen) __QAIC_HEADER_ATTRIBUTE; |
|
||||||
#ifdef __cplusplus |
|
||||||
} |
|
||||||
#endif |
|
||||||
#endif //_CALCULATOR_H
|
|
@ -1,613 +0,0 @@ |
|||||||
#ifndef _CALCULATOR_STUB_H |
|
||||||
#define _CALCULATOR_STUB_H |
|
||||||
#include "calculator.h" |
|
||||||
|
|
||||||
// remote.h
|
|
||||||
#include <stdint.h> |
|
||||||
#include <sys/types.h> |
|
||||||
|
|
||||||
typedef uint32_t remote_handle; |
|
||||||
typedef uint64_t remote_handle64; |
|
||||||
|
|
||||||
typedef struct { |
|
||||||
void *pv; |
|
||||||
size_t nLen; |
|
||||||
} remote_buf; |
|
||||||
|
|
||||||
typedef struct { |
|
||||||
int32_t fd; |
|
||||||
uint32_t offset; |
|
||||||
} remote_dma_handle; |
|
||||||
|
|
||||||
typedef union { |
|
||||||
remote_buf buf; |
|
||||||
remote_handle h; |
|
||||||
remote_handle64 h64; |
|
||||||
remote_dma_handle dma; |
|
||||||
} remote_arg; |
|
||||||
|
|
||||||
int remote_handle_open(const char* name, remote_handle *ph); |
|
||||||
int remote_handle_invoke(remote_handle h, uint32_t dwScalars, remote_arg *pra); |
|
||||||
int remote_handle_close(remote_handle h); |
|
||||||
|
|
||||||
#define REMOTE_SCALARS_MAKEX(nAttr,nMethod,nIn,nOut,noIn,noOut) \ |
|
||||||
((((uint32_t) (nAttr) & 0x7) << 29) | \
|
|
||||||
(((uint32_t) (nMethod) & 0x1f) << 24) | \
|
|
||||||
(((uint32_t) (nIn) & 0xff) << 16) | \
|
|
||||||
(((uint32_t) (nOut) & 0xff) << 8) | \
|
|
||||||
(((uint32_t) (noIn) & 0x0f) << 4) | \
|
|
||||||
((uint32_t) (noOut) & 0x0f)) |
|
||||||
|
|
||||||
#ifndef _QAIC_ENV_H |
|
||||||
#define _QAIC_ENV_H |
|
||||||
|
|
||||||
#ifdef __GNUC__ |
|
||||||
#ifdef __clang__ |
|
||||||
#pragma GCC diagnostic ignored "-Wunknown-pragmas" |
|
||||||
#else |
|
||||||
#pragma GCC diagnostic ignored "-Wpragmas" |
|
||||||
#endif |
|
||||||
#pragma GCC diagnostic ignored "-Wuninitialized" |
|
||||||
#pragma GCC diagnostic ignored "-Wunused-parameter" |
|
||||||
#pragma GCC diagnostic ignored "-Wunused-function" |
|
||||||
#endif |
|
||||||
|
|
||||||
#ifndef _ATTRIBUTE_UNUSED |
|
||||||
|
|
||||||
#ifdef _WIN32 |
|
||||||
#define _ATTRIBUTE_UNUSED |
|
||||||
#else |
|
||||||
#define _ATTRIBUTE_UNUSED __attribute__ ((unused)) |
|
||||||
#endif |
|
||||||
|
|
||||||
#endif // _ATTRIBUTE_UNUSED
|
|
||||||
|
|
||||||
#ifndef __QAIC_REMOTE |
|
||||||
#define __QAIC_REMOTE(ff) ff |
|
||||||
#endif //__QAIC_REMOTE
|
|
||||||
|
|
||||||
#ifndef __QAIC_HEADER |
|
||||||
#define __QAIC_HEADER(ff) ff |
|
||||||
#endif //__QAIC_HEADER
|
|
||||||
|
|
||||||
#ifndef __QAIC_HEADER_EXPORT |
|
||||||
#define __QAIC_HEADER_EXPORT |
|
||||||
#endif // __QAIC_HEADER_EXPORT
|
|
||||||
|
|
||||||
#ifndef __QAIC_HEADER_ATTRIBUTE |
|
||||||
#define __QAIC_HEADER_ATTRIBUTE |
|
||||||
#endif // __QAIC_HEADER_ATTRIBUTE
|
|
||||||
|
|
||||||
#ifndef __QAIC_IMPL |
|
||||||
#define __QAIC_IMPL(ff) ff |
|
||||||
#endif //__QAIC_IMPL
|
|
||||||
|
|
||||||
#ifndef __QAIC_IMPL_EXPORT |
|
||||||
#define __QAIC_IMPL_EXPORT |
|
||||||
#endif // __QAIC_IMPL_EXPORT
|
|
||||||
|
|
||||||
#ifndef __QAIC_IMPL_ATTRIBUTE |
|
||||||
#define __QAIC_IMPL_ATTRIBUTE |
|
||||||
#endif // __QAIC_IMPL_ATTRIBUTE
|
|
||||||
|
|
||||||
#ifndef __QAIC_STUB |
|
||||||
#define __QAIC_STUB(ff) ff |
|
||||||
#endif //__QAIC_STUB
|
|
||||||
|
|
||||||
#ifndef __QAIC_STUB_EXPORT |
|
||||||
#define __QAIC_STUB_EXPORT |
|
||||||
#endif // __QAIC_STUB_EXPORT
|
|
||||||
|
|
||||||
#ifndef __QAIC_STUB_ATTRIBUTE |
|
||||||
#define __QAIC_STUB_ATTRIBUTE |
|
||||||
#endif // __QAIC_STUB_ATTRIBUTE
|
|
||||||
|
|
||||||
#ifndef __QAIC_SKEL |
|
||||||
#define __QAIC_SKEL(ff) ff |
|
||||||
#endif //__QAIC_SKEL__
|
|
||||||
|
|
||||||
#ifndef __QAIC_SKEL_EXPORT |
|
||||||
#define __QAIC_SKEL_EXPORT |
|
||||||
#endif // __QAIC_SKEL_EXPORT
|
|
||||||
|
|
||||||
#ifndef __QAIC_SKEL_ATTRIBUTE |
|
||||||
#define __QAIC_SKEL_ATTRIBUTE |
|
||||||
#endif // __QAIC_SKEL_ATTRIBUTE
|
|
||||||
|
|
||||||
#ifdef __QAIC_DEBUG__ |
|
||||||
#ifndef __QAIC_DBG_PRINTF__ |
|
||||||
#include <stdio.h> |
|
||||||
#define __QAIC_DBG_PRINTF__( ee ) do { printf ee ; } while(0) |
|
||||||
#endif |
|
||||||
#else |
|
||||||
#define __QAIC_DBG_PRINTF__( ee ) (void)0 |
|
||||||
#endif |
|
||||||
|
|
||||||
|
|
||||||
#define _OFFSET(src, sof) ((void*)(((char*)(src)) + (sof))) |
|
||||||
|
|
||||||
#define _COPY(dst, dof, src, sof, sz) \ |
|
||||||
do {\
|
|
||||||
struct __copy { \
|
|
||||||
char ar[sz]; \
|
|
||||||
};\
|
|
||||||
*(struct __copy*)_OFFSET(dst, dof) = *(struct __copy*)_OFFSET(src, sof);\
|
|
||||||
} while (0) |
|
||||||
|
|
||||||
#define _COPYIF(dst, dof, src, sof, sz) \ |
|
||||||
do {\
|
|
||||||
if(_OFFSET(dst, dof) != _OFFSET(src, sof)) {\
|
|
||||||
_COPY(dst, dof, src, sof, sz); \
|
|
||||||
} \
|
|
||||||
} while (0) |
|
||||||
|
|
||||||
_ATTRIBUTE_UNUSED |
|
||||||
static __inline void _qaic_memmove(void* dst, void* src, int size) { |
|
||||||
int i; |
|
||||||
for(i = 0; i < size; ++i) { |
|
||||||
((char*)dst)[i] = ((char*)src)[i]; |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
#define _MEMMOVEIF(dst, src, sz) \ |
|
||||||
do {\
|
|
||||||
if(dst != src) {\
|
|
||||||
_qaic_memmove(dst, src, sz);\
|
|
||||||
} \
|
|
||||||
} while (0) |
|
||||||
|
|
||||||
|
|
||||||
#define _ASSIGN(dst, src, sof) \ |
|
||||||
do {\
|
|
||||||
dst = OFFSET(src, sof); \
|
|
||||||
} while (0) |
|
||||||
|
|
||||||
#define _STD_STRLEN_IF(str) (str == 0 ? 0 : strlen(str)) |
|
||||||
|
|
||||||
#define AEE_SUCCESS 0 |
|
||||||
#define AEE_EOFFSET 0x80000400 |
|
||||||
#define AEE_EBADPARM (AEE_EOFFSET + 0x00E) |
|
||||||
|
|
||||||
#define _TRY(ee, func) \ |
|
||||||
do { \
|
|
||||||
if (AEE_SUCCESS != ((ee) = func)) {\
|
|
||||||
__QAIC_DBG_PRINTF__((__FILE__ ":%d:error:%d:%s\n", __LINE__, (int)(ee),#func));\
|
|
||||||
goto ee##bail;\
|
|
||||||
} \
|
|
||||||
} while (0) |
|
||||||
|
|
||||||
#define _CATCH(exception) exception##bail: if (exception != AEE_SUCCESS) |
|
||||||
|
|
||||||
#define _ASSERT(nErr, ff) _TRY(nErr, 0 == (ff) ? AEE_EBADPARM : AEE_SUCCESS) |
|
||||||
|
|
||||||
#ifdef __QAIC_DEBUG__ |
|
||||||
#define _ALLOCATE(nErr, pal, size, alignment, pv) _TRY(nErr, _allocator_alloc(pal, __FILE_LINE__, size, alignment, (void**)&pv)) |
|
||||||
#else |
|
||||||
#define _ALLOCATE(nErr, pal, size, alignment, pv) _TRY(nErr, _allocator_alloc(pal, 0, size, alignment, (void**)&pv)) |
|
||||||
#endif |
|
||||||
|
|
||||||
|
|
||||||
#endif // _QAIC_ENV_H
|
|
||||||
|
|
||||||
#ifndef _ALLOCATOR_H |
|
||||||
#define _ALLOCATOR_H |
|
||||||
|
|
||||||
#include <stdlib.h> |
|
||||||
#include <stdint.h> |
|
||||||
|
|
||||||
typedef struct _heap _heap; |
|
||||||
struct _heap { |
|
||||||
_heap* pPrev; |
|
||||||
const char* loc; |
|
||||||
uint64_t buf; |
|
||||||
}; |
|
||||||
|
|
||||||
typedef struct _allocator { |
|
||||||
_heap* pheap; |
|
||||||
uint8_t* stack; |
|
||||||
uint8_t* stackEnd; |
|
||||||
int nSize; |
|
||||||
} _allocator; |
|
||||||
|
|
||||||
_ATTRIBUTE_UNUSED |
|
||||||
static __inline int _heap_alloc(_heap** ppa, const char* loc, int size, void** ppbuf) { |
|
||||||
_heap* pn = 0; |
|
||||||
pn = malloc(size + sizeof(_heap) - sizeof(uint64_t)); |
|
||||||
if(pn != 0) { |
|
||||||
pn->pPrev = *ppa; |
|
||||||
pn->loc = loc; |
|
||||||
*ppa = pn; |
|
||||||
*ppbuf = (void*)&(pn->buf); |
|
||||||
return 0; |
|
||||||
} else { |
|
||||||
return -1; |
|
||||||
} |
|
||||||
} |
|
||||||
#define _ALIGN_SIZE(x, y) (((x) + (y-1)) & ~(y-1)) |
|
||||||
|
|
||||||
_ATTRIBUTE_UNUSED |
|
||||||
static __inline int _allocator_alloc(_allocator* me, |
|
||||||
const char* loc, |
|
||||||
int size, |
|
||||||
unsigned int al, |
|
||||||
void** ppbuf) { |
|
||||||
if(size < 0) { |
|
||||||
return -1; |
|
||||||
} else if (size == 0) { |
|
||||||
*ppbuf = 0; |
|
||||||
return 0; |
|
||||||
} |
|
||||||
if((_ALIGN_SIZE((uintptr_t)me->stackEnd, al) + size) < (uintptr_t)me->stack + me->nSize) { |
|
||||||
*ppbuf = (uint8_t*)_ALIGN_SIZE((uintptr_t)me->stackEnd, al); |
|
||||||
me->stackEnd = (uint8_t*)_ALIGN_SIZE((uintptr_t)me->stackEnd, al) + size; |
|
||||||
return 0; |
|
||||||
} else { |
|
||||||
return _heap_alloc(&me->pheap, loc, size, ppbuf); |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
_ATTRIBUTE_UNUSED |
|
||||||
static __inline void _allocator_deinit(_allocator* me) { |
|
||||||
_heap* pa = me->pheap; |
|
||||||
while(pa != 0) { |
|
||||||
_heap* pn = pa; |
|
||||||
const char* loc = pn->loc; |
|
||||||
(void)loc; |
|
||||||
pa = pn->pPrev; |
|
||||||
free(pn); |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
_ATTRIBUTE_UNUSED |
|
||||||
static __inline void _allocator_init(_allocator* me, uint8_t* stack, int stackSize) { |
|
||||||
me->stack = stack; |
|
||||||
me->stackEnd = stack + stackSize; |
|
||||||
me->nSize = stackSize; |
|
||||||
me->pheap = 0; |
|
||||||
} |
|
||||||
|
|
||||||
|
|
||||||
#endif // _ALLOCATOR_H
|
|
||||||
|
|
||||||
#ifndef SLIM_H |
|
||||||
#define SLIM_H |
|
||||||
|
|
||||||
#include <stdint.h> |
|
||||||
|
|
||||||
//a C data structure for the idl types that can be used to implement
|
|
||||||
//static and dynamic language bindings fairly efficiently.
|
|
||||||
//
|
|
||||||
//the goal is to have a minimal ROM and RAM footprint and without
|
|
||||||
//doing too many allocations. A good way to package these things seemed
|
|
||||||
//like the module boundary, so all the idls within one module can share
|
|
||||||
//all the type references.
|
|
||||||
|
|
||||||
|
|
||||||
#define PARAMETER_IN 0x0 |
|
||||||
#define PARAMETER_OUT 0x1 |
|
||||||
#define PARAMETER_INOUT 0x2 |
|
||||||
#define PARAMETER_ROUT 0x3 |
|
||||||
#define PARAMETER_INROUT 0x4 |
|
||||||
|
|
||||||
//the types that we get from idl
|
|
||||||
#define TYPE_OBJECT 0x0 |
|
||||||
#define TYPE_INTERFACE 0x1 |
|
||||||
#define TYPE_PRIMITIVE 0x2 |
|
||||||
#define TYPE_ENUM 0x3 |
|
||||||
#define TYPE_STRING 0x4 |
|
||||||
#define TYPE_WSTRING 0x5 |
|
||||||
#define TYPE_STRUCTURE 0x6 |
|
||||||
#define TYPE_UNION 0x7 |
|
||||||
#define TYPE_ARRAY 0x8 |
|
||||||
#define TYPE_SEQUENCE 0x9 |
|
||||||
|
|
||||||
//these require the pack/unpack to recurse
|
|
||||||
//so it's a hint to those languages that can optimize in cases where
|
|
||||||
//recursion isn't necessary.
|
|
||||||
#define TYPE_COMPLEX_STRUCTURE (0x10 | TYPE_STRUCTURE) |
|
||||||
#define TYPE_COMPLEX_UNION (0x10 | TYPE_UNION) |
|
||||||
#define TYPE_COMPLEX_ARRAY (0x10 | TYPE_ARRAY) |
|
||||||
#define TYPE_COMPLEX_SEQUENCE (0x10 | TYPE_SEQUENCE) |
|
||||||
|
|
||||||
|
|
||||||
typedef struct Type Type; |
|
||||||
|
|
||||||
#define INHERIT_TYPE\ |
|
||||||
int32_t nativeSize; /*in the simple case its the same as wire size and alignment*/\
|
|
||||||
union {\
|
|
||||||
struct {\
|
|
||||||
const uintptr_t p1;\
|
|
||||||
const uintptr_t p2;\
|
|
||||||
} _cast;\
|
|
||||||
struct {\
|
|
||||||
uint32_t iid;\
|
|
||||||
uint32_t bNotNil;\
|
|
||||||
} object;\
|
|
||||||
struct {\
|
|
||||||
const Type *arrayType;\
|
|
||||||
int32_t nItems;\
|
|
||||||
} array;\
|
|
||||||
struct {\
|
|
||||||
const Type *seqType;\
|
|
||||||
int32_t nMaxLen;\
|
|
||||||
} seqSimple; \
|
|
||||||
struct {\
|
|
||||||
uint32_t bFloating;\
|
|
||||||
uint32_t bSigned;\
|
|
||||||
} prim; \
|
|
||||||
const SequenceType* seqComplex;\
|
|
||||||
const UnionType *unionType;\
|
|
||||||
const StructType *structType;\
|
|
||||||
int32_t stringMaxLen;\
|
|
||||||
uint8_t bInterfaceNotNil;\
|
|
||||||
} param;\
|
|
||||||
uint8_t type;\
|
|
||||||
uint8_t nativeAlignment\
|
|
||||||
|
|
||||||
typedef struct UnionType UnionType; |
|
||||||
typedef struct StructType StructType; |
|
||||||
typedef struct SequenceType SequenceType; |
|
||||||
struct Type { |
|
||||||
INHERIT_TYPE; |
|
||||||
}; |
|
||||||
|
|
||||||
struct SequenceType { |
|
||||||
const Type * seqType; |
|
||||||
uint32_t nMaxLen; |
|
||||||
uint32_t inSize; |
|
||||||
uint32_t routSizePrimIn; |
|
||||||
uint32_t routSizePrimROut; |
|
||||||
}; |
|
||||||
|
|
||||||
//byte offset from the start of the case values for
|
|
||||||
//this unions case value array. it MUST be aligned
|
|
||||||
//at the alignment requrements for the descriptor
|
|
||||||
//
|
|
||||||
//if negative it means that the unions cases are
|
|
||||||
//simple enumerators, so the value read from the descriptor
|
|
||||||
//can be used directly to find the correct case
|
|
||||||
typedef union CaseValuePtr CaseValuePtr; |
|
||||||
union CaseValuePtr { |
|
||||||
const uint8_t* value8s; |
|
||||||
const uint16_t* value16s; |
|
||||||
const uint32_t* value32s; |
|
||||||
const uint64_t* value64s; |
|
||||||
}; |
|
||||||
|
|
||||||
//these are only used in complex cases
|
|
||||||
//so I pulled them out of the type definition as references to make
|
|
||||||
//the type smaller
|
|
||||||
struct UnionType { |
|
||||||
const Type *descriptor; |
|
||||||
uint32_t nCases; |
|
||||||
const CaseValuePtr caseValues; |
|
||||||
const Type * const *cases; |
|
||||||
int32_t inSize; |
|
||||||
int32_t routSizePrimIn; |
|
||||||
int32_t routSizePrimROut; |
|
||||||
uint8_t inAlignment; |
|
||||||
uint8_t routAlignmentPrimIn; |
|
||||||
uint8_t routAlignmentPrimROut; |
|
||||||
uint8_t inCaseAlignment; |
|
||||||
uint8_t routCaseAlignmentPrimIn; |
|
||||||
uint8_t routCaseAlignmentPrimROut; |
|
||||||
uint8_t nativeCaseAlignment; |
|
||||||
uint8_t bDefaultCase; |
|
||||||
}; |
|
||||||
|
|
||||||
struct StructType { |
|
||||||
uint32_t nMembers; |
|
||||||
const Type * const *members; |
|
||||||
int32_t inSize; |
|
||||||
int32_t routSizePrimIn; |
|
||||||
int32_t routSizePrimROut; |
|
||||||
uint8_t inAlignment; |
|
||||||
uint8_t routAlignmentPrimIn; |
|
||||||
uint8_t routAlignmentPrimROut; |
|
||||||
}; |
|
||||||
|
|
||||||
typedef struct Parameter Parameter; |
|
||||||
struct Parameter { |
|
||||||
INHERIT_TYPE; |
|
||||||
uint8_t mode; |
|
||||||
uint8_t bNotNil; |
|
||||||
}; |
|
||||||
|
|
||||||
#define SLIM_IFPTR32(is32,is64) (sizeof(uintptr_t) == 4 ? (is32) : (is64)) |
|
||||||
#define SLIM_SCALARS_IS_DYNAMIC(u) (((u) & 0x00ffffff) == 0x00ffffff) |
|
||||||
|
|
||||||
typedef struct Method Method; |
|
||||||
struct Method { |
|
||||||
uint32_t uScalars; //no method index
|
|
||||||
int32_t primInSize; |
|
||||||
int32_t primROutSize; |
|
||||||
int maxArgs; |
|
||||||
int numParams; |
|
||||||
const Parameter * const *params; |
|
||||||
uint8_t primInAlignment; |
|
||||||
uint8_t primROutAlignment; |
|
||||||
}; |
|
||||||
|
|
||||||
typedef struct Interface Interface; |
|
||||||
|
|
||||||
struct Interface { |
|
||||||
int nMethods; |
|
||||||
const Method * const *methodArray; |
|
||||||
int nIIds; |
|
||||||
const uint32_t *iids; |
|
||||||
const uint16_t* methodStringArray; |
|
||||||
const uint16_t* methodStrings; |
|
||||||
const char* strings; |
|
||||||
}; |
|
||||||
|
|
||||||
|
|
||||||
#endif //SLIM_H
|
|
||||||
|
|
||||||
|
|
||||||
#ifndef _CALCULATOR_SLIM_H |
|
||||||
#define _CALCULATOR_SLIM_H |
|
||||||
|
|
||||||
// remote.h
|
|
||||||
|
|
||||||
#include <stdint.h> |
|
||||||
|
|
||||||
#ifndef __QAIC_SLIM |
|
||||||
#define __QAIC_SLIM(ff) ff |
|
||||||
#endif |
|
||||||
#ifndef __QAIC_SLIM_EXPORT |
|
||||||
#define __QAIC_SLIM_EXPORT |
|
||||||
#endif |
|
||||||
|
|
||||||
static const Type types[1]; |
|
||||||
static const Type types[1] = {{0x1,{{(const uintptr_t)0,(const uintptr_t)0}}, 2,0x1}}; |
|
||||||
static const Parameter parameters[3] = {{0x4,{{(const uintptr_t)0,(const uintptr_t)0}}, 2,0x4,3,0},{SLIM_IFPTR32(0x8,0x10),{{(const uintptr_t)&(types[0]),(const uintptr_t)0x0}}, 9,SLIM_IFPTR32(0x4,0x8),0,0},{SLIM_IFPTR32(0x8,0x10),{{(const uintptr_t)&(types[0]),(const uintptr_t)0x0}}, 9,SLIM_IFPTR32(0x4,0x8),3,0}}; |
|
||||||
static const Parameter* const parameterArrays[3] = {(&(parameters[1])),(&(parameters[2])),(&(parameters[0]))}; |
|
||||||
static const Method methods[2] = {{REMOTE_SCALARS_MAKEX(0,0,0x0,0x1,0x0,0x0),0x0,0x4,1,1,(&(parameterArrays[2])),0x1,0x4},{REMOTE_SCALARS_MAKEX(0,0,0x2,0x1,0x0,0x0),0x8,0x0,5,2,(&(parameterArrays[0])),0x4,0x1}}; |
|
||||||
static const Method* const methodArrays[2] = {&(methods[0]),&(methods[1])}; |
|
||||||
static const char strings[41] = "extract_and_match\0features\0leet\0init\0img\0"; |
|
||||||
static const uint16_t methodStrings[5] = {0,37,18,32,27}; |
|
||||||
static const uint16_t methodStringsArrays[2] = {3,0}; |
|
||||||
__QAIC_SLIM_EXPORT const Interface __QAIC_SLIM(calculator_slim) = {2,&(methodArrays[0]),0,0,&(methodStringsArrays [0]),methodStrings,strings}; |
|
||||||
#endif //_CALCULATOR_SLIM_H
|
|
||||||
#ifdef __cplusplus |
|
||||||
extern "C" { |
|
||||||
#endif |
|
||||||
|
|
||||||
#ifndef _const_calculator_handle |
|
||||||
#define _const_calculator_handle ((remote_handle)-1) |
|
||||||
#endif //_const_calculator_handle
|
|
||||||
|
|
||||||
static void _calculator_pls_dtor(void* data) { |
|
||||||
remote_handle* ph = (remote_handle*)data; |
|
||||||
if(_const_calculator_handle != *ph) { |
|
||||||
(void)__QAIC_REMOTE(remote_handle_close)(*ph); |
|
||||||
*ph = _const_calculator_handle; |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
static int _calculator_pls_ctor(void* ctx, void* data) { |
|
||||||
remote_handle* ph = (remote_handle*)data; |
|
||||||
*ph = _const_calculator_handle; |
|
||||||
if(*ph == (remote_handle)-1) { |
|
||||||
return __QAIC_REMOTE(remote_handle_open)((const char*)ctx, ph); |
|
||||||
} |
|
||||||
return 0; |
|
||||||
} |
|
||||||
|
|
||||||
#if (defined __qdsp6__) || (defined __hexagon__) |
|
||||||
#pragma weak adsp_pls_add_lookup |
|
||||||
extern int adsp_pls_add_lookup(uint32_t type, uint32_t key, int size, int (*ctor)(void* ctx, void* data), void* ctx, void (*dtor)(void* ctx), void** ppo); |
|
||||||
#pragma weak HAP_pls_add_lookup |
|
||||||
extern int HAP_pls_add_lookup(uint32_t type, uint32_t key, int size, int (*ctor)(void* ctx, void* data), void* ctx, void (*dtor)(void* ctx), void** ppo); |
|
||||||
|
|
||||||
__QAIC_STUB_EXPORT remote_handle _calculator_handle(void) { |
|
||||||
remote_handle* ph; |
|
||||||
if(adsp_pls_add_lookup) { |
|
||||||
if(0 == adsp_pls_add_lookup((uint32_t)_calculator_handle, 0, sizeof(*ph), _calculator_pls_ctor, "calculator", _calculator_pls_dtor, (void**)&ph)) { |
|
||||||
return *ph; |
|
||||||
} |
|
||||||
return (remote_handle)-1; |
|
||||||
} else if(HAP_pls_add_lookup) { |
|
||||||
if(0 == HAP_pls_add_lookup((uint32_t)_calculator_handle, 0, sizeof(*ph), _calculator_pls_ctor, "calculator", _calculator_pls_dtor, (void**)&ph)) { |
|
||||||
return *ph; |
|
||||||
} |
|
||||||
return (remote_handle)-1; |
|
||||||
} |
|
||||||
return(remote_handle)-1; |
|
||||||
} |
|
||||||
|
|
||||||
#else //__qdsp6__ || __hexagon__
|
|
||||||
|
|
||||||
uint32_t _calculator_atomic_CompareAndExchange(uint32_t * volatile puDest, uint32_t uExchange, uint32_t uCompare); |
|
||||||
|
|
||||||
#ifdef _WIN32 |
|
||||||
#include "Windows.h" |
|
||||||
uint32_t _calculator_atomic_CompareAndExchange(uint32_t * volatile puDest, uint32_t uExchange, uint32_t uCompare) { |
|
||||||
return (uint32_t)InterlockedCompareExchange((volatile LONG*)puDest, (LONG)uExchange, (LONG)uCompare); |
|
||||||
} |
|
||||||
#elif __GNUC__ |
|
||||||
uint32_t _calculator_atomic_CompareAndExchange(uint32_t * volatile puDest, uint32_t uExchange, uint32_t uCompare) { |
|
||||||
return __sync_val_compare_and_swap(puDest, uCompare, uExchange); |
|
||||||
} |
|
||||||
#endif //_WIN32
|
|
||||||
|
|
||||||
|
|
||||||
__QAIC_STUB_EXPORT remote_handle _calculator_handle(void) { |
|
||||||
static remote_handle handle = _const_calculator_handle; |
|
||||||
if((remote_handle)-1 != handle) { |
|
||||||
return handle; |
|
||||||
} else { |
|
||||||
remote_handle tmp; |
|
||||||
int nErr = _calculator_pls_ctor("calculator", (void*)&tmp); |
|
||||||
if(nErr) { |
|
||||||
return (remote_handle)-1; |
|
||||||
} |
|
||||||
if(((remote_handle)-1 != handle) || ((remote_handle)-1 != (remote_handle)_calculator_atomic_CompareAndExchange((uint32_t*)&handle, (uint32_t)tmp, (uint32_t)-1))) { |
|
||||||
_calculator_pls_dtor(&tmp); |
|
||||||
} |
|
||||||
return handle; |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
#endif //__qdsp6__
|
|
||||||
|
|
||||||
__QAIC_STUB_EXPORT int __QAIC_STUB(calculator_skel_invoke)(uint32_t _sc, remote_arg* _pra) __QAIC_STUB_ATTRIBUTE { |
|
||||||
return __QAIC_REMOTE(remote_handle_invoke)(_calculator_handle(), _sc, _pra); |
|
||||||
} |
|
||||||
|
|
||||||
#ifdef __cplusplus |
|
||||||
} |
|
||||||
#endif |
|
||||||
|
|
||||||
|
|
||||||
#ifdef __cplusplus |
|
||||||
extern "C" { |
|
||||||
#endif |
|
||||||
extern int remote_register_dma_handle(int, uint32_t); |
|
||||||
static __inline int _stub_method(remote_handle _handle, uint32_t _mid, uint32_t _rout0[1]) { |
|
||||||
int _numIn[1]; |
|
||||||
remote_arg _pra[1]; |
|
||||||
uint32_t _primROut[1]; |
|
||||||
int _nErr = 0; |
|
||||||
_numIn[0] = 0; |
|
||||||
_pra[(_numIn[0] + 0)].buf.pv = (void*)_primROut; |
|
||||||
_pra[(_numIn[0] + 0)].buf.nLen = sizeof(_primROut); |
|
||||||
_TRY(_nErr, __QAIC_REMOTE(remote_handle_invoke)(_handle, REMOTE_SCALARS_MAKEX(0, _mid, 0, 1, 0, 0), _pra)); |
|
||||||
_COPY(_rout0, 0, _primROut, 0, 4); |
|
||||||
_CATCH(_nErr) {} |
|
||||||
return _nErr; |
|
||||||
} |
|
||||||
__QAIC_STUB_EXPORT int __QAIC_STUB(calculator_init)(uint32* leet) __QAIC_STUB_ATTRIBUTE { |
|
||||||
uint32_t _mid = 0; |
|
||||||
return _stub_method(_calculator_handle(), _mid, (uint32_t*)leet); |
|
||||||
} |
|
||||||
static __inline int _stub_method_1(remote_handle _handle, uint32_t _mid, char* _in0[1], uint32_t _in0Len[1], char* _rout1[1], uint32_t _rout1Len[1]) { |
|
||||||
int _numIn[1]; |
|
||||||
remote_arg _pra[3]; |
|
||||||
uint32_t _primIn[2]; |
|
||||||
remote_arg* _praIn; |
|
||||||
remote_arg* _praROut; |
|
||||||
int _nErr = 0; |
|
||||||
_numIn[0] = 1; |
|
||||||
_pra[0].buf.pv = (void*)_primIn; |
|
||||||
_pra[0].buf.nLen = sizeof(_primIn); |
|
||||||
_COPY(_primIn, 0, _in0Len, 0, 4); |
|
||||||
_praIn = (_pra + 1); |
|
||||||
_praIn[0].buf.pv = _in0[0]; |
|
||||||
_praIn[0].buf.nLen = (1 * _in0Len[0]); |
|
||||||
_COPY(_primIn, 4, _rout1Len, 0, 4); |
|
||||||
_praROut = (_praIn + _numIn[0] + 0); |
|
||||||
_praROut[0].buf.pv = _rout1[0]; |
|
||||||
_praROut[0].buf.nLen = (1 * _rout1Len[0]); |
|
||||||
_TRY(_nErr, __QAIC_REMOTE(remote_handle_invoke)(_handle, REMOTE_SCALARS_MAKEX(0, _mid, 2, 1, 0, 0), _pra)); |
|
||||||
_CATCH(_nErr) {} |
|
||||||
return _nErr; |
|
||||||
} |
|
||||||
__QAIC_STUB_EXPORT int __QAIC_STUB(calculator_extract_and_match)(const uint8* img, int imgLen, uint8* features, int featuresLen) __QAIC_STUB_ATTRIBUTE { |
|
||||||
uint32_t _mid = 1; |
|
||||||
return _stub_method_1(_calculator_handle(), _mid, (char**)&img, (uint32_t*)&imgLen, (char**)&features, (uint32_t*)&featuresLen); |
|
||||||
} |
|
||||||
#ifdef __cplusplus |
|
||||||
} |
|
||||||
#endif |
|
||||||
#endif //_CALCULATOR_STUB_H
|
|
Binary file not shown.
@ -1,38 +0,0 @@ |
|||||||
#ifndef EXTRACTOR_H |
|
||||||
#define EXTRACTOR_H |
|
||||||
|
|
||||||
#ifdef __cplusplus |
|
||||||
extern "C" { |
|
||||||
#endif |
|
||||||
|
|
||||||
#include <stdint.h> |
|
||||||
|
|
||||||
#define ORBD_KEYPOINTS 3000 |
|
||||||
#define ORBD_DESCRIPTOR_LENGTH 32 |
|
||||||
#define ORBD_HEIGHT 874 |
|
||||||
#define ORBD_WIDTH 1164 |
|
||||||
#define ORBD_FOCAL 910 |
|
||||||
|
|
||||||
// matches OrbFeatures from log.capnp
|
|
||||||
struct orb_features { |
|
||||||
// align this
|
|
||||||
uint16_t n_corners; |
|
||||||
uint16_t xy[ORBD_KEYPOINTS][2]; |
|
||||||
uint8_t octave[ORBD_KEYPOINTS]; |
|
||||||
uint8_t des[ORBD_KEYPOINTS][ORBD_DESCRIPTOR_LENGTH]; |
|
||||||
int16_t matches[ORBD_KEYPOINTS]; |
|
||||||
}; |
|
||||||
|
|
||||||
// forward declare this
|
|
||||||
struct pyramid; |
|
||||||
|
|
||||||
// manage the pyramids in extractor.c
|
|
||||||
void init_gpyrs(); |
|
||||||
int extract_and_match_gpyrs(const uint8_t *img, struct orb_features *); |
|
||||||
int extract_and_match(const uint8_t *img, struct pyramid *pyrs, struct pyramid *prev_pyrs, struct orb_features *); |
|
||||||
|
|
||||||
#ifdef __cplusplus |
|
||||||
} |
|
||||||
#endif |
|
||||||
|
|
||||||
#endif // EXTRACTOR_H
|
|
@ -1,191 +0,0 @@ |
|||||||
#include <stdio.h> |
|
||||||
#include <stdlib.h> |
|
||||||
#include <signal.h> |
|
||||||
#include <unistd.h> |
|
||||||
#include <stdint.h> |
|
||||||
#include <assert.h> |
|
||||||
#include <sys/resource.h> |
|
||||||
|
|
||||||
#include "common/visionipc.h" |
|
||||||
#include "common/swaglog.h" |
|
||||||
|
|
||||||
#include "extractor.h" |
|
||||||
|
|
||||||
#ifdef DSP |
|
||||||
#include "dsp/gen/calculator.h" |
|
||||||
#else |
|
||||||
#include "turbocv.h" |
|
||||||
#endif |
|
||||||
|
|
||||||
#include <zmq.h> |
|
||||||
#include <capnp/serialize.h> |
|
||||||
#include "cereal/gen/cpp/log.capnp.h" |
|
||||||
|
|
||||||
#ifndef PATH_MAX |
|
||||||
#include <linux/limits.h> |
|
||||||
#endif |
|
||||||
|
|
||||||
volatile int do_exit = 0; |
|
||||||
|
|
||||||
static void set_do_exit(int sig) { |
|
||||||
do_exit = 1; |
|
||||||
} |
|
||||||
|
|
||||||
int main(int argc, char *argv[]) { |
|
||||||
int err; |
|
||||||
setpriority(PRIO_PROCESS, 0, -13); |
|
||||||
printf("starting orbd\n"); |
|
||||||
|
|
||||||
#ifdef DSP |
|
||||||
uint32_t test_leet = 0; |
|
||||||
char my_path[PATH_MAX+1]; |
|
||||||
memset(my_path, 0, sizeof(my_path)); |
|
||||||
|
|
||||||
ssize_t len = readlink("/proc/self/exe", my_path, sizeof(my_path)); |
|
||||||
assert(len > 5); |
|
||||||
my_path[len-5] = '\0'; |
|
||||||
LOGW("running from %s with PATH_MAX %d", my_path, PATH_MAX); |
|
||||||
|
|
||||||
char adsp_path[PATH_MAX+1]; |
|
||||||
snprintf(adsp_path, PATH_MAX, "ADSP_LIBRARY_PATH=%s/dsp/gen", my_path); |
|
||||||
assert(putenv(adsp_path) == 0); |
|
||||||
|
|
||||||
assert(calculator_init(&test_leet) == 0); |
|
||||||
assert(test_leet == 0x1337); |
|
||||||
LOGW("orbd init complete"); |
|
||||||
#else |
|
||||||
init_gpyrs(); |
|
||||||
#endif |
|
||||||
|
|
||||||
signal(SIGINT, (sighandler_t) set_do_exit); |
|
||||||
signal(SIGTERM, (sighandler_t) set_do_exit); |
|
||||||
|
|
||||||
void *ctx = zmq_ctx_new(); |
|
||||||
|
|
||||||
void *orb_features_sock = zmq_socket(ctx, ZMQ_PUB); |
|
||||||
assert(orb_features_sock); |
|
||||||
zmq_bind(orb_features_sock, "tcp://*:8058"); |
|
||||||
|
|
||||||
void *orb_features_summary_sock = zmq_socket(ctx, ZMQ_PUB); |
|
||||||
assert(orb_features_summary_sock); |
|
||||||
zmq_bind(orb_features_summary_sock, "tcp://*:8062"); |
|
||||||
|
|
||||||
struct orb_features *features = (struct orb_features *)malloc(sizeof(struct orb_features)); |
|
||||||
int last_frame_id = 0; |
|
||||||
uint64_t frame_count = 0; |
|
||||||
|
|
||||||
// every other frame
|
|
||||||
const int RATE = 2; |
|
||||||
|
|
||||||
VisionStream stream; |
|
||||||
while (!do_exit) { |
|
||||||
VisionStreamBufs buf_info; |
|
||||||
err = visionstream_init(&stream, VISION_STREAM_YUV, true, &buf_info); |
|
||||||
if (err) { |
|
||||||
printf("visionstream connect fail\n"); |
|
||||||
usleep(100000); |
|
||||||
continue; |
|
||||||
} |
|
||||||
uint64_t timestamp_last_eof = 0; |
|
||||||
while (!do_exit) { |
|
||||||
VIPCBuf *buf; |
|
||||||
VIPCBufExtra extra; |
|
||||||
buf = visionstream_get(&stream, &extra); |
|
||||||
if (buf == NULL) { |
|
||||||
printf("visionstream get failed\n"); |
|
||||||
break; |
|
||||||
} |
|
||||||
|
|
||||||
// every other frame
|
|
||||||
frame_count++; |
|
||||||
if ((frame_count%RATE) != 0) { |
|
||||||
continue; |
|
||||||
} |
|
||||||
|
|
||||||
uint64_t start = nanos_since_boot(); |
|
||||||
#ifdef DSP |
|
||||||
int ret = calculator_extract_and_match((uint8_t *)buf->addr, ORBD_HEIGHT*ORBD_WIDTH, (uint8_t *)features, sizeof(struct orb_features)); |
|
||||||
#else |
|
||||||
int ret = extract_and_match_gpyrs((uint8_t *) buf->addr, features); |
|
||||||
#endif |
|
||||||
uint64_t end = nanos_since_boot(); |
|
||||||
LOGD("total(%d): %6.2f ms to get %4d features on %d", ret, (end-start)/1000000.0, features->n_corners, extra.frame_id); |
|
||||||
assert(ret == 0); |
|
||||||
|
|
||||||
if (last_frame_id+RATE != extra.frame_id) { |
|
||||||
LOGW("dropped frame!"); |
|
||||||
} |
|
||||||
|
|
||||||
last_frame_id = extra.frame_id; |
|
||||||
|
|
||||||
if (timestamp_last_eof == 0) { |
|
||||||
timestamp_last_eof = extra.timestamp_eof; |
|
||||||
continue; |
|
||||||
} |
|
||||||
|
|
||||||
int match_count = 0; |
|
||||||
|
|
||||||
// *** send OrbFeatures ***
|
|
||||||
{ |
|
||||||
// create capnp message
|
|
||||||
capnp::MallocMessageBuilder msg; |
|
||||||
cereal::Event::Builder event = msg.initRoot<cereal::Event>(); |
|
||||||
event.setLogMonoTime(nanos_since_boot()); |
|
||||||
|
|
||||||
auto orb_features = event.initOrbFeatures(); |
|
||||||
|
|
||||||
// set timestamps
|
|
||||||
orb_features.setTimestampEof(extra.timestamp_eof); |
|
||||||
orb_features.setTimestampLastEof(timestamp_last_eof); |
|
||||||
|
|
||||||
// init descriptors for send
|
|
||||||
kj::ArrayPtr<capnp::byte> descriptorsPtr = kj::arrayPtr((uint8_t *)features->des, ORBD_DESCRIPTOR_LENGTH * features->n_corners); |
|
||||||
orb_features.setDescriptors(descriptorsPtr); |
|
||||||
|
|
||||||
auto xs = orb_features.initXs(features->n_corners); |
|
||||||
auto ys = orb_features.initYs(features->n_corners); |
|
||||||
auto octaves = orb_features.initOctaves(features->n_corners); |
|
||||||
auto matches = orb_features.initMatches(features->n_corners); |
|
||||||
|
|
||||||
// copy out normalized keypoints
|
|
||||||
for (int i = 0; i < features->n_corners; i++) { |
|
||||||
xs.set(i, (features->xy[i][0] * 1.0f - ORBD_WIDTH / 2) / ORBD_FOCAL); |
|
||||||
ys.set(i, (features->xy[i][1] * 1.0f - ORBD_HEIGHT / 2) / ORBD_FOCAL); |
|
||||||
octaves.set(i, features->octave[i]); |
|
||||||
matches.set(i, features->matches[i]); |
|
||||||
match_count += features->matches[i] != -1; |
|
||||||
} |
|
||||||
|
|
||||||
auto words = capnp::messageToFlatArray(msg); |
|
||||||
auto bytes = words.asBytes(); |
|
||||||
zmq_send(orb_features_sock, bytes.begin(), bytes.size(), 0); |
|
||||||
} |
|
||||||
|
|
||||||
// *** send OrbFeaturesSummary ***
|
|
||||||
|
|
||||||
{ |
|
||||||
// create capnp message
|
|
||||||
capnp::MallocMessageBuilder msg; |
|
||||||
cereal::Event::Builder event = msg.initRoot<cereal::Event>(); |
|
||||||
event.setLogMonoTime(nanos_since_boot()); |
|
||||||
|
|
||||||
auto orb_features_summary = event.initOrbFeaturesSummary(); |
|
||||||
|
|
||||||
orb_features_summary.setTimestampEof(extra.timestamp_eof); |
|
||||||
orb_features_summary.setTimestampLastEof(timestamp_last_eof); |
|
||||||
orb_features_summary.setFeatureCount(features->n_corners); |
|
||||||
orb_features_summary.setMatchCount(match_count); |
|
||||||
orb_features_summary.setComputeNs(end-start); |
|
||||||
|
|
||||||
auto words = capnp::messageToFlatArray(msg); |
|
||||||
auto bytes = words.asBytes(); |
|
||||||
zmq_send(orb_features_summary_sock, bytes.begin(), bytes.size(), 0); |
|
||||||
} |
|
||||||
|
|
||||||
timestamp_last_eof = extra.timestamp_eof; |
|
||||||
} |
|
||||||
} |
|
||||||
visionstream_destroy(&stream); |
|
||||||
return 0; |
|
||||||
} |
|
||||||
|
|
@ -1,13 +0,0 @@ |
|||||||
#!/bin/sh |
|
||||||
finish() { |
|
||||||
echo "exiting orbd" |
|
||||||
pkill -SIGINT -P $$ |
|
||||||
} |
|
||||||
|
|
||||||
trap finish EXIT |
|
||||||
|
|
||||||
while true; do |
|
||||||
./orbd & |
|
||||||
wait $! |
|
||||||
done |
|
||||||
|
|
Binary file not shown.
Binary file not shown.
@ -0,0 +1,53 @@ |
|||||||
|
#include <string.h> |
||||||
|
#include <assert.h> |
||||||
|
|
||||||
|
#include "clutil.h" |
||||||
|
|
||||||
|
#include "rgb_to_yuv.h" |
||||||
|
|
||||||
|
void rgb_to_yuv_init(RGBToYUVState* s, cl_context ctx, cl_device_id device_id, int width, int height, int rgb_stride) { |
||||||
|
int err = 0; |
||||||
|
memset(s, 0, sizeof(*s)); |
||||||
|
assert(width % 2 == 0); |
||||||
|
assert(height % 2 == 0); |
||||||
|
s->width = width; |
||||||
|
s->height = height; |
||||||
|
char args[1024]; |
||||||
|
snprintf(args, sizeof(args), |
||||||
|
"-cl-fast-relaxed-math -cl-denorms-are-zero " |
||||||
|
#ifdef CL_DEBUG |
||||||
|
"-DCL_DEBUG " |
||||||
|
#endif |
||||||
|
"-DWIDTH=%d -DHEIGHT=%d -DUV_WIDTH=%d -DUV_HEIGHT=%d -DRGB_STRIDE=%d -DRGB_SIZE=%d", |
||||||
|
width, height, width/ 2, height / 2, rgb_stride, width * height); |
||||||
|
cl_program prg = CLU_LOAD_FROM_FILE(ctx, device_id, "rgb_to_yuv.cl", args); |
||||||
|
|
||||||
|
s->rgb_to_yuv_krnl = clCreateKernel(prg, "rgb_to_yuv", &err); |
||||||
|
assert(err == 0); |
||||||
|
// done with this
|
||||||
|
err = clReleaseProgram(prg); |
||||||
|
assert(err == 0); |
||||||
|
} |
||||||
|
|
||||||
|
void rgb_to_yuv_destroy(RGBToYUVState* s) { |
||||||
|
int err = 0; |
||||||
|
err = clReleaseKernel(s->rgb_to_yuv_krnl); |
||||||
|
assert(err == 0); |
||||||
|
} |
||||||
|
|
||||||
|
void rgb_to_yuv_queue(RGBToYUVState* s, cl_command_queue q, cl_mem rgb_cl, cl_mem yuv_cl) { |
||||||
|
int err = 0; |
||||||
|
err = clSetKernelArg(s->rgb_to_yuv_krnl, 0, sizeof(cl_mem), &rgb_cl); |
||||||
|
assert(err == 0); |
||||||
|
err = clSetKernelArg(s->rgb_to_yuv_krnl, 1, sizeof(cl_mem), &yuv_cl); |
||||||
|
assert(err == 0); |
||||||
|
const size_t work_size[2] = { |
||||||
|
(size_t)(s->width + (s->width % 4 == 0 ? 0 : (4 - s->width % 4))) / 4,
|
||||||
|
(size_t)(s->height + (s->height % 4 == 0 ? 0 : (4 - s->height % 4))) / 4 |
||||||
|
}; |
||||||
|
cl_event event; |
||||||
|
err = clEnqueueNDRangeKernel(q, s->rgb_to_yuv_krnl, 2, NULL, &work_size[0], NULL, 0, 0, &event); |
||||||
|
assert(err == 0); |
||||||
|
clWaitForEvents(1, &event); |
||||||
|
clReleaseEvent(event); |
||||||
|
} |
@ -0,0 +1,127 @@ |
|||||||
|
#define RGB_TO_Y(r, g, b) ((((mul24(b, 13) + mul24(g, 65) + mul24(r, 33)) + 64) >> 7) + 16) |
||||||
|
#define RGB_TO_U(r, g, b) ((mul24(b, 56) - mul24(g, 37) - mul24(r, 19) + 0x8080) >> 8) |
||||||
|
#define RGB_TO_V(r, g, b) ((mul24(r, 56) - mul24(g, 47) - mul24(b, 9) + 0x8080) >> 8) |
||||||
|
#define AVERAGE(x, y, z, w) ((convert_ushort(x) + convert_ushort(y) + convert_ushort(z) + convert_ushort(w) + 1) >> 1) |
||||||
|
|
||||||
|
inline void convert_2_ys(__global uchar * out_yuv, int yi, const uchar8 rgbs1) { |
||||||
|
uchar2 yy = (uchar2)( |
||||||
|
RGB_TO_Y(rgbs1.s2, rgbs1.s1, rgbs1.s0), |
||||||
|
RGB_TO_Y(rgbs1.s5, rgbs1.s4, rgbs1.s3) |
||||||
|
); |
||||||
|
#ifdef CL_DEBUG |
||||||
|
if(yi >= RGB_SIZE) |
||||||
|
printf("Y vector2 overflow, %d > %d\n", yi, RGB_SIZE); |
||||||
|
#endif |
||||||
|
vstore2(yy, 0, out_yuv + yi); |
||||||
|
} |
||||||
|
|
||||||
|
inline void convert_4_ys(__global uchar * out_yuv, int yi, const uchar8 rgbs1, const uchar8 rgbs3) { |
||||||
|
const uchar4 yy = (uchar4)( |
||||||
|
RGB_TO_Y(rgbs1.s2, rgbs1.s1, rgbs1.s0), |
||||||
|
RGB_TO_Y(rgbs1.s5, rgbs1.s4, rgbs1.s3), |
||||||
|
RGB_TO_Y(rgbs3.s0, rgbs1.s7, rgbs1.s6), |
||||||
|
RGB_TO_Y(rgbs3.s3, rgbs3.s2, rgbs3.s1) |
||||||
|
); |
||||||
|
#ifdef CL_DEBUG |
||||||
|
if(yi > RGB_SIZE - 4) |
||||||
|
printf("Y vector4 overflow, %d > %d\n", yi, RGB_SIZE - 4); |
||||||
|
#endif |
||||||
|
vstore4(yy, 0, out_yuv + yi); |
||||||
|
} |
||||||
|
|
||||||
|
inline void convert_uv(__global uchar * out_yuv, int ui, int vi, |
||||||
|
const uchar8 rgbs1, const uchar8 rgbs2) { |
||||||
|
// U & V: average of 2x2 pixels square |
||||||
|
const short ab = AVERAGE(rgbs1.s0, rgbs1.s3, rgbs2.s0, rgbs2.s3); |
||||||
|
const short ag = AVERAGE(rgbs1.s1, rgbs1.s4, rgbs2.s1, rgbs2.s4); |
||||||
|
const short ar = AVERAGE(rgbs1.s2, rgbs1.s5, rgbs2.s2, rgbs2.s5); |
||||||
|
#ifdef CL_DEBUG |
||||||
|
if(ui >= RGB_SIZE + RGB_SIZE / 4) |
||||||
|
printf("U overflow, %d >= %d\n", ui, RGB_SIZE + RGB_SIZE / 4); |
||||||
|
if(vi >= RGB_SIZE + RGB_SIZE / 2) |
||||||
|
printf("V overflow, %d >= %d\n", vi, RGB_SIZE + RGB_SIZE / 2); |
||||||
|
#endif |
||||||
|
out_yuv[ui] = RGB_TO_U(ar, ag, ab); |
||||||
|
out_yuv[vi] = RGB_TO_V(ar, ag, ab); |
||||||
|
} |
||||||
|
|
||||||
|
inline void convert_2_uvs(__global uchar * out_yuv, int ui, int vi, |
||||||
|
const uchar8 rgbs1, const uchar8 rgbs2, const uchar8 rgbs3, const uchar8 rgbs4) { |
||||||
|
// U & V: average of 2x2 pixels square |
||||||
|
const short ab1 = AVERAGE(rgbs1.s0, rgbs1.s3, rgbs2.s0, rgbs2.s3); |
||||||
|
const short ag1 = AVERAGE(rgbs1.s1, rgbs1.s4, rgbs2.s1, rgbs2.s4); |
||||||
|
const short ar1 = AVERAGE(rgbs1.s2, rgbs1.s5, rgbs2.s2, rgbs2.s5); |
||||||
|
const short ab2 = AVERAGE(rgbs1.s6, rgbs3.s1, rgbs2.s6, rgbs4.s1); |
||||||
|
const short ag2 = AVERAGE(rgbs1.s7, rgbs3.s2, rgbs2.s7, rgbs4.s2); |
||||||
|
const short ar2 = AVERAGE(rgbs3.s0, rgbs3.s3, rgbs4.s0, rgbs4.s3); |
||||||
|
uchar2 u2 = (uchar2)( |
||||||
|
RGB_TO_U(ar1, ag1, ab1), |
||||||
|
RGB_TO_U(ar2, ag2, ab2) |
||||||
|
); |
||||||
|
uchar2 v2 = (uchar2)( |
||||||
|
RGB_TO_V(ar1, ag1, ab1), |
||||||
|
RGB_TO_V(ar2, ag2, ab2) |
||||||
|
); |
||||||
|
#ifdef CL_DEBUG1 |
||||||
|
if(ui > RGB_SIZE + RGB_SIZE / 4 - 2) |
||||||
|
printf("U 2 overflow, %d >= %d\n", ui, RGB_SIZE + RGB_SIZE / 4 - 2); |
||||||
|
if(vi > RGB_SIZE + RGB_SIZE / 2 - 2) |
||||||
|
printf("V 2 overflow, %d >= %d\n", vi, RGB_SIZE + RGB_SIZE / 2 - 2); |
||||||
|
#endif |
||||||
|
vstore2(u2, 0, out_yuv + ui); |
||||||
|
vstore2(v2, 0, out_yuv + vi); |
||||||
|
} |
||||||
|
|
||||||
|
__kernel void rgb_to_yuv(__global uchar const * const rgb, |
||||||
|
__global uchar * out_yuv) |
||||||
|
{ |
||||||
|
const int dx = get_global_id(0); |
||||||
|
const int dy = get_global_id(1); |
||||||
|
const int col = mul24(dx, 4); // Current column in rgb image |
||||||
|
const int row = mul24(dy, 4); // Current row in rgb image |
||||||
|
const int bgri_start = mad24(row, RGB_STRIDE, mul24(col, 3)); // Start offset of rgb data being converted |
||||||
|
const int yi_start = mad24(row, WIDTH, col); // Start offset in the target yuv buffer |
||||||
|
int ui = mad24(row / 2, UV_WIDTH, RGB_SIZE + col / 2); |
||||||
|
int vi = mad24(row / 2 , UV_WIDTH, RGB_SIZE + UV_WIDTH * UV_HEIGHT + col / 2); |
||||||
|
int num_col = min(WIDTH - col, 4); |
||||||
|
int num_row = min(HEIGHT - row, 4); |
||||||
|
if(num_row == 4) { |
||||||
|
const uchar8 rgbs0_0 = vload8(0, rgb + bgri_start); |
||||||
|
const uchar8 rgbs0_1 = vload8(0, rgb + bgri_start + 8); |
||||||
|
const uchar8 rgbs1_0 = vload8(0, rgb + bgri_start + RGB_STRIDE); |
||||||
|
const uchar8 rgbs1_1 = vload8(0, rgb + bgri_start + RGB_STRIDE + 8); |
||||||
|
const uchar8 rgbs2_0 = vload8(0, rgb + bgri_start + RGB_STRIDE * 2); |
||||||
|
const uchar8 rgbs2_1 = vload8(0, rgb + bgri_start + RGB_STRIDE * 2 + 8); |
||||||
|
const uchar8 rgbs3_0 = vload8(0, rgb + bgri_start + RGB_STRIDE * 3); |
||||||
|
const uchar8 rgbs3_1 = vload8(0, rgb + bgri_start + RGB_STRIDE * 3 + 8); |
||||||
|
if(num_col == 4) { |
||||||
|
convert_4_ys(out_yuv, yi_start, rgbs0_0, rgbs0_1); |
||||||
|
convert_4_ys(out_yuv, yi_start + WIDTH, rgbs1_0, rgbs1_1); |
||||||
|
convert_4_ys(out_yuv, yi_start + WIDTH * 2, rgbs2_0, rgbs2_1); |
||||||
|
convert_4_ys(out_yuv, yi_start + WIDTH * 3, rgbs3_0, rgbs3_1); |
||||||
|
convert_2_uvs(out_yuv, ui, vi, rgbs0_0, rgbs1_0, rgbs0_1, rgbs1_1); |
||||||
|
convert_2_uvs(out_yuv, ui + UV_WIDTH, vi + UV_WIDTH, rgbs2_0, rgbs3_0, rgbs2_1, rgbs3_1); |
||||||
|
} else if(num_col == 2) { |
||||||
|
convert_2_ys(out_yuv, yi_start, rgbs0_0); |
||||||
|
convert_2_ys(out_yuv, yi_start + WIDTH, rgbs1_0); |
||||||
|
convert_2_ys(out_yuv, yi_start + WIDTH * 2, rgbs2_0); |
||||||
|
convert_2_ys(out_yuv, yi_start + WIDTH * 3, rgbs3_0); |
||||||
|
convert_uv(out_yuv, ui, vi, rgbs0_0, rgbs1_0); |
||||||
|
convert_uv(out_yuv, ui + UV_WIDTH, vi + UV_WIDTH, rgbs2_0, rgbs3_0); |
||||||
|
} |
||||||
|
} else { |
||||||
|
const uchar8 rgbs0_0 = vload8(0, rgb + bgri_start); |
||||||
|
const uchar8 rgbs0_1 = vload8(0, rgb + bgri_start + 8); |
||||||
|
const uchar8 rgbs1_0 = vload8(0, rgb + bgri_start + RGB_STRIDE); |
||||||
|
const uchar8 rgbs1_1 = vload8(0, rgb + bgri_start + RGB_STRIDE + 8); |
||||||
|
if(num_col == 4) { |
||||||
|
convert_4_ys(out_yuv, yi_start, rgbs0_0, rgbs0_1); |
||||||
|
convert_4_ys(out_yuv, yi_start + WIDTH, rgbs1_0, rgbs1_1); |
||||||
|
convert_2_uvs(out_yuv, ui, vi, rgbs0_0, rgbs1_0, rgbs0_1, rgbs1_1); |
||||||
|
} else if(num_col == 2) { |
||||||
|
convert_2_ys(out_yuv, yi_start, rgbs0_0); |
||||||
|
convert_2_ys(out_yuv, yi_start + WIDTH, rgbs1_0); |
||||||
|
convert_uv(out_yuv, ui, vi, rgbs0_0, rgbs1_0); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,32 @@ |
|||||||
|
#ifndef RGB_TO_YUV_H |
||||||
|
#define RGB_TO_YUV_H |
||||||
|
|
||||||
|
#include <inttypes.h> |
||||||
|
#include <stdbool.h> |
||||||
|
|
||||||
|
#ifdef __APPLE__ |
||||||
|
#include <OpenCL/cl.h> |
||||||
|
#else |
||||||
|
#include <CL/cl.h> |
||||||
|
#endif |
||||||
|
|
||||||
|
#ifdef __cplusplus |
||||||
|
extern "C" { |
||||||
|
#endif |
||||||
|
|
||||||
|
typedef struct { |
||||||
|
int width, height; |
||||||
|
cl_kernel rgb_to_yuv_krnl; |
||||||
|
} RGBToYUVState; |
||||||
|
|
||||||
|
void rgb_to_yuv_init(RGBToYUVState* s, cl_context ctx, cl_device_id device_id, int width, int height, int rgb_stride); |
||||||
|
|
||||||
|
void rgb_to_yuv_destroy(RGBToYUVState* s); |
||||||
|
|
||||||
|
void rgb_to_yuv_queue(RGBToYUVState* s, cl_command_queue q, cl_mem rgb_cl, cl_mem yuv_cl); |
||||||
|
|
||||||
|
#ifdef __cplusplus |
||||||
|
} |
||||||
|
#endif |
||||||
|
|
||||||
|
#endif // RGB_TO_YUV_H
|
@ -0,0 +1,201 @@ |
|||||||
|
#include <memory.h> |
||||||
|
#include <iostream> |
||||||
|
#include <getopt.h> |
||||||
|
#include <math.h> |
||||||
|
#include <fstream> |
||||||
|
#include <cstdlib> |
||||||
|
#include <vector> |
||||||
|
#include <string> |
||||||
|
#include <iomanip> |
||||||
|
#include <thread> |
||||||
|
#include <fcntl.h> |
||||||
|
#include <signal.h> |
||||||
|
#include <unistd.h> |
||||||
|
#include <cassert> |
||||||
|
#include <cstdint> |
||||||
|
|
||||||
|
#ifdef ANDROID |
||||||
|
|
||||||
|
#define MAXE 0 |
||||||
|
#include <unistd.h> |
||||||
|
|
||||||
|
#else |
||||||
|
// The libyuv implementation on ARM is slightly different than on x86
|
||||||
|
// Our implementation matches the ARM version, so accept errors of 1
|
||||||
|
#define MAXE 1 |
||||||
|
|
||||||
|
#endif |
||||||
|
|
||||||
|
#include <libyuv.h> |
||||||
|
|
||||||
|
#include <CL/cl.h> |
||||||
|
|
||||||
|
#include "clutil.h" |
||||||
|
#include "rgb_to_yuv.h" |
||||||
|
|
||||||
|
|
||||||
|
static inline double millis_since_boot() { |
||||||
|
struct timespec t; |
||||||
|
clock_gettime(CLOCK_BOOTTIME, &t); |
||||||
|
return t.tv_sec * 1000.0 + t.tv_nsec * 1e-6; |
||||||
|
} |
||||||
|
|
||||||
|
void cl_init(cl_device_id &device_id, cl_context &context) { |
||||||
|
int err; |
||||||
|
cl_platform_id platform_id = NULL; |
||||||
|
cl_uint num_devices; |
||||||
|
cl_uint num_platforms; |
||||||
|
|
||||||
|
err = clGetPlatformIDs(1, &platform_id, &num_platforms); |
||||||
|
err = clGetDeviceIDs(platform_id, CL_DEVICE_TYPE_DEFAULT, 1, |
||||||
|
&device_id, &num_devices); |
||||||
|
cl_print_info(platform_id, device_id); |
||||||
|
context = clCreateContext(NULL, 1, &device_id, NULL, NULL, &err); |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
bool compare_results(uint8_t *a, uint8_t *b, int len, int stride, int width, int height, uint8_t *rgb) { |
||||||
|
int min_diff = 0., max_diff = 0., max_e = 0.; |
||||||
|
int e1 = 0, e0 = 0; |
||||||
|
int e0y = 0, e0u = 0, e0v = 0, e1y = 0, e1u = 0, e1v = 0; |
||||||
|
int max_e_i = 0; |
||||||
|
for (int i = 0;i < len;i++) { |
||||||
|
int e = ((int)a[i]) - ((int)b[i]); |
||||||
|
if(e < min_diff) { |
||||||
|
min_diff = e; |
||||||
|
} |
||||||
|
if(e > max_diff) { |
||||||
|
max_diff = e; |
||||||
|
} |
||||||
|
int e_abs = std::abs(e); |
||||||
|
if(e_abs > max_e) { |
||||||
|
max_e = e_abs; |
||||||
|
max_e_i = i; |
||||||
|
} |
||||||
|
if(e_abs < 1) { |
||||||
|
e0++; |
||||||
|
if(i < stride * height) |
||||||
|
e0y++; |
||||||
|
else if(i < stride * height + stride * height / 4) |
||||||
|
e0u++; |
||||||
|
else |
||||||
|
e0v++; |
||||||
|
} else { |
||||||
|
e1++; |
||||||
|
if(i < stride * height) |
||||||
|
e1y++; |
||||||
|
else if(i < stride * height + stride * height / 4) |
||||||
|
e1u++; |
||||||
|
else |
||||||
|
e1v++; |
||||||
|
} |
||||||
|
} |
||||||
|
//printf("max diff : %d, min diff : %d, e < 1: %d, e >= 1: %d\n", max_diff, min_diff, e0, e1);
|
||||||
|
//printf("Y: e < 1: %d, e >= 1: %d, U: e < 1: %d, e >= 1: %d, V: e < 1: %d, e >= 1: %d\n", e0y, e1y, e0u, e1u, e0v, e1v);
|
||||||
|
if(max_e <= MAXE) { |
||||||
|
return true; |
||||||
|
} |
||||||
|
int row = max_e_i / stride; |
||||||
|
if(row < height) { |
||||||
|
printf("max error is Y: %d = (libyuv: %u - cl: %u), row: %d, col: %d\n", max_e, a[max_e_i], b[max_e_i], row, max_e_i % stride); |
||||||
|
} else if(row >= height && row < (height + height / 4)) { |
||||||
|
printf("max error is U: %d = %u - %u, row: %d, col: %d\n", max_e, a[max_e_i], b[max_e_i], (row - height) / 2, max_e_i % stride / 2); |
||||||
|
} else { |
||||||
|
printf("max error is V: %d = %u - %u, row: %d, col: %d\n", max_e, a[max_e_i], b[max_e_i], (row - height - height / 4) / 2, max_e_i % stride / 2); |
||||||
|
} |
||||||
|
return false; |
||||||
|
} |
||||||
|
|
||||||
|
int main(int argc, char** argv) { |
||||||
|
srand(1337); |
||||||
|
|
||||||
|
clu_init(); |
||||||
|
cl_device_id device_id; |
||||||
|
cl_context context; |
||||||
|
cl_init(device_id, context) ; |
||||||
|
|
||||||
|
int err; |
||||||
|
const cl_queue_properties props[] = {0}; //CL_QUEUE_PRIORITY_KHR, CL_QUEUE_PRIORITY_HIGH_KHR, 0};
|
||||||
|
cl_command_queue q = clCreateCommandQueueWithProperties(context, device_id, props, &err); |
||||||
|
if(err != 0) { |
||||||
|
std::cout << "clCreateCommandQueueWithProperties error: " << err << std::endl; |
||||||
|
} |
||||||
|
|
||||||
|
int width = 1164; |
||||||
|
int height = 874; |
||||||
|
|
||||||
|
int opt = 0; |
||||||
|
while ((opt = getopt(argc, argv, "f")) != -1) |
||||||
|
{ |
||||||
|
switch (opt) |
||||||
|
{ |
||||||
|
case 'f': |
||||||
|
std::cout << "Using front camera dimensions" << std::endl; |
||||||
|
int width = 1152; |
||||||
|
int height = 846; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
std::cout << "Width: " << width << " Height: " << height << std::endl; |
||||||
|
uint8_t *rgb_frame = new uint8_t[width * height * 3]; |
||||||
|
|
||||||
|
|
||||||
|
RGBToYUVState rgb_to_yuv_state; |
||||||
|
rgb_to_yuv_init(&rgb_to_yuv_state, context, device_id, width, height, width * 3); |
||||||
|
|
||||||
|
int frame_yuv_buf_size = width * height * 3 / 2; |
||||||
|
cl_mem yuv_cl = clCreateBuffer(context, CL_MEM_READ_WRITE, frame_yuv_buf_size, (void*)NULL, &err); |
||||||
|
uint8_t *frame_yuv_buf = new uint8_t[frame_yuv_buf_size]; |
||||||
|
uint8_t *frame_yuv_ptr_y = frame_yuv_buf; |
||||||
|
uint8_t *frame_yuv_ptr_u = frame_yuv_buf + (width * height); |
||||||
|
uint8_t *frame_yuv_ptr_v = frame_yuv_ptr_u + ((width/2) * (height/2)); |
||||||
|
|
||||||
|
cl_mem rgb_cl = clCreateBuffer(context, CL_MEM_READ_WRITE, width * height * 3, (void*)NULL, &err); |
||||||
|
int mismatched = 0; |
||||||
|
int counter = 0; |
||||||
|
srand (time(NULL)); |
||||||
|
|
||||||
|
for (int i = 0; i < 100; i++){ |
||||||
|
for (int i = 0; i < width * height * 3; i++){ |
||||||
|
rgb_frame[i] = (uint8_t)rand(); |
||||||
|
} |
||||||
|
|
||||||
|
double t1 = millis_since_boot(); |
||||||
|
libyuv::RGB24ToI420((uint8_t*)rgb_frame, width * 3, |
||||||
|
frame_yuv_ptr_y, width, |
||||||
|
frame_yuv_ptr_u, width/2, |
||||||
|
frame_yuv_ptr_v, width/2, |
||||||
|
width, height); |
||||||
|
double t2 = millis_since_boot(); |
||||||
|
//printf("Libyuv: rgb to yuv: %.2fms\n", t2-t1);
|
||||||
|
|
||||||
|
clEnqueueWriteBuffer(q, rgb_cl, CL_TRUE, 0, width * height * 3, (void *)rgb_frame, 0, NULL, NULL); |
||||||
|
t1 = millis_since_boot(); |
||||||
|
rgb_to_yuv_queue(&rgb_to_yuv_state, q, rgb_cl, yuv_cl); |
||||||
|
t2 = millis_since_boot(); |
||||||
|
|
||||||
|
//printf("OpenCL: rgb to yuv: %.2fms\n", t2-t1);
|
||||||
|
uint8_t *yyy = (uint8_t *)clEnqueueMapBuffer(q, yuv_cl, CL_TRUE, |
||||||
|
CL_MAP_READ, 0, frame_yuv_buf_size, |
||||||
|
0, NULL, NULL, &err); |
||||||
|
if(!compare_results(frame_yuv_ptr_y, yyy, frame_yuv_buf_size, width, width, height, (uint8_t*)rgb_frame)) |
||||||
|
mismatched++; |
||||||
|
clEnqueueUnmapMemObject(q, yuv_cl, yyy, 0, NULL, NULL); |
||||||
|
|
||||||
|
// std::this_thread::sleep_for(std::chrono::milliseconds(20));
|
||||||
|
if(counter++ % 100 == 0) |
||||||
|
printf("Matched: %d, Mismatched: %d\n", counter - mismatched, mismatched); |
||||||
|
|
||||||
|
} |
||||||
|
printf("Matched: %d, Mismatched: %d\n", counter - mismatched, mismatched); |
||||||
|
|
||||||
|
delete[] frame_yuv_buf; |
||||||
|
rgb_to_yuv_destroy(&rgb_to_yuv_state); |
||||||
|
clReleaseContext(context); |
||||||
|
delete[] rgb_frame; |
||||||
|
|
||||||
|
if (mismatched == 0) |
||||||
|
return 0; |
||||||
|
else |
||||||
|
return -1; |
||||||
|
} |
Loading…
Reference in new issue