165 lines
3.1 KiB
C
165 lines
3.1 KiB
C
/*
|
|
* axis.c
|
|
*
|
|
* Logic to deal with various axises
|
|
*
|
|
* Copyright (C) 2013-2021 Sylvain Munaut
|
|
* SPDX-License-Identifier: GPL-3.0-or-later
|
|
*/
|
|
|
|
/*! \addtogroup axis
|
|
* @{
|
|
*/
|
|
|
|
/*! \file axis.c
|
|
* \brief Logic to deal with various axises
|
|
*/
|
|
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <math.h>
|
|
|
|
#include "axis.h"
|
|
|
|
|
|
#define FX_MODE_COUNT 0
|
|
#define FX_MODE_RELATIVE 1
|
|
#define FX_MODE_ABSOLUTE 2
|
|
|
|
|
|
static double
|
|
_si_scaling(double val, int max_num, char *prefix, int *exp)
|
|
{
|
|
const char prefixes[5] = { ' ', 'k', 'M', 'G', 'T' };
|
|
int exponent = log10f(fabs(val));
|
|
int d = exponent >= max_num ? ((exponent - max_num + 3) / 3) : 0;
|
|
|
|
if (prefix)
|
|
*prefix = prefixes[d];
|
|
|
|
if (exp)
|
|
*exp = d * 3;
|
|
|
|
return val / powf(10.0, d * 3);
|
|
}
|
|
|
|
static int
|
|
_num_decimals(double val)
|
|
{
|
|
int c;
|
|
double v;
|
|
|
|
for (c=0; c<22; c++) {
|
|
v = val * pow(10, c);
|
|
if ( fabs(v - round(v)) == 0.0 )
|
|
return c;
|
|
}
|
|
|
|
return -1;
|
|
}
|
|
|
|
|
|
void
|
|
freq_axis_build(struct freq_axis *fx, double center, double span, int n_div)
|
|
{
|
|
/* Basic info */
|
|
fx->center = center;
|
|
fx->span = span;
|
|
fx->step = span / n_div;
|
|
|
|
/* Select mode of operation */
|
|
if (span == 0.0) {
|
|
fx->mode = FX_MODE_COUNT;
|
|
} else if (center == 0.0) {
|
|
fx->mode = FX_MODE_RELATIVE;
|
|
} else if (floor(log10f(fx->step)) < (floor(log10f(fx->center)) - 4)) {
|
|
fx->mode = FX_MODE_RELATIVE;
|
|
} else {
|
|
fx->mode = FX_MODE_ABSOLUTE;
|
|
}
|
|
|
|
/* Select display format for abolute frequencies */
|
|
if (center != 0.0)
|
|
{
|
|
double min_freq, max_freq, big_freq;
|
|
char prefix[2] = {0, 0};
|
|
int exp, x, y, z;
|
|
|
|
max_freq = fx->center + (span / 2.0);
|
|
min_freq = fx->center - (span / 2.0);
|
|
|
|
big_freq = (fabs(max_freq) > fabs(min_freq)) ?
|
|
fabs(max_freq) : fabs(min_freq);
|
|
|
|
_si_scaling(big_freq, 4, prefix, &exp);
|
|
|
|
if (prefix[0] == ' ')
|
|
prefix[0] = '\0';
|
|
|
|
fx->abs_scale = 1.0 / powf(10.0, exp);
|
|
|
|
x = (int)floor(log10f(big_freq)) - exp + 1;
|
|
y = _num_decimals(fx->center * fx->abs_scale);
|
|
z = _num_decimals(fx->step * fx->abs_scale);
|
|
|
|
if (z > y)
|
|
y = z;
|
|
|
|
if (x + y > 6)
|
|
y = 6 - x;
|
|
|
|
sprintf(fx->abs_fmt, "%%.%dlf%s", y, prefix);
|
|
}
|
|
|
|
/* Select display format for relative mode */
|
|
if (fx->mode == FX_MODE_RELATIVE)
|
|
{
|
|
double max_dev = fx->step * 5;
|
|
char prefix[2] = {0, 0};
|
|
int exp, x, y;
|
|
|
|
_si_scaling(max_dev, 3, prefix, &exp);
|
|
|
|
if (prefix[0] == ' ')
|
|
prefix[0] = '\0';
|
|
|
|
fx->rel_step = fx->step / powf(10.0, exp);
|
|
|
|
x = (int)floor(log10f(max_dev)) - exp + 1;
|
|
y = _num_decimals(fx->rel_step);
|
|
|
|
if (x + y > 4)
|
|
y = 4 - x;
|
|
|
|
sprintf(fx->rel_fmt, "%%+.%dlf%s", y, prefix);
|
|
}
|
|
}
|
|
|
|
void
|
|
freq_axis_render(struct freq_axis *fx, char *str, int step)
|
|
{
|
|
/* Special case: count mode */
|
|
if (step && (fx->mode == FX_MODE_COUNT)) {
|
|
sprintf(str, "%+d", step);
|
|
return;
|
|
}
|
|
|
|
/* Special case: no center frequency */
|
|
if (!step && (fx->center == 0.0)) {
|
|
sprintf(str, "0");
|
|
return;
|
|
}
|
|
|
|
/* Relative case */
|
|
if (step && (fx->mode == FX_MODE_RELATIVE)) {
|
|
sprintf(str, fx->rel_fmt, step * fx->rel_step);
|
|
return;
|
|
}
|
|
|
|
/* Normal full absolute frequency */
|
|
sprintf(str, fx->abs_fmt, (fx->center + step * fx->step) * fx->abs_scale);
|
|
}
|
|
|
|
/*! @} */
|