From f5e5a6aed48d8c2dfb3c10210f66472b0a3084c1 Mon Sep 17 00:00:00 2001
From: "Christian W. Zuckschwerdt" <christian@zuckschwerdt.org>
Date: Tue, 18 Feb 2020 10:22:17 +0100
Subject: [PATCH] Add pulse-eval example

---
 tests/pulse-eval.c | 348 +++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 348 insertions(+)
 create mode 100644 tests/pulse-eval.c

diff --git a/tests/pulse-eval.c b/tests/pulse-eval.c
new file mode 100644
index 00000000..b6dc1c39
--- /dev/null
+++ b/tests/pulse-eval.c
@@ -0,0 +1,348 @@
+/** @file
+    Pulse Evaluation.
+
+    Functional and speed test for various pulse functions.
+
+    Copyright (C) 2018 by Christian Zuckschwerdt <zany@triq.net>
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+*/
+
+// gcc -Wall -I ../include -o pulse-eval ../src/baseband.c ../src/write_sigrok.c ../tests/pulse-eval.c && ./pulse-eval FILE
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <fcntl.h>
+#include <sys/types.h>
+#ifdef _MSC_VER
+#include <BaseTsd.h>
+typedef SSIZE_T ssize_t;
+#endif
+
+#ifdef _WIN32
+#include <io.h>
+#include <fcntl.h>
+#ifdef _MSC_VER
+#define F_OK 0
+#define R_OK (1 << 2)
+#endif
+#endif
+#ifndef _MSC_VER
+#include <unistd.h>
+#endif
+
+#include <time.h>
+#include <assert.h>
+
+#include "baseband.h"
+#include "write_sigrok.h"
+
+static int read_buf(char const *filename, void *buf, size_t nbyte)
+{
+    int fd = open(filename, O_RDONLY);
+    if (fd < 0) {
+        fprintf(stderr, "Failed to open %s\n", filename);
+        return -1;
+    }
+    ssize_t ret = read(fd, buf, nbyte);
+    close(fd);
+    return ret;
+}
+
+static int write_buf(char const *filename, void const *buf, size_t nbyte)
+{
+    int fd = open(filename, O_WRONLY | O_CREAT | O_TRUNC, 0644);
+    if (fd < 0) {
+        fprintf(stderr, "Failed to open %s\n", filename);
+        return -1;
+    }
+    ssize_t ret = write(fd, buf, nbyte);
+    close(fd);
+    return ret;
+}
+
+static int write_u16_to_f32(char const *filename, uint16_t const *u16, size_t len)
+{
+    float *f32 = malloc(sizeof(float) * len);
+    assert(f32);
+    for (size_t i = 0; i < len; ++i) {
+        f32[i] = u16[i] / 65536.0;
+    }
+    int ret = write_buf(filename, f32, sizeof(float) * len);
+    free(f32);
+    return ret;
+}
+
+static int write_s16_to_f32(char const *filename, uint16_t const *s16, size_t len)
+{
+    float *f32 = malloc(sizeof(float) * len);
+    assert(f32);
+    for (size_t i = 0; i < len; ++i) {
+        f32[i] = s16[i] / 32768.0;
+    }
+    int ret = write_buf(filename, f32, sizeof(float) * len);
+    free(f32);
+    return ret;
+}
+
+// ---
+
+#define MAVG_WIDTH 8
+typedef struct mavg {
+    int idx;
+    int avg;
+    int vs[MAVG_WIDTH];       // values
+} mavg_t;
+
+static void mavg_push(mavg_t *a, int val)
+{
+    a->avg          = a->avg - a->vs[a->idx] + val;
+    a->vs[a->idx++] = val;
+    a->idx          = a->idx % MAVG_WIDTH;
+}
+
+static int mavg_avg(mavg_t *a)
+{
+    return a->avg / MAVG_WIDTH;
+}
+
+// ---
+
+#define MAVGW_WIDTH 512
+typedef struct mavgw {
+    int idx;
+    int avg;
+    int vs[MAVGW_WIDTH]; // values
+} mavgw_t;
+
+static void mavgw_push(mavgw_t *a, int val)
+{
+    a->avg          = a->avg - a->vs[a->idx] + val;
+    a->vs[a->idx++] = val;
+    a->idx          = a->idx % MAVGW_WIDTH;
+}
+
+static int mavgw_avg(mavgw_t *a)
+{
+    return a->avg / MAVGW_WIDTH;
+}
+
+// ---
+
+#define MAVGDEV_WIDTH 8
+typedef struct mavgdev {
+    int idx;
+    int avg;
+    unsigned dev;
+    int vs[MAVGDEV_WIDTH]; // values
+    unsigned msq[MAVGDEV_WIDTH]; // mean squares
+} mavgdev_t;
+
+static void mavgdev_push(mavgdev_t *a, int val)
+{
+    a->avg        = a->avg - a->vs[a->idx] + val;
+    a->vs[a->idx] = val;
+
+    int valc         = val - a->avg / MAVGDEV_WIDTH;
+    //unsigned val2    = abs(valc);
+    unsigned val2    = (valc * valc) >> 15;
+    a->dev           = a->dev - a->msq[a->idx] + val2;
+    a->msq[a->idx++] = val2;
+
+    a->idx = a->idx % MAVGDEV_WIDTH;
+}
+
+static int mavgdev_avg(mavgdev_t *a)
+{
+    return a->avg / MAVGDEV_WIDTH;
+}
+
+static int mavgdev_dev(mavgdev_t *a)
+{
+    return a->dev / MAVGDEV_WIDTH;
+}
+
+// ---
+
+int main(int argc, char *argv[])
+{
+    //baseband_init();
+
+    char *filename;
+    long n_read;
+    size_t n_samples;
+    int block_size = 4096000;
+    unsigned sample_rate = 250000;
+    int ret = 0;
+
+    int argi = 1;
+    for (; argi < argc; ++argi) {
+        if (*argv[argi] != '-')
+            break;
+        if (argv[argi][1] == 'b')
+            block_size = atoi(argv[++argi]);
+        else if (argv[argi][1] == 's')
+            sample_rate = atoi(argv[++argi]);
+        else {
+            fprintf(stderr, "Wrong argument (%s).\n", argv[argi]);
+            return 1;
+        }
+    }
+    if (argc <= argi) {
+        fprintf(stderr, "%s [-s samplerate] [-b blocksize] file", argv[0]);
+        return 1;
+    }
+    filename = argv[argi];
+
+    uint8_t *cu8_buf = malloc(sizeof(uint8_t) * 2 * block_size);
+    assert(cu8_buf);
+    uint8_t *cs8_buf = malloc(sizeof(uint8_t) * 2 * block_size);
+    assert(cs8_buf);
+    uint16_t *y16_buf = malloc(sizeof(uint16_t) * block_size);
+    assert(y16_buf);
+    uint16_t *am16_buf = malloc(sizeof(uint16_t) * block_size);
+    assert(am16_buf);
+    int16_t *fm16_buf = malloc(sizeof(int16_t) * block_size);
+    assert(fm16_buf);
+    uint8_t *u8_buf = calloc(block_size, sizeof(uint8_t));
+    assert(u8_buf);
+
+    uint16_t *mavgl = calloc(block_size, sizeof(uint16_t));
+    assert(mavgl);
+    uint16_t *mavgr = calloc(block_size, sizeof(uint16_t));
+    assert(mavgr);
+    uint16_t *mavgw = calloc(block_size, sizeof(uint16_t));
+    assert(mavgw);
+
+    uint16_t *mdevl = calloc(block_size, sizeof(uint16_t));
+    assert(mdevl);
+    uint16_t *mdevr = calloc(block_size, sizeof(uint16_t));
+    assert(mdevr);
+
+    uint16_t *davgl = calloc(block_size, sizeof(uint16_t));
+    assert(davgl);
+    uint16_t *davgr = calloc(block_size, sizeof(uint16_t));
+    assert(davgr);
+
+    n_read = read_buf(filename, cu8_buf, sizeof(uint8_t) * 2 * block_size);
+    if (n_read < 1) {
+        ret = 1;
+        goto out;
+    }
+    n_samples = n_read / (sizeof(uint8_t) * 2);
+
+    for (size_t i = 0; i < n_samples * 2; ++i) {
+        cs8_buf[i] = cu8_buf[i] - 128;
+    }
+
+    magnitude_est_cu8(cu8_buf, y16_buf, n_samples);
+    envelope_detect_nolut(cu8_buf, am16_buf, n_samples);
+    demodfm_state_t fm_state;
+    baseband_demod_FM(cu8_buf, fm16_buf, n_samples, &fm_state, 0);
+    // envelope_detect(cu8_buf, y16_buf, n_samples);
+    // magnitude_est_cu8(cu8_buf, y16_buf, n_samples);
+    // baseband_low_pass_filter(y16_buf, (int16_t *)u16_buf, n_samples, &state);
+    // baseband_demod_FM(cu8_buf, s16_buf, n_samples, &fm_state);
+
+    // moving avgs (AM)
+    mavg_t ml = {0};
+    for (size_t i = 0; i < n_samples; ++i) {
+        mavgl[i] = mavg_avg(&ml);
+        mavg_push(&ml, y16_buf[i]);
+    }
+
+    mavg_t mr = {0};
+    for (size_t i = 0; i < n_samples - 8; ++i) {
+        mavgr[i] = mavg_avg(&mr);
+        mavg_push(&mr, y16_buf[i + 8]);
+    }
+
+    mavgw_t mw = {0};
+    for (size_t i = 0; i < n_samples; ++i) {
+        mavgw[i] = mavgw_avg(&mw);
+        mavgw_push(&mw, y16_buf[i]);
+    }
+
+    // slice mavgl by mavgw
+    for (size_t i = 0; i < n_samples; ++i) {
+        u8_buf[i] = mavgw[i] < 1000 ? 0 : mavgl[i] > mavgw[i] ? 0xff : 0;
+    }
+
+    // moving devs (FM)
+    mavgdev_t vl = {0};
+    for (size_t i = 0; i < n_samples; ++i) {
+        mdevl[i] = mavgdev_dev(&vl);
+        mavgdev_push(&vl, fm16_buf[i]);
+    }
+
+    mavgdev_t vr = {0};
+    for (size_t i = 0; i < n_samples - 8; ++i) {
+        mdevr[i] = mavgdev_dev(&vr);
+        mavgdev_push(&vr, fm16_buf[i + 8]);
+    }
+
+    // decaying avgs
+    int dl = y16_buf[0];
+    for (size_t i = 0; i < n_samples; ++i) {
+        davgl[i] = dl;
+        dl = (dl + y16_buf[i]) / 2;
+    }
+
+    int dr = y16_buf[0];
+    for (size_t i = 0; i < n_samples - 7; ++i) {
+        davgr[i] = dr / 128;
+        dr = 2 * dr - y16_buf[i] * 128 + y16_buf[i + 7];
+    }
+
+    // tests
+    for (size_t i = 0; i < n_samples; ++i) {
+        y16_buf[i] -= mdevr[i] / 16;
+    }
+
+    write_buf("logic-1-1", u8_buf, n_samples);
+    write_s16_to_f32("analog-1-2-1", am16_buf, n_samples);
+    write_s16_to_f32("analog-1-3-1", y16_buf, n_samples);
+    write_s16_to_f32("analog-1-4-1", (uint16_t *)fm16_buf, n_samples);
+    write_s16_to_f32("analog-1-5-1", mavgl, n_samples);
+    write_s16_to_f32("analog-1-6-1", mavgr, n_samples);
+    write_s16_to_f32("analog-1-7-1", mavgw, n_samples);
+    write_s16_to_f32("analog-1-8-1", mdevl, n_samples);
+    write_s16_to_f32("analog-1-9-1", mdevr, n_samples);
+    write_s16_to_f32("analog-1-10-1", davgl, n_samples);
+    write_s16_to_f32("analog-1-11-1", davgr, n_samples);
+
+    char const *labels[] = {
+            "logic",
+            "am16",
+            "y16",
+            "fm16",
+            "mavgl",
+            "mavgr",
+            "mavgw",
+            "mdevl",
+            "mdevr",
+            "davgl",
+            "davgr",
+    };
+    write_sigrok("out.sr", sample_rate, 1, 9, labels);
+    open_pulseview("out.sr");
+
+out:
+    free(cu8_buf);
+    free(cs8_buf);
+    free(y16_buf);
+    free(am16_buf);
+    free(fm16_buf);
+    free(u8_buf);
+    free(mavgl);
+    free(mavgr);
+    free(mavgw);
+    free(mdevl);
+    free(mdevr);
+    free(davgl);
+    free(davgr);
+}