From d7b4f1a5ba5024f28da6d4eee9efafbe3417480c Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Ren=C3=A9=20Kijewski?= <rene.kijewski@fu-berlin.de>
Date: Tue, 22 Jul 2014 20:24:45 +0200
Subject: [PATCH] pkg: add USEPKG module "libfixmath"

This PR adds the USEPKG module "libfixmath".
It integrates https://code.google.com/p/libfixmath/ into RIOT, cmp. #1457.
---
 Makefile.dep                               |   4 +
 pkg/libfixmath/.gitignore                  |   1 +
 pkg/libfixmath/Makefile                    |  65 +++++++
 pkg/libfixmath/Makefile.include            |   4 +
 pkg/libfixmath/Makefile.template           |   3 +
 pkg/libfixmath/Makefile.template-unittests |   3 +
 tests/libfixmath/Makefile                  |   6 +
 tests/libfixmath/do-test.py                |  96 ++++++++++
 tests/libfixmath/main.c                    | 195 +++++++++++++++++++++
 tests/libfixmath_unittests/Makefile        |  12 ++
 tests/libfixmath_unittests/main.c          |  36 ++++
 11 files changed, 425 insertions(+)
 create mode 100644 pkg/libfixmath/.gitignore
 create mode 100644 pkg/libfixmath/Makefile
 create mode 100644 pkg/libfixmath/Makefile.include
 create mode 100644 pkg/libfixmath/Makefile.template
 create mode 100644 pkg/libfixmath/Makefile.template-unittests
 create mode 100644 tests/libfixmath/Makefile
 create mode 100755 tests/libfixmath/do-test.py
 create mode 100644 tests/libfixmath/main.c
 create mode 100644 tests/libfixmath_unittests/Makefile
 create mode 100644 tests/libfixmath_unittests/main.c

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)
+
+$(BINDIR)$(PKG_NAME)-unittests.a: $(BINDIR)$(PKG_NAME)-unittests-src/Makefile $(BINDIR)$(PKG_NAME)-headers/fix16.h
+	"$(MAKE)" -C $(<D)
+
+$(BINDIR)$(PKG_NAME)-src/Makefile: $(CHECKOUT_FOLDER)/svn_info.xml
+	@rm -rf $(@D)
+	@mkdir -p $(@D)
+	$(AD)cp $(CURDIR)/Makefile.template $@
+	$(AD)cp $(CHECKOUT_FOLDER)/libfixmath/*.[ch] $(@D)
+	$(AD)rm -f $(BINDIR)$(PKG_NAME)-src/fix16.h
+
+	cd $(@D) && sed -i -e "s/1 <</(uint32_t) 1 <</g" uint32.c
+
+$(BINDIR)$(PKG_NAME)-unittests-src/Makefile:
+	@rm -rf $(@D)
+	@mkdir -p $(@D)
+	$(AD)cp $(CURDIR)/Makefile.template-unittests $@
+	$(AD)cp $(CHECKOUT_FOLDER)/unittests/*.[ch] $(@D)
+
+	$(AD)cd $(@D) && for C_FILE in *.[ch]; do \
+		sed -e "s/int main()/int $$(basename $${C_FILE} .c)(void)/" \
+		    -e '/fflush/d' \
+		    -e 's/fprintf(std[^,]*,/printf(/' \
+		    -i $${C_FILE}; \
+	done
+
+$(BINDIR)$(PKG_NAME)-headers/fix16.h: $(CHECKOUT_FOLDER)/svn_info.xml
+	@rm -rf $(@D)
+	@mkdir -p $(@D)
+	$(AD)cp $(CHECKOUT_FOLDER)/libfixmath/fix16.h $(@D)
+	@echo $(patsubst %,'extern int %(void);',$(shell basename -a -s .c $(CHECKOUT_FOLDER)/unittests/*.c)) \
+	      $(patsubst %,'%();',$(shell basename -a -s .c $(CHECKOUT_FOLDER)/unittests/*.c)) | sed -e 's/;\s*/;\n/g' > $(@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  <rene.kijewski@fu-berlin.de>
+ *
+ * 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 <rene.kijewski@fu-berlin.de>
+ *
+ * @}
+ */
+
+#include <stdio.h>
+
+#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  <rene.kijewski@fu-berlin.de>
+ *
+ * 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 <rene.kijewski@fu-berlin.de>
+ *
+ * @}
+ */
+
+int main(void)
+{
+#include "fix16_unittests.inc"
+
+    return 0;
+}
-- 
GitLab