aboutsummaryrefslogtreecommitdiffstats
path: root/src/libdisplay
diff options
context:
space:
mode:
authorAndreas Eversberg <jolly@eversberg.eu>2017-11-18 08:49:13 +0100
committerAndreas Eversberg <jolly@eversberg.eu>2017-12-03 08:45:55 +0100
commit016e72d6d70ea64dfb47a5a73727960cec4fc53a (patch)
tree4564f95f34535d29ae133377173b667ccf7e79c3 /src/libdisplay
parentab59a26a519d4bfb88f9ca6f882da8baab90a95a (diff)
Restructure: Move display from common code to 'libdisplay'
Diffstat (limited to 'src/libdisplay')
-rw-r--r--src/libdisplay/Makefile.am19
-rw-r--r--src/libdisplay/display.h99
-rw-r--r--src/libdisplay/display_iq.c280
-rw-r--r--src/libdisplay/display_measurements.c359
-rw-r--r--src/libdisplay/display_spectrum.c292
-rw-r--r--src/libdisplay/display_status.c147
-rw-r--r--src/libdisplay/display_wave.c270
7 files changed, 1466 insertions, 0 deletions
diff --git a/src/libdisplay/Makefile.am b/src/libdisplay/Makefile.am
new file mode 100644
index 0000000..8f6097d
--- /dev/null
+++ b/src/libdisplay/Makefile.am
@@ -0,0 +1,19 @@
+AM_CPPFLAGS = -Wall -Wextra -g $(all_includes)
+
+noinst_LIBRARIES = libdisplay.a
+
+libdisplay_a_SOURCES = \
+ display_status.c \
+ display_wave.c \
+ display_measurements.c
+
+if HAVE_SDR
+libdisplay_a_SOURCES += \
+ display_iq.c \
+ display_spectrum.c
+endif
+
+if HAVE_SDR
+AM_CPPFLAGS += -DHAVE_SDR
+endif
+
diff --git a/src/libdisplay/display.h b/src/libdisplay/display.h
new file mode 100644
index 0000000..386a2d7
--- /dev/null
+++ b/src/libdisplay/display.h
@@ -0,0 +1,99 @@
+#define DISPLAY_INTERVAL 0.04 /* time (in seconds) for each interval */
+#define DISPLAY_PARAM_HISTORIES 25 /* number of intervals (should result in one seconds) */
+
+#define MAX_DISPLAY_WIDTH 1024
+
+typedef struct sender sender_t;
+
+typedef struct display_wave {
+ int interval_pos;
+ int interval_max;
+ int offset;
+ sample_t buffer[MAX_DISPLAY_WIDTH];
+} dispwav_t;
+
+enum display_measurements_type {
+ DISPLAY_MEAS_LAST, /* display last value */
+ DISPLAY_MEAS_PEAK, /* display peak value */
+ DISPLAY_MEAS_PEAK2PEAK, /* display peak value of min..max range */
+ DISPLAY_MEAS_AVG, /* display average value */
+};
+
+enum display_measurements_bar {
+ DISPLAY_MEAS_LEFT, /* bar graph from left */
+ DISPLAY_MEAS_CENTER, /* bar graph from center */
+};
+
+typedef struct display_measurements_param {
+ struct display_measurements_param *next;
+ char name[32]; /* parameter name (e.g. 'Deviation') */
+ char format[32]; /* unit name (e.g. "%.2f KHz") */
+ enum display_measurements_type type;
+ enum display_measurements_bar bar;
+ double min; /* minimum value */
+ double max; /* maximum value */
+ double mark; /* mark (target) value */
+ double value; /* current value (peak, sum...) */
+ double value2; /* max value for min..max range */
+ double last; /* last valid value (used for DISPLAY_MEAS_LAST) */
+ int value_count; /* count number of values of one interval */
+ double value_history[DISPLAY_PARAM_HISTORIES]; /* history of values of last second */
+ double value2_history[DISPLAY_PARAM_HISTORIES]; /* stores max for min..max range */
+ int value_history_pos; /* next history value to write */
+} dispmeasparam_t;
+
+typedef struct display_measurements {
+ dispmeasparam_t *head;
+} dispmeas_t;
+
+#define MAX_DISPLAY_IQ 1024
+
+typedef struct display_iq {
+ int interval_pos;
+ int interval_max;
+ float buffer[MAX_DISPLAY_IQ * 2];
+} dispiq_t;
+
+#define MAX_DISPLAY_SPECTRUM 1024
+
+typedef struct display_spectrum {
+ int interval_pos;
+ int interval_max;
+ double buffer_I[MAX_DISPLAY_SPECTRUM];
+ double buffer_Q[MAX_DISPLAY_SPECTRUM];
+} dispspectrum_t;
+
+#define MAX_HEIGHT_STATUS 32
+
+void get_win_size(int *w, int *h);
+
+void display_wave_init(sender_t *sender, int samplerate);
+void display_wave_on(int on);
+void display_wave_limit_scroll(int on);
+void display_wave(sender_t *sender, sample_t *samples, int length, double range);
+
+void display_status_on(int on);
+void display_status_limit_scroll(int on);
+void display_status_start(void);
+void display_status_channel(int channel, const char *type, const char *state);
+void display_status_subscriber(const char *number, const char *state);
+void display_status_end(void);
+
+void display_measurements_init(sender_t *sender, int samplerate);
+void display_measurements_exit(sender_t *sender);
+void display_measurements_on(int on);
+void display_measurements_limit_scroll(int on);
+dispmeasparam_t *display_measurements_add(sender_t *sender, char *name, char *format, enum display_measurements_type type, enum display_measurements_bar bar, double min, double max, double mark);
+void display_measurements_update(dispmeasparam_t *param, double value, double value2);
+void display_measurements(double elapsed);
+
+void display_iq_init(int samplerate);
+void display_iq_on(int on);
+void display_iq_limit_scroll(int on);
+void display_iq(float *samples, int length);
+
+void display_spectrum_init(int samplerate, double center_frequency);
+void display_spectrum_on(int on);
+void display_spectrum_limit_scroll(int on);
+void display_spectrum(float *samples, int length);
+
diff --git a/src/libdisplay/display_iq.c b/src/libdisplay/display_iq.c
new file mode 100644
index 0000000..c6ab910
--- /dev/null
+++ b/src/libdisplay/display_iq.c
@@ -0,0 +1,280 @@
+/* display IQ data form functions
+ *
+ * (C) 2016 by Andreas Eversberg <jolly@eversberg.eu>
+ * All Rights Reserved
+ *
+ * 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 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <stdio.h>
+#include <stdint.h>
+#include <string.h>
+#include <math.h>
+#include <pthread.h>
+#include <stdlib.h>
+#include "../libsample/sample.h"
+#include "../libmobile/sender.h"
+
+/* must be odd value! */
+#define SIZE 23
+
+static char screen[SIZE][MAX_DISPLAY_WIDTH];
+static uint8_t screen_color[SIZE][MAX_DISPLAY_WIDTH];
+static uint8_t screen_history[SIZE * 2][MAX_DISPLAY_WIDTH];
+static int iq_on = 0;
+static double db = 80;
+
+static dispiq_t disp;
+
+void display_iq_init(int samplerate)
+{
+ memset(&disp, 0, sizeof(disp));
+ memset(&screen_history, 0, sizeof(screen_history));
+ disp.interval_max = (double)samplerate * DISPLAY_INTERVAL + 0.5;
+ /* should not happen due to low interval */
+ if (disp.interval_max < MAX_DISPLAY_IQ - 1)
+ disp.interval_max = MAX_DISPLAY_IQ - 1;
+}
+
+void display_iq_on(int on)
+{
+ int j;
+ int w, h;
+
+ get_win_size(&w, &h);
+
+ if (iq_on) {
+ memset(&screen, ' ', sizeof(screen));
+ memset(&screen_history, 0, sizeof(screen_history));
+ printf("\0337\033[H");
+ for (j = 0; j < SIZE; j++) {
+ screen[j][w] = '\0';
+ puts(screen[j]);
+ }
+ printf("\0338"); fflush(stdout);
+ }
+
+ if (on < 0) {
+ if (++iq_on == 3)
+ iq_on = 0;
+ } else
+ iq_on = on;
+}
+
+void display_iq_limit_scroll(int on)
+{
+ int w, h;
+
+ if (!iq_on)
+ return;
+
+ get_win_size(&w, &h);
+
+ printf("\0337");
+ printf("\033[%d;%dr", (on) ? SIZE + 1 : 1, h);
+ printf("\0338");
+}
+
+/*
+ * plot IQ data:
+ *
+ * theoretical example: SIZE = 3 allows 6 steps plotted as dots
+ *
+ * Line 0: :
+ * Line 1: :
+ * Line 2: :
+ *
+ * The level of -1.0 .. 1.0 is scaled to -3 and 3.
+ *
+ * The lowest of the upper 3 dots ranges from 0.0 .. <1.5.
+ * The upper most dot ranges from 2.5 .. <3.5.
+ * The highest of the lower 3 dots ranges from <0.0 .. >-1.5;
+ * The lower most dot ranges from -2.5 .. >-3.5.
+ *
+ * The center column ranges from -0.5 .. <0.5.
+ * The columns about the center from -1.5 .. <1.5.
+ */
+void display_iq(float *samples, int length)
+{
+ int pos, max;
+ float *buffer;
+ int i, j, k;
+ int color = 9; /* default color */
+ int x_center, y_center;
+ double I, Q, L, l, s;
+ int x, y;
+ int v, r;
+ int width, h;
+
+ if (!iq_on)
+ return;
+
+ get_win_size(&width, &h);
+
+ /* at what line we draw our zero-line and what character we use */
+ x_center = width >> 1;
+ y_center = (SIZE - 1) >> 1;
+
+ pos = disp.interval_pos;
+ max = disp.interval_max;
+ buffer = disp.buffer;
+
+ for (i = 0; i < length; i++) {
+ if (pos >= MAX_DISPLAY_IQ) {
+ if (++pos == max)
+ pos = 0;
+ continue;
+ }
+ buffer[pos * 2] = samples[i * 2];
+ buffer[pos * 2 + 1] = samples[i * 2 + 1];
+ pos++;
+ if (pos == MAX_DISPLAY_IQ) {
+ memset(&screen, ' ', sizeof(screen));
+ memset(&screen_color, 7, sizeof(screen_color));
+ /* render screen history to screen */
+ for (y = 0; y < SIZE * 2; y++) {
+ for (x = 0; x < width; x++) {
+ v = screen_history[y][x];
+ v -= 8;
+ if (v < 0)
+ v = 0;
+ screen_history[y][x] = v;
+ r = random() & 0x3f;
+ if (r >= v)
+ continue;
+ if (screen[y/2][x] == ':')
+ continue;
+ if (screen[y/2][x] == '.') {
+ if ((y & 1) == 0)
+ screen[y/2][x] = ':';
+ continue;
+ }
+ if (screen[y/2][x] == '\'') {
+ if ((y & 1))
+ screen[y/2][x] = ':';
+ continue;
+ }
+ if ((y & 1) == 0)
+ screen[y/2][x] = '\'';
+ else
+ screen[y/2][x] = '.';
+ screen_color[y/2][x] = 4;
+ }
+ }
+ /* plot current IQ date */
+ for (j = 0; j < MAX_DISPLAY_IQ; j++) {
+ I = buffer[j * 2];
+ Q = buffer[j * 2 + 1];
+ L = I*I + Q*Q;
+ if (iq_on > 1) {
+ /* logarithmic scale */
+ l = sqrt(L);
+ s = log10(l) * 20 + db;
+ if (s < 0)
+ s = 0;
+ I = (I / l) * (s / db);
+ Q = (Q / l) * (s / db);
+ }
+ x = x_center + (int)(I * (double)SIZE + (double)width + 0.5) - width;
+ if (x < 0)
+ continue;
+ if (x > width - 1)
+ continue;
+ if (Q >= 0)
+ y = SIZE - 1 - (int)(Q * (double)SIZE - 0.5);
+ else
+ y = SIZE - (int)(Q * (double)SIZE + 0.5);
+ if (y < 0)
+ continue;
+ if (y > SIZE * 2 - 1)
+ continue;
+ if (screen[y/2][x] == ':' && screen_color[y/2][x] >= 10)
+ goto cont;
+ if (screen[y/2][x] == '.' && screen_color[y/2][x] >= 10) {
+ if ((y & 1) == 0)
+ screen[y/2][x] = ':';
+ goto cont;
+ }
+ if (screen[y/2][x] == '\'' && screen_color[y/2][x] >= 10) {
+ if ((y & 1))
+ screen[y/2][x] = ':';
+ goto cont;
+ }
+ if ((y & 1) == 0)
+ screen[y/2][x] = '\'';
+ else
+ screen[y/2][x] = '.';
+cont:
+ screen_history[y][x] = 255;
+ /* overdrive:
+ * red = close to -1..1 or above
+ * yellow = close to -0.5..0.5 or above
+ * Note: L is square of vector length,
+ * so we compare with square values.
+ */
+ if (L > 0.9 * 0.9)
+ screen_color[y/2][x] = 11;
+ else if (L > 0.45 * 0.45 && screen_color[y/2][x] != 11)
+ screen_color[y/2][x] = 13;
+ else if (screen_color[y/2][x] < 10)
+ screen_color[y/2][x] = 12;
+ }
+ if (iq_on == 1)
+ sprintf(screen[0], "(IQ linear");
+ else
+ sprintf(screen[0], "(IQ log %.0f dB", db);
+ *strchr(screen[0], '\0') = ')';
+ printf("\0337\033[H");
+ for (j = 0; j < SIZE; j++) {
+ for (k = 0; k < width; k++) {
+ if ((j == y_center || k == x_center) && screen[j][k] == ' ') {
+ /* cross */
+ if (color != 4) {
+ color = 4;
+ printf("\033[0;34m");
+ }
+ if (j == y_center) {
+ if (k == x_center)
+ putchar('o');
+ else if (k == x_center - SIZE)
+ putchar('+');
+ else if (k == x_center + SIZE)
+ putchar('+');
+ else
+ putchar('-');
+ } else {
+ if (j == 0 || j == SIZE - 1)
+ putchar('+');
+ else
+ putchar('|');
+ }
+ } else {
+ if (screen_color[j][k] != color) {
+ color = screen_color[j][k];
+ printf("\033[%d;3%dm", color / 10, color % 10);
+ }
+ putchar(screen[j][k]);
+ }
+ }
+ printf("\n");
+ }
+ /* reset color and position */
+ printf("\033[0;39m\0338"); fflush(stdout);
+ }
+ }
+
+ disp.interval_pos = pos;
+}
+
+
diff --git a/src/libdisplay/display_measurements.c b/src/libdisplay/display_measurements.c
new file mode 100644
index 0000000..eb8618e
--- /dev/null
+++ b/src/libdisplay/display_measurements.c
@@ -0,0 +1,359 @@
+/* display measurements functions
+ *
+ * (C) 2017 by Andreas Eversberg <jolly@eversberg.eu>
+ * All Rights Reserved
+ *
+ * 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 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <stdio.h>
+#include <stdint.h>
+#include <string.h>
+#include <stdlib.h>
+#include <pthread.h>
+#include <sys/ioctl.h>
+#include <math.h>
+#include "../libsample/sample.h"
+#include "../libmobile/sender.h"
+
+#define MAX_NAME_LEN 16
+#define MAX_UNIT_LEN 16
+
+static int has_init = 0;
+static int measurements_on = 0;
+double time_elapsed = 0.0;
+static int lines_total = 0;
+static char line[MAX_DISPLAY_WIDTH];
+static char line_color[MAX_DISPLAY_WIDTH];
+
+void display_measurements_init(sender_t *sender, int __attribute__((unused)) samplerate)
+{
+ dispmeas_t *disp = &sender->dispmeas;
+
+ memset(disp, 0, sizeof(*disp));
+ has_init = 1;
+ lines_total = 0;
+ time_elapsed = 0.0;
+}
+
+void display_measurements_exit(sender_t *sender)
+{
+ dispmeas_t *disp = &sender->dispmeas;
+ dispmeasparam_t *param = disp->head, *temp;
+
+ while (param) {
+ temp = param;
+ param = param->next;
+ free(temp);
+ }
+ disp->head = NULL;
+ has_init = 0;
+}
+
+static int color;
+
+static void display_line(int on, int w)
+{
+ int j;
+
+ if (on) {
+ for (j = 0; j < w; j++) {
+ if (line_color[j] != color && line[j] != ' ') {
+ color = line_color[j];
+ printf("\033[%d;3%dm", color / 10, color % 10);
+ }
+ putchar(line[j]);
+ }
+ } else {
+ for (j = 0; j < w; j++)
+ putchar(' ');
+ }
+ putchar('\n');
+ lines_total++;
+}
+
+static void print_measurements(int on)
+{
+ sender_t *sender;
+ dispmeasparam_t *param;
+ int i, j;
+ int width, h;
+ char text[128];
+ double value = 0.0, value2 = 0.0, hold, hold2;
+ int bar_width, bar_left, bar_right, bar_hold, bar_mark;
+
+ get_win_size(&width, &h);
+
+ /* no display, if bar graph is less than one character */
+ bar_width = width - MAX_NAME_LEN - MAX_UNIT_LEN;
+ if (bar_width < 1)
+ return;
+
+ lines_total = 0;
+ color = -1;
+ printf("\0337\033[H");
+ for (sender = sender_head; sender; sender = sender->next) {
+ memset(line, ' ', width);
+ memset(line_color, 7, width);
+ sprintf(line, "(chan %d", sender->kanal);
+ *strchr(line, '\0') = ')';
+ display_line(on, width);
+ for (param = sender->dispmeas.head; param; param = param->next) {
+ memset(line, ' ', width);
+ memset(line_color, 7, width);
+ memset(line_color, 3, MAX_NAME_LEN); /* yellow */
+ switch (param->type) {
+ case DISPLAY_MEAS_LAST:
+ value = param->value;
+ param->value = -NAN;
+ break;
+ case DISPLAY_MEAS_PEAK:
+ /* peak value */
+ value = param->value;
+ param->value = -NAN;
+ param->value_count = 0;
+ break;
+ case DISPLAY_MEAS_PEAK2PEAK:
+ /* peak to peak value */
+ value = param->value;
+ value2 = param->value2;
+ param->value = -NAN;
+ param->value2 = -NAN;
+ param->value_count = 0;
+ break;
+ case DISPLAY_MEAS_AVG:
+ /* average value */
+ if (param->value_count)
+ value = param->value / (double)param->value_count;
+ else
+ value = -NAN;
+ param->value = 0.0;
+ param->value_count = 0;
+ break;
+ }
+ /* add current value to history */
+ param->value_history[param->value_history_pos] = value;
+ param->value2_history[param->value_history_pos] = value2;
+ param->value_history_pos = param->value_history_pos % DISPLAY_PARAM_HISTORIES;
+ /* calculate hold values */
+ hold = -NAN;
+ hold2 = -NAN;
+ switch (param->type) {
+ case DISPLAY_MEAS_LAST:
+ /* if we have valid value, we update 'last' */
+ if (!isnan(value)) {
+ param->last = value;
+ hold = value;
+ } else
+ hold = param->last;
+ break;
+ case DISPLAY_MEAS_PEAK:
+ for (i = 0; i < DISPLAY_PARAM_HISTORIES; i++) {
+ if (isnan(param->value_history[i]))
+ continue;
+ if (isnan(hold) || param->value_history[i] > hold)
+ hold = param->value_history[i];
+ }
+ break;
+ case DISPLAY_MEAS_PEAK2PEAK:
+ for (i = 0; i < DISPLAY_PARAM_HISTORIES; i++) {
+ if (isnan(param->value_history[i]))
+ continue;
+ if (isnan(hold) || param->value_history[i] < hold)
+ hold = param->value_history[i];
+ if (isnan(hold2) || param->value2_history[i] > hold2)
+ hold2 = param->value2_history[i];
+ }
+ if (!isnan(hold))
+ hold = hold2 - hold;
+ if (!isnan(value))
+ value = value2 - value;
+ break;
+ case DISPLAY_MEAS_AVG:
+ for (i = 0, j = 0; i < DISPLAY_PARAM_HISTORIES; i++) {
+ if (isnan(param->value_history[i]))
+ continue;
+ if (j == 0)
+ hold = 0.0;
+ hold += param->value_history[i];
+ j++;
+ }
+ if (j)
+ hold /= j;
+ break;
+ }
+ /* "Deviation ::::::::::............ 4.5 KHz" */
+ strncpy(line, param->name, (strlen(param->name) < MAX_NAME_LEN) ? strlen(param->name) : MAX_NAME_LEN);
+ if (isinf(value) || isnan(value)) {
+ bar_left = -1;
+ bar_right = -1;
+ } else if (param->bar == DISPLAY_MEAS_CENTER) {
+ if (value >= 0.0) {
+ bar_left = (-param->min) / (param->max - param->min) * ((double)bar_width - 1.0);
+ bar_right = (value - param->min) / (param->max - param->min) * ((double)bar_width - 1.0);
+ } else {
+ bar_left = (value - param->min) / (param->max - param->min) * ((double)bar_width - 1.0);
+ bar_right = (-param->min) / (param->max - param->min) * ((double)bar_width - 1.0);
+ }
+ } else {
+ bar_left = -1;
+ bar_right = (value - param->min) / (param->max - param->min) * ((double)bar_width - 1.0);
+ }
+ if (isinf(hold) || isnan(hold))
+ bar_hold = -1;
+ else
+ bar_hold = (hold - param->min) / (param->max - param->min) * ((double)bar_width - 1.0);
+ if (isinf(param->mark))
+ bar_mark = -1;
+ else
+ bar_mark = (param->mark - param->min) / (param->max - param->min) * ((double)bar_width - 1.0);
+ for (i = 0; i < bar_width; i++) {
+ line[i + MAX_NAME_LEN] = ':';
+ if (i == bar_hold)
+ line_color[i + MAX_NAME_LEN] = 13;
+ else if (i == bar_mark)
+ line_color[i + MAX_NAME_LEN] = 14;
+ else if (i >= bar_left && i <= bar_right)
+ line_color[i + MAX_NAME_LEN] = 2;
+ else
+ line_color[i + MAX_NAME_LEN] = 4;
+ }
+ sprintf(text, param->format, hold);
+ if (isnan(hold))
+ memset(line_color + width - MAX_UNIT_LEN, 4, MAX_UNIT_LEN); /* blue */
+ else
+ memset(line_color + width - MAX_UNIT_LEN, 3, MAX_UNIT_LEN); /* yellow */
+ strncpy(line + width - MAX_UNIT_LEN + 1, text, (strlen(text) < MAX_UNIT_LEN) ? strlen(text) : MAX_UNIT_LEN);
+ display_line(on, width);
+ }
+ }
+ /* reset color and position */
+ printf("\033[0;39m\0338"); fflush(stdout);
+}
+
+void display_measurements_on(int on)
+{
+ if (measurements_on)
+ print_measurements(0);
+
+ if (on < 0)
+ measurements_on = 1 - measurements_on;
+ else
+ measurements_on = on;
+}
+
+void display_measurements_limit_scroll(int on)
+{
+ int w, h;
+
+ if (!measurements_on)
+ return;
+
+ get_win_size(&w, &h);
+
+ printf("\0337");
+ printf("\033[%d;%dr", (on) ? lines_total + 1 : 1, h);
+ printf("\0338");
+}
+
+/* add new parameter on startup to the list of measurements */
+dispmeasparam_t *display_measurements_add(sender_t *sender, char *name, char *format, enum display_measurements_type type, enum display_measurements_bar bar, double min, double max, double mark)
+{
+ dispmeas_t *disp = &sender->dispmeas;
+ dispmeasparam_t *param, **param_p = &disp->head;
+ int i;
+
+ if (!has_init) {
+ fprintf(stderr, "Not initialized prior adding measurement, please fix!\n");
+ abort();
+ }
+
+ while (*param_p)
+ param_p = &((*param_p)->next);
+ *param_p = calloc(sizeof(dispmeasparam_t), 1);
+ if (!*param_p)
+ return NULL;
+ param = *param_p;
+ strncpy(param->name, name, sizeof(param->name) - 1);
+ strncpy(param->format, format, sizeof(param->format) - 1);
+ param->type = type;
+ param->bar = bar;
+ param->min = min;
+ param->max = max;
+ param->mark = mark;
+ param->value = -NAN;
+ param->value2 = -NAN;
+ param->last = -NAN;
+ for (i = 0; i < DISPLAY_PARAM_HISTORIES; i++)
+ param->value_history[i] = -NAN;
+ param->value_count = 0;
+
+ return param;
+}
+
+void display_measurements_update(dispmeasparam_t *param, double value, double value2)
+{
+ /* special case where we do not have an instance of the parameter */
+ if (!param)
+ return;
+
+ if (!has_init) {
+ fprintf(stderr, "Not initialized prior updating measurement value, please fix!\n");
+ abort();
+ }
+
+ switch (param->type) {
+ case DISPLAY_MEAS_LAST:
+ param->value = value;
+ break;
+ case DISPLAY_MEAS_PEAK:
+ if (isnan(param->value) || value > param->value)
+ param->value = value;
+ break;
+ case DISPLAY_MEAS_PEAK2PEAK:
+ if (param->value_count == 0 || value < param->value)
+ param->value = value;
+ if (param->value_count == 0 || value2 > param->value2)
+ param->value2 = value2;
+ param->value_count++;
+ break;
+ case DISPLAY_MEAS_AVG:
+ param->value += value;
+ param->value_count++;
+ break;
+ default:
+ fprintf(stderr, "Paramer '%s' has unknown type %d, please fix!\n", param->name, param->type);
+ abort();
+ }
+}
+
+void display_measurements(double elapsed)
+{
+ if (!measurements_on)
+ return;
+
+ if (!has_init) {
+ fprintf(stderr, "Not initialized prior display measurement values, please fix!\n");
+ abort();
+ }
+
+ /* count and check if we need to display this time */
+ time_elapsed += elapsed;
+ if (time_elapsed < DISPLAY_INTERVAL)
+ return;
+ time_elapsed = fmod(time_elapsed, DISPLAY_INTERVAL);
+
+ print_measurements(1);
+}
+
diff --git a/src/libdisplay/display_spectrum.c b/src/libdisplay/display_spectrum.c
new file mode 100644
index 0000000..3c885b7
--- /dev/null
+++ b/src/libdisplay/display_spectrum.c
@@ -0,0 +1,292 @@
+/* display spectrum of IQ data
+ *
+ * (C) 2016 by Andreas Eversberg <jolly@eversberg.eu>
+ * All Rights Reserved
+ *
+ * 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 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <stdio.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+#include <math.h>
+#include "../libsample/sample.h"
+#include "../libmobile/sender.h"
+#include "../libfft/fft.h"
+
+#define HEIGHT 20
+
+static double buffer_max[MAX_DISPLAY_SPECTRUM];
+static char screen[HEIGHT][MAX_DISPLAY_WIDTH];
+static uint8_t screen_color[HEIGHT][MAX_DISPLAY_WIDTH];
+static int spectrum_on = 0;
+static double db = 120;
+static double center_frequency, frequency_range;
+
+static dispspectrum_t disp;
+
+void display_spectrum_init(int samplerate, double _center_frequency)
+{
+ memset(&disp, 0, sizeof(disp));
+ disp.interval_max = (double)samplerate * DISPLAY_INTERVAL + 0.5;
+ /* should not happen due to low interval */
+ if (disp.interval_max < MAX_DISPLAY_SPECTRUM - 1)
+ disp.interval_max = MAX_DISPLAY_SPECTRUM - 1;
+ memset(buffer_max, 0, sizeof(buffer_max));
+
+ center_frequency = _center_frequency;
+ frequency_range = (double)samplerate;
+}
+
+void display_spectrum_on(int on)
+{
+ int j;
+ int w, h;
+
+ get_win_size(&w, &h);
+
+ if (spectrum_on) {
+ memset(&screen, ' ', sizeof(screen));
+ printf("\0337\033[H");
+ for (j = 0; j < HEIGHT; j++) {
+ screen[j][w] = '\0';
+ puts(screen[j]);
+ }
+ printf("\0338"); fflush(stdout);
+ }
+
+ if (on < 0) {
+ if (++spectrum_on == 2)
+ spectrum_on = 0;
+ } else
+ spectrum_on = on;
+}
+
+void display_spectrum_limit_scroll(int on)
+{
+ int w, h;
+
+ if (!spectrum_on)
+ return;
+
+ get_win_size(&w, &h);
+
+ printf("\0337");
+ printf("\033[%d;%dr", (on) ? HEIGHT + 1 : 1, h);
+ printf("\0338");
+}
+
+/*
+ * plot spectrum data:
+ *
+ */
+void display_spectrum(float *samples, int length)
+{
+ sender_t *sender;
+ char print_channel[32], print_frequency[32];
+ int width, h;
+ int pos, max;
+ double *buffer_I, *buffer_Q;
+ int color = 9; /* default color */
+ int i, j, k, o;
+ double I, Q, v;
+ int s, e, l, n;
+
+ if (!spectrum_on)
+ return;
+
+ get_win_size(&width, &h);
+ if (width > MAX_DISPLAY_WIDTH)
+ width = MAX_DISPLAY_WIDTH;
+
+ /* calculate size of FFT */
+ int m, fft_size = 0, fft_taps = 0;
+ for (m = 0; m < 16; m++) {
+ if ((1 << m) > MAX_DISPLAY_SPECTRUM)
+ break;
+ if ((1 << m) <= width) {
+ fft_taps = m;
+ fft_size = 1 << m;
+ }
+ }
+ if (m == 16) {
+ fprintf(stderr, "Size of spectrum is not a power of 2, please fix!\n");
+ abort();
+ }
+
+ int heigh[fft_size], low[fft_size];
+
+ pos = disp.interval_pos;
+ max = disp.interval_max;
+ buffer_I = disp.buffer_I;
+ buffer_Q = disp.buffer_Q;
+
+ for (i = 0; i < length; i++) {
+ if (pos >= fft_size) {
+ if (++pos == max)
+ pos = 0;
+ continue;
+ }
+ buffer_I[pos] = samples[i * 2];
+ buffer_Q[pos] = samples[i * 2 + 1];
+ pos++;
+ if (pos == fft_size) {
+ fft_process(1, fft_taps, buffer_I, buffer_Q);
+ k = 0;
+ for (j = 0; j < fft_size; j++) {
+ /* scale result vertically */
+ I = buffer_I[(j + fft_size / 2) % fft_size];
+ Q = buffer_Q[(j + fft_size / 2) % fft_size];
+ v = sqrt(I*I + Q*Q);
+ v = log10(v) * 20 + db;
+ if (v < 0)
+ v = 0;
+ v /= db;
+ buffer_max[j] -= DISPLAY_INTERVAL / 10.0;
+ if (v > buffer_max[j])
+ buffer_max[j] = v;
+
+ /* heigh is the maximum value */
+ heigh[j] = (double)(HEIGHT * 2 - 1) * (1.0 - buffer_max[j]);
+ if (heigh[j] < 0)
+ heigh[j] = 0;
+ if (heigh[j] >= (HEIGHT * 2))
+ heigh[j] = (HEIGHT * 2) - 1;
+ /* low is the current value */
+ low[j] = (double)(HEIGHT * 2 - 1) * (1.0 - v);
+ if (low[j] < 0)
+ low[j] = 0;
+ if (low[j] >= (HEIGHT * 2))
+ low[j] = (HEIGHT * 2) - 1;
+ }
+ /* plot scaled buffer */
+ memset(&screen, ' ', sizeof(screen));
+ memset(&screen_color, 7, sizeof(screen_color)); /* all white */
+ sprintf(screen[0], "(spectrum log %.0f dB", db);
+ *strchr(screen[0], '\0') = ')';
+ o = (width - fft_size) / 2; /* offset from left border */
+ for (j = 0; j < fft_size; j++) {
+ s = l = n = low[j];
+ /* get last and next value */
+ if (j > 0)
+ l = (low[j - 1] + s) / 2;
+ if (j < fft_size - 1)
+ n = (low[j + 1] + s) / 2;
+ if (s > l && s > n) {
+ /* current value is a minimum */
+ e = s;
+ s = (l < n) ? (l + 1) : (n + 1);
+ } else if (s < l && s < n) {
+ /* current value is a maximum */
+ e = (l > n) ? l : n;
+ } else if (l < n) {
+ /* last value is higher, next value is lower */
+ s = l + 1;
+ e = n;
+ } else if (l > n) {
+ /* last value is lower, next value is higher */
+ s = n + 1;
+ e = l;
+ } else {
+ /* current, last and next values are equal */
+ e = s;
+ }
+ if (s == e) {
+ if ((s & 1) == 0)
+ screen[s >> 1][j + o] = '\'';
+ else
+ screen[s >> 1][j + o] = '.';
+ screen_color[s >> 1][j + o] = 13;
+ } else {
+ if ((s & 1) == 0)
+ screen[s >> 1][j + o] = '|';
+ else
+ screen[s >> 1][j + o] = '.';
+ screen_color[s >> 1][j + o] = 13;
+ if ((e & 1) == 0)
+ screen[e >> 1][j + o] = '\'';
+ else
+ screen[e >> 1][j + o] = '|';
+ screen_color[e >> 1][j + o] = 13;
+ for (k = (s >> 1) + 1; k < (e >> 1); k++) {
+ screen[k][j + o] = '|';
+ screen_color[k][j + o] = 13;
+ }
+ }
+ e = s;
+ s = heigh[j];
+ if ((s >> 1) < (e >> 1)) {
+ if ((s & 1) == 0)
+ screen[s >> 1][j + o] = '|';
+ else
+ screen[s >> 1][j + o] = '.';
+ screen_color[s >> 1][j + o] = 4;
+ for (k = (s >> 1) + 1; k < (e >> 1); k++) {
+ screen[k][j + o] = '|';
+ screen_color[k][j + o] = 4;
+ }
+ }
+ }
+ for (sender = sender_head; sender; sender = sender->next) {
+ j = (int)((sender->empfangsfrequenz - center_frequency) / frequency_range * (double) fft_size + width / 2 + 0.5);
+ if (j < 0 || j >= width) /* check out-of-range, should not happen */
+ continue;
+ for (k = 0; k < HEIGHT; k++) {
+ /* skip yellow graph */
+ if (screen_color[k][j] == 13)
+ continue;
+ screen[k][j] = ':';
+ screen_color[k][j] = 12;
+ }
+ sprintf(print_channel, "Ch%d", sender->kanal);
+ for (o = 0; o < (int)strlen(print_channel); o++) {
+ s = j - strlen(print_channel) + o;
+ if (s >= 0 && s < width) {
+ screen[HEIGHT - 1][s] = print_channel[o];
+ screen_color[HEIGHT - 1][s] = 7;
+ }
+ }
+ if (fmod(sender->empfangsfrequenz, 1000.0))
+ sprintf(print_frequency, "%.4f", sender->empfangsfrequenz / 1e6);
+ else
+ sprintf(print_frequency, "%.3f", sender->empfangsfrequenz / 1e6);
+ for (o = 0; o < (int)strlen(print_frequency); o++) {
+ s = j + o + 1;
+ if (s >= 0 && s < width) {
+ screen[HEIGHT - 1][s] = print_frequency[o];
+ screen_color[HEIGHT - 1][s] = 7;
+ }
+ }
+ }
+ printf("\0337\033[H");
+ for (j = 0; j < HEIGHT; j++) {
+ for (k = 0; k < width; k++) {
+ if (screen_color[j][k] != color) {
+ color = screen_color[j][k];
+ printf("\033[%d;3%dm", color / 10, color % 10);
+ }
+ putchar(screen[j][k]);
+ }
+ printf("\n");
+ }
+ /* reset color and position */
+ printf("\033[0;39m\0338"); fflush(stdout);
+ }
+ }
+
+ disp.interval_pos = pos;
+}
+
+
diff --git a/src/libdisplay/display_status.c b/src/libdisplay/display_status.c
new file mode 100644
index 0000000..d49ddb6
--- /dev/null
+++ b/src/libdisplay/display_status.c
@@ -0,0 +1,147 @@
+/* display status functions
+ *
+ * (C) 2017 by Andreas Eversberg <jolly@eversberg.eu>
+ * All Rights Reserved
+ *
+ * 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 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <stdio.h>
+#include <stdint.h>
+#include <string.h>
+#include <pthread.h>
+#include <sys/ioctl.h>
+#include "../libsample/sample.h"
+#include "../libmobile/sender.h"
+
+static int status_on = 0;
+static int line_count = 0;
+static int lines_total = 0;
+static char screen[MAX_HEIGHT_STATUS][MAX_DISPLAY_WIDTH];
+
+static void print_status(int on)
+{
+ int i, j;
+ int w, h;
+
+ get_win_size(&w, &h);
+
+ if (w > MAX_DISPLAY_WIDTH)
+ w = MAX_DISPLAY_WIDTH;
+ h--;
+ if (h > lines_total)
+ h = lines_total;
+
+ printf("\0337\033[H\033[1;37m");
+ for (i = 0; i < h; i++) {
+ j = 0;
+ if (on) {
+ for (j = 0; j < w; j++)
+ putchar(screen[i][j]);
+ } else {
+ for (j = 0; j < w; j++)
+ putchar(' ');
+ }
+ putchar('\n');
+ }
+ printf("\0338"); fflush(stdout);
+}
+
+void display_status_on(int on)
+{
+ if (status_on)
+ print_status(0);
+
+ if (on < 0)
+ status_on = 1 - status_on;
+ else
+ status_on = on;
+
+ if (status_on)
+ print_status(1);
+}
+
+void display_status_limit_scroll(int on)
+{
+ int w, h;
+
+ if (!status_on)
+ return;
+
+ get_win_size(&w, &h);
+
+ printf("\0337");
+ printf("\033[%d;%dr", (on) ? lines_total + 1 : 1, h);
+ printf("\0338");
+}
+
+/* start status display */
+void display_status_start(void)
+{
+ memset(screen, ' ', sizeof(screen));
+ memset(screen[0], '-', sizeof(screen[0]));
+ strncpy(screen[0] + 4, "Channel Status", 14);
+ line_count = 1;
+}
+
+void display_status_channel(int channel, const char *type, const char *state)
+{
+ char line[MAX_DISPLAY_WIDTH];
+
+ /* add empty line after previous channel+subscriber */
+ if (line_count > 1 && line_count < MAX_HEIGHT_STATUS)
+ line_count++;
+
+ if (line_count == MAX_HEIGHT_STATUS)
+ return;
+
+ if (type)
+ snprintf(line, sizeof(line), "Channel: %d Type: %s State: %s", channel, type, state);
+ else
+ snprintf(line, sizeof(line), "Channel: %d State: %s", channel, state);
+ line[sizeof(line) - 1] = '\0';
+ strncpy(screen[line_count++], line, strlen(line));
+}
+
+void display_status_subscriber(const char *number, const char *state)
+{
+ char line[MAX_DISPLAY_WIDTH];
+
+ if (line_count == MAX_HEIGHT_STATUS)
+ return;
+
+ if (state)
+ snprintf(line, sizeof(line), " Subscriber: %s State: %s", number, state);
+ else
+ snprintf(line, sizeof(line), " Subscriber: %s", number);
+ line[sizeof(line) - 1] = '\0';
+ strncpy(screen[line_count++], line, strlen(line));
+}
+
+void display_status_end(void)
+{
+ if (line_count < MAX_HEIGHT_STATUS) {
+ memset(screen[line_count], '-', sizeof(screen[line_count]));
+ line_count++;
+ }
+ /* if last total lines exceed current line count, keep it, so removed lines are overwritten with spaces */
+ if (line_count > lines_total)
+ lines_total = line_count;
+ if (status_on)
+ print_status(1);
+ /* set new total lines */
+ lines_total = line_count;
+}
+
+
diff --git a/src/libdisplay/display_wave.c b/src/libdisplay/display_wave.c
new file mode 100644
index 0000000..b4005f9
--- /dev/null
+++ b/src/libdisplay/display_wave.c
@@ -0,0 +1,270 @@
+/* display wave form functions
+ *
+ * (C) 2016 by Andreas Eversberg <jolly@eversberg.eu>
+ * All Rights Reserved
+ *
+ * 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 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <stdio.h>
+#include <stdint.h>
+#include <string.h>
+#include <pthread.h>
+#include <math.h>
+#include <sys/ioctl.h>
+#include "../libsample/sample.h"
+#include "../libmobile/sender.h"
+
+#define HEIGHT 11
+
+static int num_sender = 0;
+static char screen[HEIGHT][MAX_DISPLAY_WIDTH];
+static int wave_on = 0;
+
+void get_win_size(int *w, int *h)
+{
+ struct winsize win;
+ int rc;
+
+ rc = ioctl(0, TIOCGWINSZ, &win);
+ if (rc) {
+ *w = 80;
+ *h = 25;
+ return;
+ }
+
+ *h = win.ws_row;
+ *w = win.ws_col;
+ if (*w > MAX_DISPLAY_WIDTH - 1)
+ *w = MAX_DISPLAY_WIDTH - 1;
+}
+
+void display_wave_init(sender_t *sender, int samplerate)
+{
+ dispwav_t *disp = &sender->dispwav;
+
+ memset(disp, 0, sizeof(*disp));
+ disp->offset = (num_sender++) * HEIGHT;
+ disp->interval_max = (double)samplerate * DISPLAY_INTERVAL + 0.5;
+}
+
+void display_wave_on(int on)
+{
+ int i, j;
+ int w, h;
+
+ get_win_size(&w, &h);
+
+ if (wave_on) {
+ memset(&screen, ' ', sizeof(screen));
+ printf("\0337\033[H");
+ for (i = 0; i < num_sender; i++) {
+ for (j = 0; j < HEIGHT; j++) {
+ screen[j][w] = '\0';
+ puts(screen[j]);
+ }
+ }
+ printf("\0338"); fflush(stdout);
+ }
+
+ if (on < 0)
+ wave_on = 1 - wave_on;
+ else
+ wave_on = on;
+}
+
+void display_wave_limit_scroll(int on)
+{
+ int w, h;
+
+ if (!wave_on)
+ return;
+
+ get_win_size(&w, &h);
+
+ printf("\0337");
+ printf("\033[%d;%dr", (on) ? num_sender * HEIGHT + 1 : 1, h);
+ printf("\0338");
+}
+
+/*
+ * draw wave form:
+ *
+ * theoretical example: HEIGHT = 3 allows 5 steps
+ *
+ * Line 0: '.
+ * Line 1: '.
+ * Line 2: '
+ *
+ * HEIGHT is odd, so the center line's char is ''' (otherwise '.')
+ * (HEIGHT - 1) / 2 = 1, so the center line is drawn in line 1
+ *
+ * y is in range of 0..4, so these are 5 steps, where 2 is the
+ * center line. this is calculated by (HEIGHT * 2 - 1)
+ */
+void display_wave(sender_t *sender, sample_t *samples, int length, double range)
+{
+ dispwav_t *disp = &sender->dispwav;
+ int pos, max;
+ sample_t *buffer;
+ int i, j, k, s, e;
+ double last, current, next;
+ int color = 9; /* default color */
+ int center_line;
+ char center_char;
+ int width, h;
+
+ if (!wave_on)
+ return;
+
+ get_win_size(&width, &h);
+
+ /* at what line we draw our zero-line and what character we use */
+ center_line = (HEIGHT - 1) >> 1;
+ center_char = (HEIGHT & 1) ? '\'' : '.';
+
+ pos = disp->interval_pos;
+ max = disp->interval_max;
+ buffer = disp->buffer;
+
+ for (i = 0; i < length; i++) {
+ if (pos >= width + 2) {
+ if (++pos == max)
+ pos = 0;
+ continue;
+ }
+ buffer[pos++] = samples[i];
+ if (pos == width + 2) {
+ memset(&screen, ' ', sizeof(screen));
+ for (j = 0; j < width; j++) {
+ /* Input value is scaled to range -1 .. 1 and then substracted from 1,
+ * so the result ranges from 0 .. 2.
+ * HEIGHT-1 is multiplied with the range, so a HEIGHT of 3 would allow
+ * 0..4 (5 steps) and a HEIGHT of 11 would allow 0..20 (21 steps).
+ * We always use odd number of steps, so there will be a center between
+ * values.
+ */
+ last = (1.0 - buffer[j] / range) * (double)(HEIGHT - 1);
+ current = (1.0 - buffer[j + 1] / range) * (double)(HEIGHT - 1);
+ next = (1.0 - buffer[j + 2] / range) * (double)(HEIGHT - 1);
+ /* calculate start and end for vertical line
+ * if the current value is a peak (above or below last AND next point),
+ * round this peak point to become one end of the vertical line.
+ * the other end is rounded up or down, so the end of the line will
+ * not overlap with the ends of the surrounding lines.
+ */
+ if (last > current) {
+ if (next > current) {
+ /* current point is a peak up */
+ s = round(current);
+ /* use lowest neighbor point and end is half way */
+ if (last > next)
+ e = floor((last + current) / 2.0);
+ else
+ e = floor((next + current) / 2.0);
+ /* end point must not be above start point */
+ if (e < s)
+ e = s;
+ } else {
+ /* current point is a transition upwards */
+ s = ceil((next + current) / 2.0);
+ e = floor((last + current) / 2.0);
+ /* end point must not be above start point */
+ if (e < s)
+ s = e = round(current);
+ }
+ } else {
+ if (next <= current) {
+ /* current point is a peak down */
+ e = round(current);
+ /* use heighes neighbor point and start is half way */
+ if (last <= next)
+ s = ceil((last + current) / 2.0);
+ else
+ s = ceil((next + current) / 2.0);
+ /* start point must not be below end point */
+ if (s > e)
+ s = e;
+ } else {
+ /* current point is a transition downwards */
+ s = ceil((last + current) / 2.0);
+ e = floor((next + current) / 2.0);
+ /* start point must not be below end point */
+ if (s > e)
+ s = e = round(current);
+ }
+ }
+ /* only draw line, if it is in range */
+ if (e >= 0 && s < HEIGHT * 2 - 1) {
+ /* clip */
+ if (s < 0)
+ s = 0;
+ if (e >= HEIGHT * 2 - 1)
+ e = HEIGHT * 2 - 1;
+ /* plot start and end point */
+ if ((s & 1))
+ screen[s >> 1][j] = '.';
+ else if (e != s)
+ screen[s >> 1][j] = '|';
+ if (!(e & 1))
+ screen[e >> 1][j] = '\'';
+ else if (e != s)
+ screen[e >> 1][j] = '|';
+ /* plot line between start and end point */
+ for (k = (s >> 1) + 1; k < (e >> 1); k++)
+ screen[k][j] = '|';
+ }
+ }
+ sprintf(screen[0], "(chan %d", sender->kanal);
+ *strchr(screen[0], '\0') = ')';
+ printf("\0337\033[H");
+ for (j = 0; j < disp->offset; j++)
+ puts("");
+ for (j = 0; j < HEIGHT; j++) {
+ for (k = 0; k < width; k++) {
+ if (j == center_line && screen[j][k] == ' ') {
+ /* blue 0-line */
+ if (color != 4) {
+ color = 4;
+ printf("\033[0;34m");
+ }
+ putchar(center_char);
+ } else if (screen[j][k] == '\'' || screen[j][k] == '.' || screen[j][k] == '|') {
+ /* green scope curve */
+ if (color != 2) {
+ color = 2;
+ printf("\033[1;32m");
+ }
+ putchar(screen[j][k]);
+ } else if (screen[j][k] != ' ') {
+ /* white other characters */
+ if (color != 7) {
+ color = 7;
+ printf("\033[1;37m");
+ }
+ putchar(screen[j][k]);
+ } else
+ putchar(screen[j][k]);
+ }
+ printf("\n");
+ }
+ /* reset color and position */
+ printf("\033[0;39m\0338"); fflush(stdout);
+ }
+ }
+
+ disp->interval_pos = pos;
+}
+
+