aboutsummaryrefslogtreecommitdiff
path: root/Tools
diff options
context:
space:
mode:
authorLorenz Meier <lm@inf.ethz.ch>2014-07-07 00:05:29 +0200
committerLorenz Meier <lm@inf.ethz.ch>2014-07-07 00:05:29 +0200
commit5559e568b6e5502ad51ec96fd531876c1191d641 (patch)
treebc6356b5bed0481cced4a7d7e7ef99bd225d91a7 /Tools
parent863385dbc4243c8ecb19890a0dfce4a0b7ead9d6 (diff)
parentd67089b23f58ac152253f58c5deaebbd57db0362 (diff)
downloadpx4-firmware-safelink.tar.gz
px4-firmware-safelink.tar.bz2
px4-firmware-safelink.zip
Merged master into safelinksafelink
Diffstat (limited to 'Tools')
-rw-r--r--Tools/.gitignore (renamed from Tools/px4params/.gitignore)1
-rw-r--r--Tools/fetch_log.py133
-rwxr-xr-xTools/fix_code_style.sh2
-rwxr-xr-xTools/fix_code_style_ubuntu.sh19
-rwxr-xr-xTools/fsm_visualisation.py201
-rw-r--r--Tools/px4params/README.md10
-rw-r--r--Tools/px4params/__init__.py1
-rw-r--r--Tools/px4params/dokuwikiout.py80
-rw-r--r--Tools/px4params/dokuwikiout_listings.py27
-rw-r--r--Tools/px4params/dokuwikirpc.py16
-rw-r--r--Tools/px4params/output.py5
-rwxr-xr-xTools/px4params/px_process_params.py61
-rw-r--r--Tools/px4params/srcparser.py (renamed from Tools/px4params/parser.py)25
-rw-r--r--Tools/px4params/srcscanner.py (renamed from Tools/px4params/scanner.py)14
-rw-r--r--Tools/px4params/xmlout.py12
-rw-r--r--Tools/px4params/xmlrpc.sh5
-rw-r--r--Tools/px_generate_xml.sh2
-rw-r--r--Tools/px_process_params.py140
-rw-r--r--Tools/px_romfs_pruner.py84
-rw-r--r--Tools/px_update_wiki.sh2
-rwxr-xr-xTools/px_uploader.py28
-rw-r--r--Tools/tests-host/.gitignore2
-rw-r--r--Tools/tests-host/Makefile60
-rw-r--r--Tools/tests-host/autodeclination_test.cpp28
-rw-r--r--Tools/tests-host/board_config.h0
-rw-r--r--Tools/tests-host/debug.h5
-rw-r--r--Tools/tests-host/mixer_test.cpp2
-rwxr-xr-xTools/tests-host/run_tests.sh6
-rw-r--r--Tools/tests-host/sbus2_test.cpp75
29 files changed, 798 insertions, 248 deletions
diff --git a/Tools/px4params/.gitignore b/Tools/.gitignore
index 5d0378b4a..7628bda82 100644
--- a/Tools/px4params/.gitignore
+++ b/Tools/.gitignore
@@ -1,3 +1,2 @@
parameters.wiki
parameters.xml
-cookies.txt \ No newline at end of file
diff --git a/Tools/fetch_log.py b/Tools/fetch_log.py
new file mode 100644
index 000000000..edcc6557c
--- /dev/null
+++ b/Tools/fetch_log.py
@@ -0,0 +1,133 @@
+#!/usr/bin/env python
+############################################################################
+#
+# Copyright (C) 2012, 2013 PX4 Development Team. All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+#
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in
+# the documentation and/or other materials provided with the
+# distribution.
+# 3. Neither the name PX4 nor the names of its contributors may be
+# used to endorse or promote products derived from this software
+# without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+# COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+# OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+# AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+# POSSIBILITY OF SUCH DAMAGE.
+#
+############################################################################
+
+#
+# Log fetcher
+#
+# Print list of logs:
+# python fetch_log.py
+#
+# Fetch log:
+# python fetch_log.py sess001/log001.bin
+#
+
+import serial, time, sys, os
+
+def wait_for_string(ser, s, timeout=1.0, debug=False):
+ t0 = time.time()
+ buf = []
+ res = []
+ n = 0
+ while (True):
+ c = ser.read()
+ if debug:
+ sys.stderr.write(c)
+ buf.append(c)
+ if len(buf) > len(s):
+ res.append(buf.pop(0))
+ n += 1
+ if n % 10000 == 0:
+ sys.stderr.write(str(n) + "\n")
+ if "".join(buf) == s:
+ break
+ if timeout > 0.0 and time.time() - t0 > timeout:
+ raise Exception("Timeout while waiting for: " + s)
+ return "".join(res)
+
+def exec_cmd(ser, cmd, timeout):
+ ser.write(cmd + "\n")
+ ser.flush()
+ wait_for_string(ser, cmd + "\r\n", timeout)
+ return wait_for_string(ser, "nsh> \x1b[K", timeout)
+
+def ls_dir(ser, dir, timeout=1.0):
+ res = []
+ for line in exec_cmd(ser, "ls -l " + dir, timeout).splitlines()[1:]:
+ res.append((line[20:], int(line[11:19].strip()), line[1] == "d"))
+ return res
+
+def list_logs(ser):
+ logs_dir = "/fs/microsd/log"
+ res = []
+ for d in ls_dir(ser, logs_dir):
+ if d[2]:
+ sess_dir = d[0][:-1]
+ for f in ls_dir(ser, logs_dir + "/" + sess_dir):
+ log_file = f[0]
+ log_size = f[1]
+ res.append(sess_dir + "/" + log_file + "\t" + str(log_size))
+ return "\n".join(res)
+
+def fetch_log(ser, fn, timeout):
+ cmd = "dumpfile " + fn
+ ser.write(cmd + "\n")
+ ser.flush()
+ wait_for_string(ser, cmd + "\r\n", timeout, True)
+ res = wait_for_string(ser, "\n", timeout, True)
+ data = []
+ if res.startswith("OK"):
+ size = int(res.split()[1])
+ n = 0
+ print "Reading data:"
+ while (n < size):
+ buf = ser.read(min(size - n, 8192))
+ data.append(buf)
+ n += len(buf)
+ sys.stdout.write(".")
+ sys.stdout.flush()
+ print
+ else:
+ raise Exception("Error reading log")
+ wait_for_string(ser, "nsh> \x1b[K", timeout)
+ return "".join(data)
+
+def main():
+ dev = "/dev/tty.usbmodem1"
+ ser = serial.Serial(dev, "115200", timeout=0.2)
+ if len(sys.argv) < 2:
+ print list_logs(ser)
+ else:
+ log_file = sys.argv[1]
+ data = fetch_log(ser, "/fs/microsd/log/" + log_file, 1.0)
+ try:
+ os.mkdir(log_file.split("/")[0])
+ except:
+ pass
+ fout = open(log_file, "wb")
+ fout.write(data)
+ fout.close()
+ ser.close()
+
+if __name__ == "__main__":
+ main()
diff --git a/Tools/fix_code_style.sh b/Tools/fix_code_style.sh
index 832ee79da..5995d428e 100755
--- a/Tools/fix_code_style.sh
+++ b/Tools/fix_code_style.sh
@@ -16,4 +16,6 @@ astyle \
--ignore-exclude-errors-x \
--lineend=linux \
--exclude=EASTL \
+ --add-brackets \
+ --max-code-length=120 \
$*
diff --git a/Tools/fix_code_style_ubuntu.sh b/Tools/fix_code_style_ubuntu.sh
deleted file mode 100755
index 90ab57b89..000000000
--- a/Tools/fix_code_style_ubuntu.sh
+++ /dev/null
@@ -1,19 +0,0 @@
-#!/bin/sh
-astyle \
- --style=linux \
- --indent=force-tab=8 \
- --indent-cases \
- --indent-preprocessor \
- --break-blocks=all \
- --pad-oper \
- --pad-header \
- --unpad-paren \
- --keep-one-line-blocks \
- --keep-one-line-statements \
- --align-pointer=name \
- --suffix=none \
- --lineend=linux \
- $*
- #--ignore-exclude-errors-x \
- #--exclude=EASTL \
- #--align-reference=name \
diff --git a/Tools/fsm_visualisation.py b/Tools/fsm_visualisation.py
new file mode 100755
index 000000000..c678ef0f4
--- /dev/null
+++ b/Tools/fsm_visualisation.py
@@ -0,0 +1,201 @@
+#!/usr/bin/env python3
+
+"""fsm_visualisation.py: Create dot code and dokuwiki table from a state transition table
+
+convert dot code to png using graphviz:
+
+dot fsm.dot -Tpng -o fsm.png
+"""
+
+import argparse
+import re
+
+__author__ = "Julian Oes"
+
+def get_dot_header():
+
+ return """digraph finite_state_machine {
+ graph [ dpi = 300 ];
+ ratio = 1.5
+ node [shape = circle];"""
+
+def get_dot_footer():
+
+ return """}\n"""
+
+def main():
+
+ # parse input arguments
+ parser = argparse.ArgumentParser(description='Create dot code and dokuwiki table from a state transition table.')
+ parser.add_argument("-i", "--input-file", default=None, help="choose file to parse")
+ parser.add_argument("-d", "--dot-file", default=None, help="choose file for output dot file")
+ parser.add_argument("-t", "--table-file", default=None, help="choose file for output of table")
+ args = parser.parse_args()
+
+ # open source file
+ if args.input_file == None:
+ exit('please specify file')
+ f = open(args.input_file,'r')
+ source = f.read()
+
+ # search for state transition table and extract the table itself
+ # first look for StateTable::Tran
+ # then accept anything including newline until {
+ # but don't accept the definition (without ;)
+ # then extract anything inside the brackets until };
+ match = re.search(r'StateTable::Tran(?:.|\n!;)*\{((?:.|\n)*?)\};', source)
+
+ if not match:
+ exit('no state transition table found')
+
+ table_source = match.group(1)
+
+ # bookkeeping for error checking
+ num_errors_found = 0
+
+ states = []
+ events = []
+
+ # first get all states and events
+ for table_line in table_source.split('\n'):
+
+ match = re.search(r'/\*\s+\w+_STATE_(\w+)\s+\*/', table_line)
+ if match:
+ states.append(str(match.group(1)))
+ # go to next line
+ continue
+
+ if len(states) == 1:
+ match = re.search(r'/\*\s+EVENT_(\w+)\s+\*/', table_line)
+ if match:
+ events.append(str(match.group(1)))
+
+ print('Found %d states and %d events' % (len(states), len(events)))
+
+
+ # keep track of origin state
+ state = None
+
+ # fill dot code in here
+ dot_code = ''
+
+ # create table len(states)xlen(events)
+ transition_table = [[[] for x in range(len(states))] for y in range(len(events))]
+
+ # now fill the transition table and write the dot code
+ for table_line in table_source.split('\n'):
+
+ # get states
+ # from: /* NAV_STATE_NONE */
+ # extract only "NONE"
+ match = re.search(r'/\*\s+\w+_STATE_(\w+)\s+\*/', table_line)
+ if match:
+ state = match.group(1)
+ state_index = states.index(state)
+ # go to next line
+ continue
+
+ # can't advance without proper state
+ if state == None:
+ continue
+
+ # get event and next state
+ # from /* EVENT_READY_REQUESTED */ {ACTION(&Navigator::start_ready), NAV_STATE_READY}
+ # extract "READY_REQUESTED" and "READY" if there is ACTION
+ match_action = re.search(r'/\*\s+EVENT_(\w+)\s+\*/\s+\{ACTION\((?:.|\n)*\w+_STATE_(\w+)', table_line)
+
+ # get event and next state
+ # from /* EVENT_NONE_REQUESTED */ {NO_ACTION, NAV_STATE_NONE},
+ # extract "NONE_REQUESTED" and "NAV_STATE_NONE" if there is NO_ACTION
+ match_no_action = re.search(r'/\*\s+EVENT_(\w+)\s+\*/\s+\{NO_ACTION(?:.|\n)*\w+_STATE_(\w+)', table_line)
+
+ # ignore lines with brackets only
+ if match_action or match_no_action:
+
+ # only write arrows for actions
+ if match_action:
+ event = match_action.group(1)
+ new_state = match_action.group(2)
+ dot_code += ' ' + state + ' -> ' + new_state + '[ label = "' + event + '"];\n'
+
+ elif match_no_action:
+ event = match_no_action.group(1)
+ new_state = match_no_action.group(2)
+
+ # check for state changes without action
+ if state != new_state:
+ print('Error: no action but state change:')
+ print('State: ' + state + ' changed to: ' + new_state)
+ print(table_line)
+ num_errors_found += 1
+
+ # check for wrong events
+ if event not in events:
+ print('Error: unknown event: ' + event)
+ print(table_line)
+ num_errors_found += 1
+
+ # check for wrong new states
+ if new_state not in states:
+ print('Error: unknown new state: ' + new_state)
+ print(table_line)
+ num_errors_found += 1
+
+ # save new state in transition table
+ event_index = events.index(event)
+
+ # bold for action
+ if match_action:
+ transition_table[event_index][state_index] = '**' + new_state + '**'
+ else:
+ transition_table[event_index][state_index] = new_state
+
+
+
+ # assemble dot code
+ dot_code = get_dot_header() + dot_code + get_dot_footer()
+
+ # write or print dot file
+ if args.dot_file:
+ f = open(args.dot_file,'w')
+ f.write(dot_code)
+ print('Wrote dot file')
+ else:
+ print('##########Dot-start##########')
+ print(dot_code)
+ print('##########Dot-end############')
+
+
+ # assemble doku wiki table
+ table_code = '| ^ '
+ # start with header of all states
+ for state in states:
+ table_code += state + ' ^ '
+
+ table_code += '\n'
+
+ # add events and new states
+ for event, row in zip(events, transition_table):
+ table_code += '^ ' + event + ' | '
+ for new_state in row:
+ table_code += new_state + ' | '
+ table_code += '\n'
+
+ # write or print wiki table
+ if args.table_file:
+ f = open(args.table_file,'w')
+ f.write(table_code)
+ print('Wrote table file')
+ else:
+ print('##########Table-start########')
+ print(table_code)
+ print('##########Table-end##########')
+
+ # report obvous errors
+ if num_errors_found:
+ print('Obvious errors found: %d' % num_errors_found)
+ else:
+ print('No obvious errors found')
+
+if __name__ == '__main__':
+ main()
diff --git a/Tools/px4params/README.md b/Tools/px4params/README.md
index a23b44799..50dcd2e29 100644
--- a/Tools/px4params/README.md
+++ b/Tools/px4params/README.md
@@ -1,9 +1 @@
-h1. PX4 Parameters Processor
-
-It's designed to scan PX4 source codes, find declarations of tunable parameters,
-and generate the list in various formats.
-
-Currently supported formats are:
-
-* XML for the parametric UI generator
-* Human-readable description in DokuWiki format
+This folder contains a python library used by px_process_params.py
diff --git a/Tools/px4params/__init__.py b/Tools/px4params/__init__.py
new file mode 100644
index 000000000..3a9f1e2c6
--- /dev/null
+++ b/Tools/px4params/__init__.py
@@ -0,0 +1 @@
+__all__ = ["srcscanner", "srcparser", "xmlout", "dokuwikiout", "dokuwikirpc"] \ No newline at end of file
diff --git a/Tools/px4params/dokuwikiout.py b/Tools/px4params/dokuwikiout.py
index c5cf65ea6..77e0ef53d 100644
--- a/Tools/px4params/dokuwikiout.py
+++ b/Tools/px4params/dokuwikiout.py
@@ -1,62 +1,44 @@
-import output
from xml.sax.saxutils import escape
+import codecs
-class DokuWikiOutput(output.Output):
- def Generate(self, groups):
- pre_text = """<?xml version='1.0'?>
- <methodCall>
- <methodName>wiki.putPage</methodName>
- <params>
- <param>
- <value>
- <string>:firmware:parameters</string>
- </value>
- </param>
- <param>
- <value>
- <string>"""
- result = "====== Parameter Reference ======\nThis list is auto-generated every few minutes and contains the most recent parameter names and default values."
+class DokuWikiTablesOutput():
+ def __init__(self, groups):
+ result = ("====== Parameter Reference ======\n"
+ "<note>**This list is auto-generated from the source code** and contains the most recent parameter documentation.</note>\n"
+ "\n")
for group in groups:
result += "==== %s ====\n\n" % group.GetName()
- result += "|< 100% 20% 20% 10% 10% 10% 30%>|\n"
- result += "^ Name ^ Description ^ Min ^ Max ^ Default ^ Comment ^\n"
+ result += "|< 100% 25% 45% 10% 10% 10% >|\n"
+ result += "^ Name ^ Description ^ Min ^ Max ^ Default ^\n"
+ result += "^ ::: ^ Comment ^^^^\n"
for param in group.GetParams():
code = param.GetFieldValue("code")
name = param.GetFieldValue("short_desc")
- name = name.replace("\n", "")
- result += "| %s | %s " % (code, name)
min_val = param.GetFieldValue("min")
- if min_val is not None:
- result += " | %s " % min_val
- else:
- result += " | "
max_val = param.GetFieldValue("max")
- if max_val is not None:
- result += " | %s " % max_val
- else:
- result += " | "
def_val = param.GetFieldValue("default")
- if def_val is not None:
- result += "| %s " % def_val
- else:
- result += " | "
long_desc = param.GetFieldValue("long_desc")
- if long_desc is not None:
- long_desc = long_desc.replace("\n", "")
- result += "| %s " % long_desc
+
+ if name == code:
+ name = ""
else:
- result += " | "
- result += " |\n"
+ name = name.replace("\n", " ")
+ name = name.replace("|", "%%|%%")
+ name = name.replace("^", "%%^%%")
+
+ result += "| **%s** |" % code
+ result += " %s |" % name
+ result += " %s |" % (min_val or "")
+ result += " %s |" % (max_val or "")
+ result += " %s |" % (def_val or "")
+ result += "\n"
+
+ if long_desc is not None:
+ result += "| ::: | <div>%s</div> ||||\n" % long_desc
+
result += "\n"
- post_text = """</string>
- </value>
- </param>
- <param>
- <value>
- <name>sum</name>
- <string>Updated parameters automagically from code.</string>
- </value>
- </param>
- </params>
- </methodCall>"""
- return pre_text + escape(result) + post_text
+ self.output = result;
+
+ def Save(self, filename):
+ with codecs.open(filename, 'w', 'utf-8') as f:
+ f.write(self.output)
diff --git a/Tools/px4params/dokuwikiout_listings.py b/Tools/px4params/dokuwikiout_listings.py
deleted file mode 100644
index 33f76b415..000000000
--- a/Tools/px4params/dokuwikiout_listings.py
+++ /dev/null
@@ -1,27 +0,0 @@
-import output
-
-class DokuWikiOutput(output.Output):
- def Generate(self, groups):
- result = ""
- for group in groups:
- result += "==== %s ====\n\n" % group.GetName()
- for param in group.GetParams():
- code = param.GetFieldValue("code")
- name = param.GetFieldValue("short_desc")
- if code != name:
- name = "%s (%s)" % (name, code)
- result += "=== %s ===\n\n" % name
- long_desc = param.GetFieldValue("long_desc")
- if long_desc is not None:
- result += "%s\n\n" % long_desc
- min_val = param.GetFieldValue("min")
- if min_val is not None:
- result += "* Minimal value: %s\n" % min_val
- max_val = param.GetFieldValue("max")
- if max_val is not None:
- result += "* Maximal value: %s\n" % max_val
- def_val = param.GetFieldValue("default")
- if def_val is not None:
- result += "* Default value: %s\n" % def_val
- result += "\n"
- return result
diff --git a/Tools/px4params/dokuwikirpc.py b/Tools/px4params/dokuwikirpc.py
new file mode 100644
index 000000000..407d306fd
--- /dev/null
+++ b/Tools/px4params/dokuwikirpc.py
@@ -0,0 +1,16 @@
+try:
+ import xmlrpclib
+except ImportError:
+ import xmlrpc.client as xmlrpclib
+
+# See https://www.dokuwiki.org/devel:xmlrpc for a list of available functions!
+# Usage example:
+# xmlrpc = dokuwikirpc.get_xmlrpc(url, username, password)
+# print(xmlrpc.dokuwiki.getVersion())
+
+def get_xmlrpc(url, username, password):
+ #proto, url = url.split("://")
+ #url = proto + "://" + username + ":" + password + "@" + url + "/lib/exe/xmlrpc.php"
+ url += "/lib/exe/xmlrpc.php?u=" + username + "&p=" + password
+
+ return xmlrpclib.ServerProxy(url)
diff --git a/Tools/px4params/output.py b/Tools/px4params/output.py
deleted file mode 100644
index c09246871..000000000
--- a/Tools/px4params/output.py
+++ /dev/null
@@ -1,5 +0,0 @@
-class Output(object):
- def Save(self, groups, fn):
- data = self.Generate(groups)
- with open(fn, 'w') as f:
- f.write(data)
diff --git a/Tools/px4params/px_process_params.py b/Tools/px4params/px_process_params.py
deleted file mode 100755
index cdf5aba7c..000000000
--- a/Tools/px4params/px_process_params.py
+++ /dev/null
@@ -1,61 +0,0 @@
-#!/usr/bin/env python
-############################################################################
-#
-# Copyright (C) 2013 PX4 Development Team. All rights reserved.
-#
-# Redistribution and use in source and binary forms, with or without
-# modification, are permitted provided that the following conditions
-# are met:
-#
-# 1. Redistributions of source code must retain the above copyright
-# notice, this list of conditions and the following disclaimer.
-# 2. Redistributions in binary form must reproduce the above copyright
-# notice, this list of conditions and the following disclaimer in
-# the documentation and/or other materials provided with the
-# distribution.
-# 3. Neither the name PX4 nor the names of its contributors may be
-# used to endorse or promote products derived from this software
-# without specific prior written permission.
-#
-# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
-# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
-# COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
-# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
-# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
-# OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
-# AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
-# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
-# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
-# POSSIBILITY OF SUCH DAMAGE.
-#
-############################################################################
-
-#
-# PX4 paramers processor (main executable file)
-#
-# It scans src/ subdirectory of the project, collects all parameters
-# definitions, and outputs list of parameters in XML and DokuWiki formats.
-#
-
-import scanner
-import parser
-import xmlout
-import dokuwikiout
-
-# Initialize parser
-prs = parser.Parser()
-
-# Scan directories, and parse the files
-sc = scanner.Scanner()
-sc.ScanDir("../../src", prs)
-output = prs.GetParamGroups()
-
-# Output into XML
-out = xmlout.XMLOutput()
-out.Save(output, "parameters.xml")
-
-# Output into DokuWiki
-out = dokuwikiout.DokuWikiOutput()
-out.Save(output, "parameters.wiki")
diff --git a/Tools/px4params/parser.py b/Tools/px4params/srcparser.py
index 251be672f..0a4d21d26 100644
--- a/Tools/px4params/parser.py
+++ b/Tools/px4params/srcparser.py
@@ -28,8 +28,7 @@ class ParameterGroup(object):
state of the parser.
"""
return sorted(self.params,
- cmp=lambda x, y: cmp(x.GetFieldValue("code"),
- y.GetFieldValue("code")))
+ key=lambda x: x.GetFieldValue("code"))
class Parameter(object):
"""
@@ -45,6 +44,7 @@ class Parameter(object):
"default": 6,
"min": 5,
"max": 4,
+ "unit": 3,
# all others == 0 (sorted alphabetically)
}
@@ -61,9 +61,10 @@ class Parameter(object):
"""
Return list of existing field codes in convenient order
"""
- return sorted(self.fields.keys(),
- cmp=lambda x, y: cmp(self.priority.get(y, 0),
- self.priority.get(x, 0)) or cmp(x, y))
+ keys = self.fields.keys()
+ keys = sorted(keys)
+ keys = sorted(keys, key=lambda x: self.priority.get(x, 0), reverse=True)
+ return keys
def GetFieldValue(self, code):
"""
@@ -71,7 +72,7 @@ class Parameter(object):
"""
return self.fields.get(code)
-class Parser(object):
+class SourceParser(object):
"""
Parses provided data and stores all found parameters internally.
"""
@@ -86,7 +87,7 @@ class Parser(object):
re_is_a_number = re.compile(r'^-?[0-9\.]')
re_remove_dots = re.compile(r'\.+$')
- valid_tags = set(["min", "max", "group"])
+ valid_tags = set(["group", "min", "max", "unit"])
# Order of parameter groups
priority = {
@@ -197,7 +198,7 @@ class Parser(object):
if tag == "group":
group = tags[tag]
elif tag not in self.valid_tags:
- sys.stderr.write("Skipping invalid"
+ sys.stderr.write("Skipping invalid "
"documentation tag: '%s'\n" % tag)
else:
param.SetField(tag, tags[tag])
@@ -214,7 +215,7 @@ class Parser(object):
object. Note that returned object is not a copy. Modifications affect
state of the parser.
"""
- return sorted(self.param_groups.values(),
- cmp=lambda x, y: cmp(self.priority.get(y.GetName(), 0),
- self.priority.get(x.GetName(), 0)) or cmp(x.GetName(),
- y.GetName()))
+ groups = self.param_groups.values()
+ groups = sorted(groups, key=lambda x: x.GetName())
+ groups = sorted(groups, key=lambda x: self.priority.get(x.GetName(), 0), reverse=True)
+ return groups
diff --git a/Tools/px4params/scanner.py b/Tools/px4params/srcscanner.py
index b5a1af47c..d7eca72d7 100644
--- a/Tools/px4params/scanner.py
+++ b/Tools/px4params/srcscanner.py
@@ -1,26 +1,22 @@
import os
import re
+import codecs
-class Scanner(object):
+class SourceScanner(object):
"""
Traverses directory tree, reads all source files, and passes their contents
to the Parser.
"""
- re_file_extension = re.compile(r'\.([^\.]+)$')
-
def ScanDir(self, srcdir, parser):
"""
Scans provided path and passes all found contents to the parser using
parser.Parse method.
"""
- extensions = set(parser.GetSupportedExtensions())
+ extensions = tuple(parser.GetSupportedExtensions())
for dirname, dirnames, filenames in os.walk(srcdir):
for filename in filenames:
- m = self.re_file_extension.search(filename)
- if m:
- ext = m.group(1)
- if ext in extensions:
+ if filename.endswith(extensions):
path = os.path.join(dirname, filename)
self.ScanFile(path, parser)
@@ -29,6 +25,6 @@ class Scanner(object):
Scans provided file and passes its contents to the parser using
parser.Parse method.
"""
- with open(path, 'r') as f:
+ with codecs.open(path, 'r', 'utf-8') as f:
contents = f.read()
parser.Parse(contents)
diff --git a/Tools/px4params/xmlout.py b/Tools/px4params/xmlout.py
index d56802b15..e845cd1b1 100644
--- a/Tools/px4params/xmlout.py
+++ b/Tools/px4params/xmlout.py
@@ -1,8 +1,8 @@
-import output
from xml.dom.minidom import getDOMImplementation
+import codecs
-class XMLOutput(output.Output):
- def Generate(self, groups):
+class XMLOutput():
+ def __init__(self, groups):
impl = getDOMImplementation()
xml_document = impl.createDocument(None, "parameters", None)
xml_parameters = xml_document.documentElement
@@ -19,4 +19,8 @@ class XMLOutput(output.Output):
xml_param.appendChild(xml_field)
xml_value = xml_document.createTextNode(value)
xml_field.appendChild(xml_value)
- return xml_document.toprettyxml(indent=" ", newl="\n", encoding="utf-8")
+ self.xml_document = xml_document
+
+ def Save(self, filename):
+ with codecs.open(filename, 'w', 'utf-8') as f:
+ self.xml_document.writexml(f, indent=" ", addindent=" ", newl="\n")
diff --git a/Tools/px4params/xmlrpc.sh b/Tools/px4params/xmlrpc.sh
deleted file mode 100644
index 36c52ff71..000000000
--- a/Tools/px4params/xmlrpc.sh
+++ /dev/null
@@ -1,5 +0,0 @@
-python px_process_params.py
-
-rm cookies.txt
-curl --cookie cookies.txt --cookie-jar cookies.txt --user-agent Mozilla/4.0 --data "u=$XMLRPCUSER&p=$XMLRPCPASS" https://pixhawk.org/start?do=login
-curl -k --cookie cookies.txt -H "Content-Type: application/xml" -X POST --data-binary @parameters.wiki "https://pixhawk.org/lib/exe/xmlrpc.php"
diff --git a/Tools/px_generate_xml.sh b/Tools/px_generate_xml.sh
new file mode 100644
index 000000000..65f0c95da
--- /dev/null
+++ b/Tools/px_generate_xml.sh
@@ -0,0 +1,2 @@
+#!/bin/sh
+python px_process_params.py --xml
diff --git a/Tools/px_process_params.py b/Tools/px_process_params.py
new file mode 100644
index 000000000..12128a997
--- /dev/null
+++ b/Tools/px_process_params.py
@@ -0,0 +1,140 @@
+#!/usr/bin/env python
+############################################################################
+#
+# Copyright (C) 2013-2014 PX4 Development Team. All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+#
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in
+# the documentation and/or other materials provided with the
+# distribution.
+# 3. Neither the name PX4 nor the names of its contributors may be
+# used to endorse or promote products derived from this software
+# without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+# COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+# OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+# AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+# POSSIBILITY OF SUCH DAMAGE.
+#
+############################################################################
+
+#
+# PX4 paramers processor (main executable file)
+#
+# This tool scans the PX4 source code for declarations of tunable parameters
+# and outputs the list in various formats.
+#
+# Currently supported formats are:
+# * XML for the parametric UI generator
+# * Human-readable description in DokuWiki page format
+#
+# This tool also allows to automatically upload the human-readable version
+# to the DokuWiki installation via XML-RPC.
+#
+
+from __future__ import print_function
+import sys
+import os
+import argparse
+from px4params import srcscanner, srcparser, xmlout, dokuwikiout, dokuwikirpc
+
+def main():
+ # Parse command line arguments
+ parser = argparse.ArgumentParser(description="Process parameter documentation.")
+ parser.add_argument("-s", "--src-path",
+ default="../src",
+ metavar="PATH",
+ help="path to source files to scan for parameters")
+ parser.add_argument("-x", "--xml",
+ nargs='?',
+ const="parameters.xml",
+ metavar="FILENAME",
+ help="Create XML file"
+ " (default FILENAME: parameters.xml)")
+ parser.add_argument("-w", "--wiki",
+ nargs='?',
+ const="parameters.wiki",
+ metavar="FILENAME",
+ help="Create DokuWiki file"
+ " (default FILENAME: parameters.wiki)")
+ parser.add_argument("-u", "--wiki-update",
+ nargs='?',
+ const="firmware:parameters",
+ metavar="PAGENAME",
+ help="Update DokuWiki page"
+ " (default PAGENAME: firmware:parameters)")
+ parser.add_argument("--wiki-url",
+ default="https://pixhawk.org",
+ metavar="URL",
+ help="DokuWiki URL"
+ " (default: https://pixhawk.org)")
+ parser.add_argument("--wiki-user",
+ default=os.environ.get('XMLRPCUSER', None),
+ metavar="USERNAME",
+ help="DokuWiki XML-RPC user name"
+ " (default: $XMLRPCUSER environment variable)")
+ parser.add_argument("--wiki-pass",
+ default=os.environ.get('XMLRPCPASS', None),
+ metavar="PASSWORD",
+ help="DokuWiki XML-RPC user password"
+ " (default: $XMLRPCUSER environment variable)")
+ parser.add_argument("--wiki-summary",
+ metavar="SUMMARY",
+ default="Automagically updated parameter documentation from code.",
+ help="DokuWiki page edit summary")
+ args = parser.parse_args()
+
+ # Check for valid command
+ if not (args.xml or args.wiki or args.wiki_update):
+ print("Error: You need to specify at least one output method!\n")
+ parser.print_usage()
+ sys.exit(1)
+
+ # Initialize source scanner and parser
+ scanner = srcscanner.SourceScanner()
+ parser = srcparser.SourceParser()
+
+ # Scan directories, and parse the files
+ print("Scanning source path " + args.src_path)
+ scanner.ScanDir(args.src_path, parser)
+ param_groups = parser.GetParamGroups()
+
+ # Output to XML file
+ if args.xml:
+ print("Creating XML file " + args.xml)
+ out = xmlout.XMLOutput(param_groups)
+ out.Save(args.xml)
+
+ # Output to DokuWiki tables
+ if args.wiki or args.wiki_update:
+ out = dokuwikiout.DokuWikiTablesOutput(param_groups)
+ if args.wiki:
+ print("Creating wiki file " + args.wiki)
+ out.Save(args.wiki)
+ if args.wiki_update:
+ if args.wiki_user and args.wiki_pass:
+ print("Updating wiki page " + args.wiki_update)
+ xmlrpc = dokuwikirpc.get_xmlrpc(args.wiki_url, args.wiki_user, args.wiki_pass)
+ xmlrpc.wiki.putPage(args.wiki_update, out.output, {'sum': args.wiki_summary})
+ else:
+ print("Error: You need to specify DokuWiki XML-RPC username and password!")
+
+ print("All done!")
+
+
+if __name__ == "__main__":
+ main()
diff --git a/Tools/px_romfs_pruner.py b/Tools/px_romfs_pruner.py
new file mode 100644
index 000000000..fcc40b09e
--- /dev/null
+++ b/Tools/px_romfs_pruner.py
@@ -0,0 +1,84 @@
+#!/usr/bin/env python
+############################################################################
+#
+# Copyright (C) 2014 PX4 Development Team. All rights reserved.
+# Author: Julian Oes <joes@student.ethz.ch>
+
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+#
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in
+# the documentation and/or other materials provided with the
+# distribution.
+# 3. Neither the name PX4 nor the names of its contributors may be
+# used to endorse or promote products derived from this software
+# without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+# COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+# OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+# AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+# POSSIBILITY OF SUCH DAMAGE.
+#
+############################################################################
+
+
+"""
+px_romfs_pruner.py:
+Delete all comments and newlines before ROMFS is converted to an image
+"""
+
+from __future__ import print_function
+import argparse
+import os
+
+
+def main():
+
+ # Parse commandline arguments
+ parser = argparse.ArgumentParser(description="ROMFS pruner.")
+ parser.add_argument('--folder', action="store", help="ROMFS scratch folder.")
+ args = parser.parse_args()
+
+ print("Pruning ROMFS files.")
+
+ # go through
+ for (root, dirs, files) in os.walk(args.folder):
+ for file in files:
+ # only prune text files
+ if ".zip" in file or ".bin" in file or ".swp" in file:
+ continue
+
+ file_path = os.path.join(root, file)
+
+ # read file line by line
+ pruned_content = ""
+ with open(file_path, "r") as f:
+ for line in f:
+
+ # handle mixer files differently than startup files
+ if file_path.endswith(".mix"):
+ if line.startswith(("Z:", "M:", "R: ", "O:", "S:")):
+ pruned_content += line
+ else:
+ if not line.isspace() and not line.strip().startswith("#"):
+ pruned_content += line
+
+ # overwrite old scratch file
+ with open(file_path, "w") as f:
+ f.write(pruned_content)
+
+
+if __name__ == '__main__':
+ main()
diff --git a/Tools/px_update_wiki.sh b/Tools/px_update_wiki.sh
new file mode 100644
index 000000000..d66bb9e10
--- /dev/null
+++ b/Tools/px_update_wiki.sh
@@ -0,0 +1,2 @@
+# Remember to set the XMLRPCUSER and XMLRPCPASS environment variables
+python px_process_params.py --wiki-update
diff --git a/Tools/px_uploader.py b/Tools/px_uploader.py
index e4a8b3c05..985e6ffd9 100755
--- a/Tools/px_uploader.py
+++ b/Tools/px_uploader.py
@@ -389,18 +389,22 @@ class uploader(object):
self.otp_pid = self.otp[12:8:-1]
self.otp_coa = self.otp[32:160]
# show user:
- print("type: " + self.otp_id.decode('Latin-1'))
- print("idtype: " + binascii.b2a_qp(self.otp_idtype).decode('Latin-1'))
- print("vid: " + binascii.hexlify(self.otp_vid).decode('Latin-1'))
- print("pid: "+ binascii.hexlify(self.otp_pid).decode('Latin-1'))
- print("coa: "+ binascii.b2a_base64(self.otp_coa).decode('Latin-1'))
- print("sn: ", end='')
- for byte in range(0,12,4):
- x = self.__getSN(byte)
- x = x[::-1] # reverse the bytes
- self.sn = self.sn + x
- print(binascii.hexlify(x).decode('Latin-1'), end='') # show user
- print('')
+ try:
+ print("type: " + self.otp_id.decode('Latin-1'))
+ print("idtype: " + binascii.b2a_qp(self.otp_idtype).decode('Latin-1'))
+ print("vid: " + binascii.hexlify(self.otp_vid).decode('Latin-1'))
+ print("pid: "+ binascii.hexlify(self.otp_pid).decode('Latin-1'))
+ print("coa: "+ binascii.b2a_base64(self.otp_coa).decode('Latin-1'))
+ print("sn: ", end='')
+ for byte in range(0,12,4):
+ x = self.__getSN(byte)
+ x = x[::-1] # reverse the bytes
+ self.sn = self.sn + x
+ print(binascii.hexlify(x).decode('Latin-1'), end='') # show user
+ print('')
+ except Exception:
+ # ignore bad character encodings
+ pass
print("erase...")
self.__erase()
diff --git a/Tools/tests-host/.gitignore b/Tools/tests-host/.gitignore
index 61e091551..87b314c61 100644
--- a/Tools/tests-host/.gitignore
+++ b/Tools/tests-host/.gitignore
@@ -1,2 +1,4 @@
./obj/*
mixer_test
+sbus2_test
+autodeclination_test
diff --git a/Tools/tests-host/Makefile b/Tools/tests-host/Makefile
index 7ab1454f0..f0737ef88 100644
--- a/Tools/tests-host/Makefile
+++ b/Tools/tests-host/Makefile
@@ -1,47 +1,39 @@
CC=g++
-CFLAGS=-I. -I../../src/modules -I ../../src/include -I../../src/drivers -I../../src -D__EXPORT="" -Dnullptr="0"
+CFLAGS=-I. -I../../src/modules -I ../../src/include -I../../src/drivers \
+ -I../../src -I../../src/lib -D__EXPORT="" -Dnullptr="0" -lm
-ODIR=obj
-LDIR =../lib
+all: mixer_test sbus2_test autodeclination_test
-LIBS=-lm
+MIXER_FILES=../../src/systemcmds/tests/test_mixer.cpp \
+ ../../src/systemcmds/tests/test_conv.cpp \
+ ../../src/modules/systemlib/mixer/mixer_simple.cpp \
+ ../../src/modules/systemlib/mixer/mixer_multirotor.cpp \
+ ../../src/modules/systemlib/mixer/mixer.cpp \
+ ../../src/modules/systemlib/mixer/mixer_group.cpp \
+ ../../src/modules/systemlib/mixer/mixer_load.c \
+ ../../src/modules/systemlib/pwm_limit/pwm_limit.c \
+ hrt.cpp \
+ mixer_test.cpp
-#_DEPS = test.h
-#DEPS = $(patsubst %,$(IDIR)/%,$(_DEPS))
+SBUS2_FILES=../../src/modules/px4iofirmware/sbus.c \
+ hrt.cpp \
+ sbus2_test.cpp
-_OBJ = mixer_test.o test_mixer.o mixer_simple.o mixer_multirotor.o \
- mixer.o mixer_group.o mixer_load.o test_conv.o pwm_limit.o hrt.o
-OBJ = $(patsubst %,$(ODIR)/%,$(_OBJ))
+AUTODECLINATION_FILES= ../../src/lib/geo/geo_mag_declination.c \
+ hrt.cpp \
+ autodeclination_test.cpp
-#$(DEPS)
-$(ODIR)/%.o: %.cpp
- mkdir -p obj
- $(CC) -c -o $@ $< $(CFLAGS)
+mixer_test: $(MIXER_FILES)
+ $(CC) -o mixer_test $(MIXER_FILES) $(CFLAGS)
-$(ODIR)/%.o: ../../src/systemcmds/tests/%.cpp
- $(CC) -c -o $@ $< $(CFLAGS)
+sbus2_test: $(SBUS2_FILES)
+ $(CC) -o sbus2_test $(SBUS2_FILES) $(CFLAGS)
-$(ODIR)/%.o: ../../src/modules/systemlib/%.cpp
- $(CC) -c -o $@ $< $(CFLAGS)
-
-$(ODIR)/%.o: ../../src/modules/systemlib/mixer/%.cpp
- $(CC) -c -o $@ $< $(CFLAGS)
-
-$(ODIR)/%.o: ../../src/modules/systemlib/pwm_limit/%.cpp
- $(CC) -c -o $@ $< $(CFLAGS)
-
-$(ODIR)/%.o: ../../src/modules/systemlib/pwm_limit/%.c
- $(CC) -c -o $@ $< $(CFLAGS)
-
-$(ODIR)/%.o: ../../src/modules/systemlib/mixer/%.c
- $(CC) -c -o $@ $< $(CFLAGS)
-
-#
-mixer_test: $(OBJ)
- g++ -o $@ $^ $(CFLAGS) $(LIBS)
+autodeclination_test: $(SBUS2_FILES)
+ $(CC) -o autodeclination_test $(AUTODECLINATION_FILES) $(CFLAGS)
.PHONY: clean
clean:
- rm -f $(ODIR)/*.o *~ core $(INCDIR)/*~ \ No newline at end of file
+ rm -f $(ODIR)/*.o *~ core $(INCDIR)/*~ mixer_test sbus2_test autodeclination_test \ No newline at end of file
diff --git a/Tools/tests-host/autodeclination_test.cpp b/Tools/tests-host/autodeclination_test.cpp
new file mode 100644
index 000000000..93bc340bb
--- /dev/null
+++ b/Tools/tests-host/autodeclination_test.cpp
@@ -0,0 +1,28 @@
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <systemlib/mixer/mixer.h>
+#include <systemlib/err.h>
+#include <drivers/drv_hrt.h>
+#include <px4iofirmware/px4io.h>
+#include "../../src/systemcmds/tests/tests.h"
+#include <geo/geo.h>
+
+int main(int argc, char *argv[]) {
+ warnx("autodeclination test started");
+
+ if (argc < 3)
+ errx(1, "Need lat/lon!");
+
+ char* p_end;
+
+ float lat = strtod(argv[1], &p_end);
+ float lon = strtod(argv[2], &p_end);
+
+ float declination = get_mag_declination(lat, lon);
+
+ printf("lat: %f lon: %f, dec: %f\n", lat, lon, declination);
+
+}
diff --git a/Tools/tests-host/board_config.h b/Tools/tests-host/board_config.h
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/Tools/tests-host/board_config.h
diff --git a/Tools/tests-host/debug.h b/Tools/tests-host/debug.h
new file mode 100644
index 000000000..9824d13fc
--- /dev/null
+++ b/Tools/tests-host/debug.h
@@ -0,0 +1,5 @@
+
+#pragma once
+
+#include <systemlib/err.h>
+#define lowsyslog warnx \ No newline at end of file
diff --git a/Tools/tests-host/mixer_test.cpp b/Tools/tests-host/mixer_test.cpp
index e311617f9..06499afd0 100644
--- a/Tools/tests-host/mixer_test.cpp
+++ b/Tools/tests-host/mixer_test.cpp
@@ -11,4 +11,4 @@ int main(int argc, char *argv[]) {
test_mixer(3, args);
test_conv(1, args);
-} \ No newline at end of file
+}
diff --git a/Tools/tests-host/run_tests.sh b/Tools/tests-host/run_tests.sh
new file mode 100755
index 000000000..ff5ee509a
--- /dev/null
+++ b/Tools/tests-host/run_tests.sh
@@ -0,0 +1,6 @@
+#!/bin/sh
+
+make clean
+make all
+./mixer_test
+./sbus2_test ../../../../data/sbus2/sbus2_r7008SB_gps_baro_tx_off.txt \ No newline at end of file
diff --git a/Tools/tests-host/sbus2_test.cpp b/Tools/tests-host/sbus2_test.cpp
new file mode 100644
index 000000000..d8fcb695d
--- /dev/null
+++ b/Tools/tests-host/sbus2_test.cpp
@@ -0,0 +1,75 @@
+
+#include <stdio.h>
+#include <unistd.h>
+#include <string.h>
+#include <systemlib/mixer/mixer.h>
+#include <systemlib/err.h>
+#include <drivers/drv_hrt.h>
+#include <px4iofirmware/px4io.h>
+#include "../../src/systemcmds/tests/tests.h"
+
+int main(int argc, char *argv[]) {
+ warnx("SBUS2 test started");
+
+ if (argc < 2)
+ errx(1, "Need a filename for the input file");
+
+ warnx("loading data from: %s", argv[1]);
+
+ FILE *fp;
+
+ fp = fopen(argv[1],"rt");
+
+ if (!fp)
+ errx(1, "failed opening file");
+
+ float f;
+ unsigned x;
+ int ret;
+
+ // Trash the first 20 lines
+ for (unsigned i = 0; i < 20; i++) {
+ (void)fscanf(fp, "%f,%x,,", &f, &x);
+ }
+
+ // Init the parser
+ uint8_t frame[30];
+ unsigned partial_frame_count = 0;
+ uint16_t rc_values[18];
+ uint16_t num_values;
+ bool sbus_failsafe;
+ bool sbus_frame_drop;
+ uint16_t max_channels = sizeof(rc_values) / sizeof(rc_values[0]);
+
+ float last_time = 0;
+
+ while (EOF != (ret = fscanf(fp, "%f,%x,,", &f, &x))) {
+ if (((f - last_time) * 1000 * 1000) > 3000) {
+ partial_frame_count = 0;
+ warnx("FRAME RESET\n\n");
+ }
+
+ frame[partial_frame_count] = x;
+ partial_frame_count++;
+
+ //warnx("%f: 0x%02x, first: 0x%02x, last: 0x%02x, pcount: %u", (double)f, x, frame[0], frame[24], partial_frame_count);
+
+ if (partial_frame_count == sizeof(frame))
+ partial_frame_count = 0;
+
+ last_time = f;
+
+ // Pipe the data into the parser
+ hrt_abstime now = hrt_absolute_time();
+
+ //if (partial_frame_count % 25 == 0)
+ //sbus_parse(now, frame, &partial_frame_count, rc_values, &num_values, &sbus_failsafe, &sbus_frame_drop, max_channels);
+ }
+
+ if (ret == EOF) {
+ warnx("Test finished, reached end of file");
+ } else {
+ warnx("Test aborted, errno: %d", ret);
+ }
+
+}