diff --git a/Makefile.dep b/Makefile.dep index 7455a419b7..64207bf61b 100644 --- a/Makefile.dep +++ b/Makefile.dep @@ -85,3 +85,7 @@ endif ifneq (,$(filter pipe,$(USEMODULE))) USEMODULE += lib endif + +ifneq (,$(filter libfixmath-unittests,$(USEMODULE))) + USEPKG += libfixmath +endif diff --git a/pkg/libfixmath/.gitignore b/pkg/libfixmath/.gitignore new file mode 100644 index 0000000000..5e660dc18e --- /dev/null +++ b/pkg/libfixmath/.gitignore @@ -0,0 +1 @@ +/checkout diff --git a/pkg/libfixmath/Makefile b/pkg/libfixmath/Makefile new file mode 100644 index 0000000000..1f0b2d79b3 --- /dev/null +++ b/pkg/libfixmath/Makefile @@ -0,0 +1,65 @@ +PKG_NAME := libfixmath +PKG_VERSION := 91 +PKG_BRANCH := trunk +PKG_URL := http://libfixmath.googlecode.com/svn/$(PKG_BRANCH)/ + +CHECKOUT_FOLDER := $(CURDIR)/checkout/$(PKG_BRANCH)-r$(PKG_VERSION) + +.PHONY: all clean distclean + +all: $(BINDIR)$(PKG_NAME).a + +all-unittests: $(BINDIR)$(PKG_NAME)-unittests.a + +ifneq (,$(filter libfixmath-unittests,$(USEMODULE))) + all: all-unittests +endif + +$(BINDIR)$(PKG_NAME).a: $(BINDIR)$(PKG_NAME)-src/Makefile $(BINDIR)$(PKG_NAME)-headers/fix16.h + "$(MAKE)" -C $( $(@D)/fix16_unittests.inc + +$(CHECKOUT_FOLDER)/svn_info.xml: + @mkdir -p $(@D) + svn checkout -r $(PKG_VERSION) $(PKG_URL) $(@D) + @svn info --xml $(@D) > $@ + +clean:: + rm -rf $(BINDIR)$(PKG_NAME)-src/ $(BINDIR)$(PKG_NAME)-headers/ + +distclean:: clean + rm -rf $(CHECKOUT_FOLDER) + +Makefile.include: + @true diff --git a/pkg/libfixmath/Makefile.include b/pkg/libfixmath/Makefile.include new file mode 100644 index 0000000000..147ba409f3 --- /dev/null +++ b/pkg/libfixmath/Makefile.include @@ -0,0 +1,4 @@ +# The static cache is huge, disable it. +CFLAGS += -DFIXMATH_NO_CACHE + +INCLUDES += -I$(BINDIR)libfixmath-headers/ diff --git a/pkg/libfixmath/Makefile.template b/pkg/libfixmath/Makefile.template new file mode 100644 index 0000000000..c875b98543 --- /dev/null +++ b/pkg/libfixmath/Makefile.template @@ -0,0 +1,3 @@ +MODULE = libfixmath + +include $(RIOTBASE)/Makefile.base diff --git a/pkg/libfixmath/Makefile.template-unittests b/pkg/libfixmath/Makefile.template-unittests new file mode 100644 index 0000000000..245ffb9416 --- /dev/null +++ b/pkg/libfixmath/Makefile.template-unittests @@ -0,0 +1,3 @@ +MODULE = libfixmath-unittests + +include $(RIOTBASE)/Makefile.base diff --git a/tests/libfixmath/Makefile b/tests/libfixmath/Makefile new file mode 100644 index 0000000000..f589677d67 --- /dev/null +++ b/tests/libfixmath/Makefile @@ -0,0 +1,6 @@ +APPLICATION = libfixmath +include ../Makefile.tests_common + +USEPKG += libfixmath + +include $(RIOTBASE)/Makefile.include diff --git a/tests/libfixmath/do-test.py b/tests/libfixmath/do-test.py new file mode 100755 index 0000000000..70393f32c7 --- /dev/null +++ b/tests/libfixmath/do-test.py @@ -0,0 +1,96 @@ +#!/usr/bin/env python3 + +import math +import operator +import sys + + +def rem(a, b): + ret = a % b + if ret < 0 and a > 0 and b < 0 or \ + ret > 0 and a < 0 and b > 0: + ret -= b + return ret + + +FUNS = { + 'add': operator.add, + 'sub': operator.sub, + 'mul': operator.mul, + 'div': operator.truediv, + 'mod': rem, + + 'sadd': operator.add, + 'ssub': operator.sub, + 'smul': operator.mul, + 'sdiv': operator.truediv, + + 'min': min, + 'max': max, + + 'abs': abs, + 'sqrt': math.sqrt, + 'sq': lambda x: x * x, + + 'sin': math.sin, + 'cos': math.cos, + 'tan': math.tan, + 'asin': math.asin, + 'acos': math.acos, + 'atan': math.atan, + + 'exp': math.exp, + 'log': math.log, + 'log2': math.log2, + 'slog2': math.log2, +} + +ABS_ERROR_LIMIT = 0.011 + +def main(): + total = 0 + errors = 0 + + print('Calculation: abs result != exp result, abs error > limit') + + started = False + for line in sys.stdin: + line = line.strip() + if not started: + if line == 'Unary.': + print(line) + started = True + continue + elif line == 'Binary.': + print(line) + continue + elif line == 'Done.': + print(line) + break + + total += 1 + try: + res_locals = {} + res_locals['input'], res_locals['expected'] = map(str.strip, line.split('=')) + exec('result = {}'.format(res_locals['input']), FUNS, res_locals) + + abs_error = abs(res_locals['result'] - float(res_locals['expected'])) + res_locals['result'] = '{:.4f}'.format(res_locals['result']) + if abs_error > ABS_ERROR_LIMIT: + print('{}: {} != {}, {:.4f} > {}'.format(res_locals['input'], res_locals['result'], res_locals['expected'], + abs_error, ABS_ERROR_LIMIT)) + errors += 1 + except: + errors += 1 + print('ERROR {}'.format(line)) + + print('{} calculations passed.'.format(total - errors)) + if errors: + print('{} calculations had errors.'.format(errors)) + return 1 + else: + return 0 + + +if __name__ == '__main__': + sys.exit(main()) diff --git a/tests/libfixmath/main.c b/tests/libfixmath/main.c new file mode 100644 index 0000000000..dd6763e073 --- /dev/null +++ b/tests/libfixmath/main.c @@ -0,0 +1,195 @@ +/* + * Copyright (C) 2014 René Kijewski + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +/** + * @ingroup tests + * @{ + * + * @file + * @brief Print some calculations for libfixmath + * + * @author René Kijewski + * + * @} + */ + +#include + +#include "fix16.h" + +#ifndef M_PI +# define M_PI 3.14159265359 +#endif + +static void binary_ops(void) +{ + static const struct { + const char *op; + fix16_t (*fun)(fix16_t, fix16_t); + } ops[] = { + { "add", fix16_add }, + { "sub", fix16_sub }, + { "mul", fix16_mul }, + { "div", fix16_div }, + { "mod", fix16_mod }, + + { "sadd", fix16_sadd }, + { "ssub", fix16_ssub }, + { "smul", fix16_smul }, + { "sdiv", fix16_sdiv }, + + { "min", fix16_min }, + { "max", fix16_max }, + }; + + for (fix16_t a = fix16_from_dbl(-5.0); a < fix16_from_dbl(5.0); a += fix16_from_dbl(0.25)) { + for (fix16_t b = fix16_from_dbl(-5.0); b < fix16_from_dbl(5.0); b += fix16_from_dbl(0.25)) { + if (b == 0) { + continue; + } + + + for (unsigned o = 0; o < sizeof(ops) / sizeof(*ops); ++o) { + fix16_t c = ops[o].fun(a, b); + + char buf[3][14]; + fix16_to_str(a, buf[0], 12); + fix16_to_str(b, buf[1], 12); + fix16_to_str(c, buf[2], 12); + + printf("%s(%s, %s) = %s\n", ops[o].op, buf[0], buf[1], buf[2]); + } + } + } +} + +static void unary_ops(void) +{ + /* range [-10 : +10 : 0.25] */ + { + static const struct { + const char *op; + fix16_t (*fun)(fix16_t); + } ops[] = { + { "abs", fix16_abs }, + { "sq", fix16_sq }, + + { "atan", fix16_atan }, + + { "exp", fix16_exp }, + }; + + for (fix16_t input = fix16_from_dbl(-10.0); input < fix16_from_dbl(+10.0); input += fix16_from_dbl(0.25)) { + for (unsigned o = 0; o < sizeof(ops) / sizeof(*ops); ++o) { + fix16_t result = ops[o].fun(input); + + char buf[2][14]; + fix16_to_str(input, buf[0], 12); + fix16_to_str(result, buf[1], 12); + + printf("%s(%s) = %s\n", ops[o].op, buf[0], buf[1]); + } + } + } + + /* range [-pi/2 : +pi/2 : 0.05] */ + { + static const struct { + const char *op; + fix16_t (*fun)(fix16_t); + } ops[] = { + /* { "sin_parabola", fix16_sin_parabola }, FIXME: what is sin_parabola? */ + { "sin", fix16_sin }, + { "cos", fix16_cos }, + { "tan", fix16_tan }, + }; + + for (fix16_t input = fix16_from_dbl(-M_PI/2); input < fix16_from_dbl(+M_PI/2); input += fix16_from_dbl(0.05)) { + for (unsigned o = 0; o < sizeof(ops) / sizeof(*ops); ++o) { + fix16_t result = ops[o].fun(input); + + char buf[2][14]; + fix16_to_str(input, buf[0], 12); + fix16_to_str(result, buf[1], 12); + + printf("%s(%s) = %s\n", ops[o].op, buf[0], buf[1]); + } + } + } + + /* range [-1 : +1 : 0.05] */ + { + static const struct { + const char *op; + fix16_t (*fun)(fix16_t); + } ops[] = { + { "asin", fix16_asin }, + { "acos", fix16_acos }, + }; + + for (fix16_t input = fix16_from_dbl(-1.0); input < fix16_from_dbl(+1.0); input += fix16_from_dbl(0.05)) { + for (unsigned o = 0; o < sizeof(ops) / sizeof(*ops); ++o) { + fix16_t result = ops[o].fun(input); + + char buf[2][14]; + fix16_to_str(input, buf[0], 12); + fix16_to_str(result, buf[1], 12); + + printf("%s(%s) = %s\n", ops[o].op, buf[0], buf[1]); + } + } + } + + /* range [+0.05 : +10 : 0.25] */ + { + static const struct { + const char *op; + fix16_t (*fun)(fix16_t); + } ops[] = { + { "sqrt", fix16_sqrt }, + + { "log", fix16_log }, + { "log2", fix16_log2 }, + { "slog2", fix16_slog2 }, + }; + + for (fix16_t input = fix16_from_dbl(0.05); input < fix16_from_dbl(+10.0); input += fix16_from_dbl(0.25)) { + for (unsigned o = 0; o < sizeof(ops) / sizeof(*ops); ++o) { + fix16_t result = ops[o].fun(input); + + char buf[2][14]; + fix16_to_str(input, buf[0], 12); + fix16_to_str(result, buf[1], 12); + + printf("%s(%s) = %s\n", ops[o].op, buf[0], buf[1]); + } + } + } +} + +int main(void) +{ + puts("Unary."); + unary_ops(); + + puts("Binary."); + binary_ops(); + + puts("Done."); + return 0; +} diff --git a/tests/libfixmath_unittests/Makefile b/tests/libfixmath_unittests/Makefile new file mode 100644 index 0000000000..162d11afa1 --- /dev/null +++ b/tests/libfixmath_unittests/Makefile @@ -0,0 +1,12 @@ +APPLICATION = libfixmath_unittests +include ../Makefile.tests_common + +# The MSP boards don't feature round(), exp(), and log(), which are used in the unittests +BOARD_INSUFFICIENT_RAM := chronos msb-430 msb-430h telosb wsn430-v1_3b wsn430-v1_4 z1 + +# Insufficient RAM / ROM +BOARD_INSUFFICIENT_RAM += redbee-econotag stm32f0discovery + +USEMODULE += libfixmath-unittests + +include $(RIOTBASE)/Makefile.include diff --git a/tests/libfixmath_unittests/main.c b/tests/libfixmath_unittests/main.c new file mode 100644 index 0000000000..a5f040a6dc --- /dev/null +++ b/tests/libfixmath_unittests/main.c @@ -0,0 +1,36 @@ +/* + * Copyright (C) 2014 René Kijewski + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +/** + * @ingroup tests + * @{ + * + * @file + * @brief execute libfixmath's unittests in RIOT + * + * @author René Kijewski + * + * @} + */ + +int main(void) +{ +#include "fix16_unittests.inc" + + return 0; +}