Commit 44c105e7 authored by enfo's avatar enfo

Merge branch 'develop' into 'master'

Develop

See merge request enfo/sentiboard-utils!12
parents d7c27900 5d962718
image: gcc-cpplint-cmake-gcov
build:
stage: build
script:
- make -C parsers tests -j 4
artifacts:
paths:
- parsers/build/sentiboard_utils_test
cache:
paths:
- parsers/build/
lint:
stage: build
script:
- make -C parsers lint
test:
stage: test
script:
- ./parsers/build/sentiboard_utils_test
coverage:
stage: test
script:
- make -C parsers coverage
coverage: '/lines[.: ]*\d+.\d+%/'
artifacts:
paths:
- parsers/build_coverage/sentiboard_utils_test
- parsers/coverage/
cache:
paths:
- parsers/build_coverage/
- parsers/coverage/
branch-coverage:
stage: test
script:
- make -C parsers coverage
coverage: '/branches[.: ]*\d+.\d+%/'
artifacts:
paths:
- parsers/build_coverage/sentiboard_utils_test
- parsers/coverage/
cache:
paths:
- parsers/build_coverage/
- parsers/coverage/
Sentiboard Utils
================
Status
------
[![build status](https://kyb.guru/gitlab/enfo/sentiboard-utils/badges/master/pipeline.svg)](https://kyb.guru/gitlab/enfo/sentiboard-utils/commits/master)
Lines: [![coverage report](https://kyb.guru/gitlab/enfo/sentiboard-utils/badges/master/coverage.svg?job=coverage)](https://kyb.guru/gitlab/enfo/sentiboard-utils/commits/master)
Branches: [![coverage report](https://kyb.guru/gitlab/enfo/sentiboard-utils/badges/master/coverage.svg?job=branch-coverage)](https://kyb.guru/gitlab/enfo/sentiboard-utils/commits/master)
Usage
=====
......
Note that the UART connector is pin-equal to the RS-232 connector, except that
the CTS and RTS pins are not connected.
# -*- coding: utf-8 -*-
"""
Created on Fri May 4 21:08:58 2018
@author: Enfenion
"""
import argparse
import os
import rtk
import datetime
kml_start='''<?xml version="1.0" encoding="UTF-8"?>
<kml xmlns="http://www.opengis.net/kml/2.2">
<Document>
<name>Paths</name>
<description>Examples of paths. Note that the tessellate tag is by default
set to 0. If you want to create tessellated lines, they must be authored
(or edited) directly in KML.</description>
<Style id="yellowLineGreenPoly">
<LineStyle>
<color>7f00ffff</color>
<width>4</width>
</LineStyle>
<PolyStyle>
<color>7f00ff00</color>
</PolyStyle>
</Style>
<Placemark>
<name>Absolute Extruded</name>
<description>Transparent green wall with yellow outlines</description>
<styleUrl>#yellowLineGreenPoly</styleUrl>
<LineString>
<extrude>1</extrude>
<tessellate>1</tessellate>
<altitudeMode>absolute</altitudeMode>
<coordinates>
'''
kml_end=''' </coordinates>
</LineString>
</Placemark>
</Document>
</kml>
'''
kml2_start='''<?xml version="1.0" encoding="UTF-8"?>
<kml xmlns="http://www.opengis.net/kml/2.2" xmlns:gx="http://www.google.com/kml/ext/2.2" xmlns:kml="http://www.opengis.net/kml/2.2" xmlns:atom="http://www.w3.org/2005/Atom">
<Document>
<name>UAV-icons.kml</name>
<Style id="sn_airports">
<IconStyle>
<color>ffffaa00</color>
<scale>1.2</scale>
<Icon>
<href>http://maps.google.com/mapfiles/kml/shapes/airports.png</href>
</Icon>
<hotSpot x="0.5" y="0" xunits="fraction" yunits="fraction"/>
</IconStyle>
<ListStyle>
</ListStyle>
<LineStyle>
<color>99ffaa00</color>
<width>6</width>
</LineStyle>
</Style>
<StyleMap id="msn_airports">
<Pair>
<key>normal</key>
<styleUrl>#sn_airports</styleUrl>
</Pair>
<Pair>
<key>highlight</key>
<styleUrl>#sh_airports</styleUrl>
</Pair>
</StyleMap>
<Style id="sh_airports">
<IconStyle>
<color>ffffaa00</color>
<scale>1.4</scale>
<Icon>
<href>http://maps.google.com/mapfiles/kml/shapes/airports.png</href>
</Icon>
<hotSpot x="0.5" y="0" xunits="fraction" yunits="fraction"/>
</IconStyle>
<ListStyle>
</ListStyle>
<LineStyle>
<color>99ffaa00</color>
<width>8</width>
</LineStyle>
</Style>
<Placemark>
<name>UAV</name>
<styleUrl>#msn_airports</styleUrl>
<gx:Track>
<altitudeMode>relativeToGround</altitudeMode>
'''
kml2_end=''' </gx:Track>
</Placemark>
</Document>
</kml>
'''
def create_kml(data, destname, interval):
lats, lons, alts = data['latitude'], data['longitude'], data['height']
gpsweek, gpsseconds = data['week'], data['gpst']
mode=2
leapseconds = 0
last = datetime.datetime(1980, 1, 6, 0, 0, 0)
with open(destname, 'w') as f:
if mode == 1:
f.write(kml_start)
for ix in range(len(lats)):
f.write('%.9f,%.9f,%.2f\n' % (lons[ix], lats[ix], alts[ix]))
f.write(kml_end)
else:
f.write(kml2_start)
for ix in range(len(lats)):
d = rtk.weeksecondstoutc(gpsweek[ix],gpsseconds[ix],leapseconds)
if (d - last).total_seconds() < interval:
continue
last = d
f.write('<when>%s</when>\n' % d.strftime("%Y-%m-%dT%H:%M:%SZ"))
f.write('<gx:coord>')
f.write('%.9f,%.9f,%.2f' % (lons[ix], lats[ix], alts[ix]))
f.write('</gx:coord>\n')
f.write(kml2_end)
print('Stored .kml to %s' % destname)
if __name__ == '__main__':
parser = argparse.ArgumentParser(description='Convert an RTK .pos-file to a .kml for Google Earth')
parser.add_argument('pos', help='RTK .pos-file')
parser.add_argument('--output', '-O', dest='dest', help='Optional destination file (default *.kml)')
parser.add_argument('--interval', '-d', dest='interval', type=float, help='Interval between each data point', default=1)
args = parser.parse_args()
filename = args.pos
if args.dest:
destname = args.dest
else:
destname = os.path.splitext(filename)[0] + '.kml'
data, names = rtk.parse_file(filename)
create_kml(data, destname, args.interval)
# -*- coding: utf-8 -*-
"""
Created on Fri May 4 21:44:51 2018
@author: Enfenion
"""
import re
import datetime
def weeksecondstoutc(gpsweek,gpsseconds,leapseconds):
epoch = datetime.datetime(1980, 1, 6, 0, 0, 0)
elapsed = datetime.timedelta(days=(gpsweek * 7), seconds=(gpsseconds + leapseconds))
return epoch + elapsed
def split_line(line):
if '|' in line:
return line.split('|')
if '\t' in line:
return line.split('\t')
return re.sub(' +', ' ', line).split(' ')
def parseline(line, names):
values = split_line(line)
converters = [int] + [float]*4 + [int]*2 + [float]*8
conv_vals = [c(v) for c, v in zip(converters, values) ]
return dict(zip(names, conv_vals))
def count_lines(f):
n_lines = 0
for line in f:
if line.startswith('%'):
continue
n_lines += 1
f.seek(0)
return n_lines
def handle_name(name):
name = name.strip().lower()
if '(' in name:
name = name[:name.index('(')]
name = name.replace('-', '_')
return name
def extract_data(f, n_lines):
names = ['week', 'tow', 'ecef_x', 'ecef_y', 'ecef_z', 'Q', 'ns',
'sdx', 'sdy', 'sdz', 'sdxy', 'sdyz', 'sdzx', 'age', 'ratio']
ref_pos = None
tmpbuf = [0] * n_lines
print('Found %d lines' % n_lines)
data = {}
c_line = 0
for line in f:
if line.startswith('%'):
if 'age' in line:
names = [handle_name(name) for name in split_line(line.strip('% '))]
if names[0] == 'gpst':
names.insert(0, 'week')
if 'ref pos' in line:
data_start = line.index(':', 1)
ref_pos = [float(p) for p in split_line(line[data_start+1:].strip())]
continue
data_line = parseline(line, names)
for key, val in data_line.items():
try:
data[key][c_line] = val
except KeyError:
data[key] = list(tmpbuf)
data[key][c_line] = val
c_line += 1
f.seek(0)
return data, names, ref_pos
def parse_file(infile):
ref_pos = None
with open(infile, 'rU') as f:
n_lines = count_lines(f)
data, names, ref_pos = extract_data(f, n_lines)
if 'ecef_x' in names:
try:
from pymap3d import ecef2geodetic
print('Adding latlon')
lat, lon, height = ecef2geodetic(data['ecef_x'], data['ecef_y'], data['ecef_z'])
data['latitude'] = lat
data['longitude'] = lon
data['height'] = height
names += ['latitude', 'longitude', 'height']
except ImportError:
print('Install pymap3d to automatically add enu solution')
if 'latitude' in names and ref_pos is not None:
try:
from pymap3d import geodetic2enu
print('Adding enu')
east, north, up = geodetic2enu(data['latitude'], data['longitude'], data['height'], *ref_pos)
data['e_baseline'] = east
data['n_baseline'] = north
data['u_baseline'] = up
names += ['e_baseline', 'n_baseline', 'u_baseline']
except ImportError:
print('Install pymap3d to automatically add enu solution')
elif 'e_baseline' in names:
try:
from pymap3d import enu2geodetic
print('Adding latlon')
lat, lon, height = enu2geodetic(data['e_baseline'], data['n_baseline'], data['u_baseline'], *ref_pos)
data['latitude'] = lat
data['longitude'] = lon
data['height'] = height
names += ['latitude', 'longitude', 'height']
except ImportError:
print('Install pymap3d to automatically add enu solution')
return data, names
\ No newline at end of file
......@@ -3,6 +3,7 @@
import sys
import os
import re
import numpy as np
from scipy.io import savemat
dir_path = os.path.dirname(os.path.realpath(__file__))
sys.path.append(os.path.join(dir_path, '..'))
......@@ -52,6 +53,7 @@ if __name__ == '__main__':
'sdz', 'sdxy', 'sdyz', 'sdzx', 'age', 'ratio']
n_lines = 0
ref_pos = None
with open(infile, 'rU') as f:
for line in f:
if line.startswith('%'):
......@@ -69,6 +71,9 @@ if __name__ == '__main__':
names = [handle_name(name) for name in split_line(line.strip('% '))]
if names[0] == 'gpst':
names.insert(0, 'week')
if 'ref pos' in line:
data_start = line.index(':', 1)
ref_pos = [float(p) for p in split_line(line[data_start+1:].strip())]
continue
data_line = parseline(line, names)
for key, val in data_line.items():
......@@ -79,6 +84,38 @@ if __name__ == '__main__':
data[key][c_line] = val
c_line += 1
if 'latitude' in names:
try:
from pymap3d import geodetic2enu
print('Adding enu')
east, north, up = geodetic2enu(data['latitude'], data['longitude'], data['height'], *ref_pos)
data['e_baseline'] = east
data['n_baseline'] = north
data['u_baseline'] = up
names += ['e_baseline', 'n_baseline', 'u_baseline']
except ImportError:
print('Install pymap3d to automatically add enu solution')
elif 'e_baseline' in names:
try:
from pymap3d import enu2geodetic
print('Adding latlon')
lat, lon, height = geodetic2enu(data['e_baseline'], data['n_baseline'], data['u_baseline'], *ref_pos)
data['latitude'] = lat
data['longitude'] = lon
data['height'] = height
names += ['latitude', 'longitude', 'height']
except ImportError:
print('Install pymap3d to automatically add enu solution')
print('Saving .mat')
savemat(outfile, {'rtklib': data})
print('Saving .npy')
values = np.array([data[name] for name in names])
struct_packs = np.array(values,
dtype={'names': names, 'formats': ['d']*len(names)})
np.savez_compressed(outfile[:-4], rtk=struct_packs)
print('Done.')
......@@ -40,7 +40,7 @@ def extract_packages(fname, parsers):
try:
pack = parser.parse_buffer(raw_pack)
except Exception as e:
print 'Could not parse package', e
print('Could not parse package: %s' % e)
continue
except EOFError:
break
......@@ -52,7 +52,7 @@ def extract_packages(fname, parsers):
packages[raw_pack.package_type] = data
for k, v in pack.dict.iteritems():
for k, v in pack.dict.items():
try:
data[k].append(v)
except KeyError:
......@@ -62,19 +62,19 @@ def extract_packages(fname, parsers):
def handle_data_directory(sb_data_dir, parsers, outdir):
global data
for i in xrange(1, 101):
for i in range(1, 101):
if i == 100:
raise IndexError('Could not find proper directory')
odir = outdir + '_%02d' % i
if not os.path.exists(odir):
os.mkdir(odir, 0770)
os.mkdir(odir, 0o0770)
break
if os.listdir(odir) == []:
break
outdir = os.path.abspath(odir)
print('Saving to %s' % outdir)
print
print('')
globname = glob(os.path.join(sb_data_dir, '*.*'))
......@@ -91,9 +91,7 @@ def handle_data_directory(sb_data_dir, parsers, outdir):
return
def do_combine_data(data_dir, combined_dir, overwrite, newnames=None):
if newnames is None:
newnames = {}
def do_combine_data(data_dir, combined_dir, overwrite):
if os.path.exists(combined_dir):
if not overwrite:
return combined_dir
......@@ -102,20 +100,17 @@ def do_combine_data(data_dir, combined_dir, overwrite, newnames=None):
sensor_files = {}
for pardir in sorted(glob(os.path.join(data_dir, '*_[0-9]*'))):
print 'pardir:', pardir
for p in glob(os.path.join(pardir, '*')):
print 'Handling', os.path.abspath(p)
print('Handling %s' % os.path.abspath(p))
_, fname = os.path.split(p)
try:
sensor_files[fname].append(p)
except KeyError:
sensor_files[fname] = [p]
for n, filelist in sensor_files.iteritems():
try:
newname = newnames[n]
except KeyError:
newname = n
for n, filelist in sensor_files.items():
newname = n
with open(os.path.join(combined_dir, newname), 'wb') as out_f:
for fn in filelist:
with open(fn, 'rb') as in_f:
......@@ -149,6 +144,7 @@ if __name__ == '__main__':
else:
print('not combining')
outdir = os.path.join(sb_data_dir, outdir_name)
print('Getting data from', sb_data_dir)
print('Writing data to:', outdir)
parsers = {'adis': AdisParser(), 'aeroprobe': AeroprobeParser(),
......@@ -157,4 +153,4 @@ if __name__ == '__main__':
try:
handle_data_directory(sb_data_dir, parsers, outdir)
finally:
print 'Done'
print('Done')
......@@ -17,7 +17,7 @@ for flight_id in $( seq 0 999 ); do
FLIGHT_DIR=$( printf "$FLIGHT_DIR_NAME%03d" $flight_id )
FLIGHT_PATH=$ROOT_DIR/$FLIGHT_DIR
if [ ! -e "$FLIGHT_PATH" ]; then
break
break
fi
done
......@@ -37,7 +37,7 @@ for logger in ${LOGGER_PROGRAMS[@]}; do
mkdir "$FLIGHT_PATH/$logdir"
#Start task, with logdir as param
echo "Starting logger $logger in directory $FLIGHT_PATH/$logdir"
$logger $FLIGHT_PATH/$logdir &
$logger $FLIGHT_PATH/$logdir &
pid=$!
LOGGERS[$logger]="$!;$logdir"
done
......
cmake_minimum_required (VERSION 3.1.0 FATAL_ERROR)
project (sentiboard-utils)
# The version number.
set (sentiboard_utils_VERSION_MAJOR 0)
set (sentiboard_utils_VERSION_MINOR 1)
set (SENTIBOARD_SRC_DIR "${CMAKE_SOURCE_DIR}/src")
set (SENTIBOARD_INCLUDE_DIR "${CMAKE_SOURCE_DIR}/include")
include_directories(
${SENTIBOARD_INCLUDE_DIR}
)
set(SENTIBOARD_SRCS
${SENTIBOARD_SRC_DIR}/Reader.cpp
${SENTIBOARD_SRC_DIR}/Package.cpp
)
FILE(GLOB_RECURSE SENTIBOARD_HEADERS "include/*.h" "include/*.hpp)")
add_library(sentiboard_utils
${SENTIBOARD_SRCS}
${SENTIBOARD_HEADERS}
)
target_compile_features(sentiboard_utils PRIVATE cxx_auto_type cxx_range_for)
# -- Set up test project --
set (TEST_TARGET "sentiboard_utils_test")
set (TEST_SRC_DIR "${CMAKE_SOURCE_DIR}/tests")
set (TEST_INCLUDE_DIR "${TEST_SRC_DIR}/include")
include_directories(
${TEST_INCLUDE_DIR}
)
# Select wether to use Catch c++ or doctest
option(USE_CATCH_TESTS "Use the Catch c++ test framework" True)
set(SENTIBOARD_TEST_HEADERS "${TEST_SRC_DIR}/include/framework/test_framework.hpp")
if(USE_CATCH_TESTS)
add_definitions(-DUSE_CATCH)
list(APPEND SENTIBOARD_TEST_HEADERS "${TEST_SRC_DIR}/include/framework/catch.hpp")
else()
list(APPEND SENTIBOARD_TEST_HEADERS "${TEST_SRC_DIR}/include/framework/doctest.h")
endif()
set(SENTIBOARD_TEST_SRCS
${TEST_SRC_DIR}/testmain.cpp
${TEST_SRC_DIR}/sentiboard_reader_test.cpp
${TEST_SRC_DIR}/stim_parser_test.cpp
${TEST_SRC_DIR}/test_utils.cpp
)
add_executable(${TEST_TARGET}
${SENTIBOARD_TEST_SRCS}
${SENTIBOARD_TEST_HEADERS}
)
target_compile_features(${TEST_TARGET} PRIVATE cxx_auto_type cxx_nullptr)
target_link_libraries(${TEST_TARGET} sentiboard_utils)
# -- Set up code coverage project --
set(CMAKE_MODULE_PATH ${CMAKE_CURRENT_SOURCE_DIR}/cmake-modules)
if (CMAKE_BUILD_TYPE STREQUAL "Coverage")
SET(CMAKE_CXX_FLAGS "-g -O0 -fprofile-arcs -ftest-coverage")
SET(CMAKE_C_FLAGS "-g -O0 -fprofile-arcs -ftest-coverage")
include(CodeCoverage)
SETUP_TARGET_FOR_COVERAGE(test_coverage ${TEST_TARGET} ${PROJECT_SOURCE_DIR}/coverage)
endif() #CMAKE_BUILD_TYPE STREQUAL "Coverage"
CPPLINT_EXCLUDES = --exclude=tests/include/framework/* --filter=-readability/casting,-readability/check --repository=.
BIN_DIR = build
BIN_COV_DIR = build_coverage
TEST_TARGET = sentiboard_utils_test
.PHONY: all
all: tests
.PHONY: tests
tests :
mkdir -p $(BIN_DIR)
cd $(BIN_DIR) && cmake ..
make -C $(BIN_DIR) -j 8
.PHONY: lint
lint:
cpplint --recursive $(CPPLINT_EXCLUDES) src include tests simulator
.PHONY: coverage
coverage:
mkdir -p $(BIN_COV_DIR)
cd $(BIN_COV_DIR) && cmake -DCMAKE_BUILD_TYPE=Coverage ..
make -C $(BIN_COV_DIR) -j8 test_coverage
.PHONY: run
run: $(BIN_DIR)/$(TEST_TARGET)
./$(BIN_DIR)/$(TEST_TARGET)
.PHONY: test
test: run
.PHONY: clean
clean:
$(RM) -r build
#
# 2012-01-31, Lars Bilke
# - Enable Code Coverage
#
# 2013-09-17, Joakim Söderberg
# - Added support for Clang.
# - Some additional usage instructions.
#
# USAGE:
# 0. (Mac only) If you use Xcode 5.1 make sure to patch geninfo as described here:
# http://stackoverflow.com/a/22404544/80480
#
# 1. Copy this file into your cmake modules path.
#
# 2. Add the following line to your CMakeLists.txt:
# INCLUDE(CodeCoverage)
#
# 3. Set compiler flags to turn off optimization and enable coverage:
# SET(CMAKE_CXX_FLAGS "-g -O0 -fprofile-arcs -ftest-coverage")
# SET(CMAKE_C_FLAGS "-g -O0 -fprofile-arcs -ftest-coverage")
#
# 3. Use the function SETUP_TARGET_FOR_COVERAGE to create a custom make target
# which runs your test executable and produces a lcov code coverage report:
# Example:
# SETUP_TARGET_FOR_COVERAGE(
# my_coverage_target # Name for custom target.
# test_driver # Name of the test driver executable that runs the tests.
# # NOTE! This should always have a ZERO as exit code
# # otherwise the coverage generation will not complete.
# coverage # Name of output directory.
# )
#
# 4. Build a Debug build:
# cmake -DCMAKE_BUILD_TYPE=Debug ..
# make
# make my_coverage_target
#
#
# Check prereqs
FIND_PROGRAM( GCOV_PATH gcov )
FIND_PROGRAM( LCOV_PATH lcov )
FIND_PROGRAM( GENHTML_PATH genhtml )
FIND_PROGRAM( GCOVR_PATH gcovr PATHS ${CMAKE_SOURCE_DIR}/tests)
IF(NOT GCOV_PATH)
MESSAGE(FATAL_ERROR "gcov not found! Aborting...")
ENDIF() # NOT GCOV_PATH
IF(NOT CMAKE_COMPILER_IS_GNUCXX)
# Clang version 3.0.0 and greater now supports gcov as well.
MESSAGE(WARNING "Compiler is not GNU gcc! Clang Version 3.0.0 and greater supports gcov as well, but older versions don't.")
IF(NOT "${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang")
MESSAGE(FATAL_ERROR "Compiler is not GNU gcc! Aborting...")
ENDIF()
ENDIF() # NOT CMAKE_COMPILER_IS_GNUCXX
SET(CMAKE_CXX_FLAGS_COVERAGE
"-Wunused-variable -g -O0 --coverage -fprofile-arcs -ftest-coverage"
CACHE STRING "Flags used by the C++ compiler during coverage builds."
FORCE )
SET(CMAKE_C_FLAGS_COVERAGE
"-Wunused-variable -g -O0 --coverage -fprofile-arcs -ftest-coverage"
CACHE STRING "Flags used by the C compiler during coverage builds."
FORCE )
SET(CMAKE_EXE_LINKER_FLAGS_COVERAGE
""
CACHE STRING "Flags used for linking binaries during coverage builds."
FORCE )
SET(CMAKE_SHARED_LINKER_FLAGS_COVERAGE
""
CACHE STRING "Flags used by the shared libraries linker during coverage builds."
FORCE )
MARK_AS_ADVANCED(
CMAKE_CXX_FLAGS_COVERAGE
CMAKE_C_FLAGS_COVERAGE
CMAKE_EXE_LINKER_FLAGS_COVERAGE
CMAKE_SHARED_LINKER_FLAGS_COVERAGE )
IF ( NOT (CMAKE_BUILD_TYPE STREQUAL "Debug" OR CMAKE_BUILD_TYPE STREQUAL "Coverage"))
MESSAGE( WARNING "Code coverage results with an optimized (non-Debug) build may be misleading" )
ENDIF() # NOT CMAKE_BUILD_TYPE STREQUAL "Debug"
# Param _targetname The name of new the custom make target
# Param _testrunner The name of the target which runs the tests.
# MUST return ZERO always, even on errors.
# If not, no coverage report will be created!
# Param _outputname lcov output is generated as _outputname.info
# HTML report is generated in _outputname/index.html
# Optional fourth parameter is passed as arguments to _testrunner
# Pass them in list form, e.g.: "-j;2" for -j 2
FUNCTION(SETUP_TARGET_FOR_COVERAGE _targetname _testrunner _outputname)
IF(NOT LCOV_PATH)
MESSAGE(FATAL_ERROR "lcov not found! Aborting...")
ENDIF() # NOT LCOV_PATH
IF(NOT GENHTML_PATH)