/**************************************************************************** * libs/libdsp/lib_misc.c * * SPDX-License-Identifier: Apache-2.0 * * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. The * ASF licenses this file to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance with the * License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the * License for the specific language governing permissions and limitations * under the License. * ****************************************************************************/ /**************************************************************************** * Included Files ****************************************************************************/ #include /**************************************************************************** * Pre-processor Definitions ****************************************************************************/ #define VECTOR2D_SATURATE_MAG_MIN (1e-10f) #define FAST_ATAN2_SMALLNUM (1e-10f) /**************************************************************************** * Public Functions ****************************************************************************/ /**************************************************************************** * Name: f_saturate * * Description: * Saturate float number * * Input Parameters: * val - pointer to float number * min - lower limit * max - upper limit * * Returned Value: * None * ****************************************************************************/ void f_saturate(FAR float *val, float min, float max) { if (*val < min) { *val = min; } else if (*val > max) { *val = max; } } /**************************************************************************** * Name: vector2d_mag * * Description: * Get 2D vector magnitude. * * Input Parameters: * x - (in) vector x component * y - (in) vector y component * * Returned Value: * Return 2D vector magnitude * ****************************************************************************/ float vector2d_mag(float x, float y) { return sqrtf(x * x + y * y); } /**************************************************************************** * Name: vector2d_saturate * * Description: * Saturate 2D vector magnitude. * * Input Parameters: * x - (in/out) pointer to the vector x component * y - (in/out) pointer to the vector y component * max - (in) maximum vector magnitude * * Returned Value: * None * ****************************************************************************/ void vector2d_saturate(FAR float *x, FAR float *y, float max) { float mag = 0.0f; float tmp = 0.0f; /* Get vector magnitude */ mag = vector2d_mag(*x, *y); if (mag < VECTOR2D_SATURATE_MAG_MIN) { mag = VECTOR2D_SATURATE_MAG_MIN; } if (mag > max) { /* Saturate vector */ tmp = max / mag; *x *= tmp; *y *= tmp; } } /**************************************************************************** * Name: dq_mag * * Description: * Get DQ vector magnitude. * * Input Parameters: * dq - (in/out) dq frame vector * * Returned Value: * Return dq vector magnitude * ****************************************************************************/ float dq_mag(FAR dq_frame_f32_t *dq) { return vector2d_mag(dq->d, dq->q); } /**************************************************************************** * Name: dq_saturate * * Description: * Saturate dq frame vector magnitude. * * Input Parameters: * dq - (in/out) dq frame vector * max - (in) maximum vector magnitude * * Returned Value: * None * ****************************************************************************/ void dq_saturate(FAR dq_frame_f32_t *dq, float max) { vector2d_saturate(&dq->d, &dq->q, max); } /**************************************************************************** * Name: fast_sin * * Description: * Fast sin calculation * * Input Parameters: * angle - (in) * * Returned Value: * Return estimated sine value * ****************************************************************************/ float fast_sin(float angle) { float sin = 0.0f; float n1 = 1.27323954f; float n2 = 0.405284735f; /* Normalize angle */ angle_norm_2pi(&angle, -M_PI_F, M_PI_F); /* Get estiamte sine value from quadratic equation */ if (angle < 0.0f) { sin = n1 * angle + n2 * angle * angle; } else { sin = n1 * angle - n2 * angle * angle; } return sin; } /**************************************************************************** * Name:fast_cos * * Description: * Fast cos calculation * * Input Parameters: * angle - (in) * * Returned Value: * Return estimated cosine value * ****************************************************************************/ float fast_cos(float angle) { /* Get cosine value from sine sin(x + PI/2) = cos(x) */ return fast_sin(angle + M_PI_2_F); } /**************************************************************************** * Name: fast_sin2 * * Description: * Fast sin calculation with better accuracy (quadratic curve * approximation) * * Input Parameters: * angle - (in) * * Returned Value: * Return estimated sine value * ****************************************************************************/ float fast_sin2(float angle) { float sin = 0.0f; float n1 = 1.27323954f; float n2 = 0.405284735f; float n3 = 0.225f; /* Normalize angle */ angle_norm_2pi(&angle, -M_PI_F, M_PI_F); /* Get estiamte sine value from quadratic equation and do more */ if (angle < 0.0f) { sin = n1 * angle + n2 * angle * angle; if (sin < 0.0f) { sin = n3 * (sin *(-sin) - sin) + sin; } else { sin = n3 * (sin * sin - sin) + sin; } } else { sin = n1 * angle - n2 * angle * angle; if (sin < 0.0f) { sin = n3 * (sin *(-sin) - sin) + sin; } else { sin = n3 * (sin * sin - sin) + sin; } } return sin; } /**************************************************************************** * Name:fast_cos2 * * Description: * Fast cos calculation with better accuracy (quadratic curve * approximation) * * Input Parameters: * angle - (in) * * Returned Value: * Return estimated cosine value * ****************************************************************************/ float fast_cos2(float angle) { /* Get cosine value from sine sin(x + PI/2) = cos(x) */ return fast_sin2(angle + M_PI_2_F); } /**************************************************************************** * Name: fast_atan2 * * Description: * Fast atan2 calculation * * REFERENCE: * https://dspguru.com/dsp/tricks/fixed-point-atan2-with-self-normalization/ * * Input Parameters: * x - (in) * y - (in) * * Returned Value: * Return estimated angle * ****************************************************************************/ float fast_atan2(float y, float x) { float angle = 0.0f; float abs_y = 0.0f; float rsq = 0.0f; float r = 0.0f; float n1 = 0.1963f; float n2 = 0.9817f; /* Get absolute value of y and add some small number to prevent 0/0 */ abs_y = fabsf(y) + FAST_ATAN2_SMALLNUM; /* Calculate angle */ if (x >= 0.0f) { r = (x - abs_y) / (x + abs_y); rsq = r * r; angle = ((n1 * rsq) - n2) * r + (M_PI_F / 4.0f); } else { r = (x + abs_y) / (abs_y - x); rsq = r * r; angle = ((n1 * rsq) - n2) * r + (3.0f * M_PI_F / 4.0f); } /* Get angle sign */ if (y < 0.0f) { angle = -angle; } return angle; } /**************************************************************************** * Name: angle_norm * * Description: * Normalize radians angle to a given boundary and a given period. * * Input Parameters: * angle - (in/out) pointer to the angle data * per - (in) angle period * bottom - (in) lower limit * top - (in) upper limit * * Returned Value: * None * ****************************************************************************/ void angle_norm(FAR float *angle, float per, float bottom, float top) { while (*angle > top) { /* Move the angle backwards by given period */ *angle = *angle - per; } while (*angle < bottom) { /* Move the angle forwards by given period */ *angle = *angle + per; } } /**************************************************************************** * Name: angle_norm_2pi * * Description: * Normalize radians angle with period 2*PI to a given boundary. * * Input Parameters: * angle - (in/out) pointer to the angle data * bottom - (in) lower limit * top - (in) upper limit * * Returned Value: * None * ****************************************************************************/ void angle_norm_2pi(FAR float *angle, float bottom, float top) { angle_norm(angle, 2.0f*M_PI_F, bottom, top); } /**************************************************************************** * Name: phase_angle_update * * Description: * Update phase_angle_s structure: * 1. normalize angle value to <0.0, 2PI> range * 2. update angle value * 3. update sin/cos value for given angle * * Input Parameters: * angle - (in/out) pointer to the angle data * val - (in) angle radian value * * Returned Value: * None * ****************************************************************************/ void phase_angle_update(FAR struct phase_angle_f32_s *angle, float val) { LIBDSP_DEBUGASSERT(angle != NULL); /* Normalize angle to <0.0, 2PI> */ angle_norm_2pi(&val, 0.0f, 2.0f*M_PI_F); /* Update structure */ angle->angle = val; #if CONFIG_LIBDSP_PRECISION == 1 angle->sin = fast_sin2(val); angle->cos = fast_cos2(val); #elif CONFIG_LIBDSP_PRECISION == 2 angle->sin = sin(val); angle->cos = cos(val); #else angle->sin = fast_sin(val); angle->cos = fast_cos(val); #endif }