/**************************************************************************** * libs/libdsp/lib_misc_b16.c * * 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 (1) #define FAST_ATAN2_SMALLNUM (1) #ifndef ABS # define ABS(a) (a < 0 ? -a : a) #endif /**************************************************************************** * Public Functions ****************************************************************************/ /**************************************************************************** * Name: f_saturate_b16 * * Description: * Saturate b16_t number * * Input Parameters: * val - pointer to b16_t number * min - lower limit * max - upper limit * * Returned Value: * None * ****************************************************************************/ void f_saturate_b16(FAR b16_t *val, b16_t min, b16_t max) { if (*val < min) { *val = min; } else if (*val > max) { *val = max; } } /**************************************************************************** * Name: vector2d_mag_b16 * * Description: * Get 2D vector magnitude. * * Input Parameters: * x - (in) vector x component * y - (in) vector y component * * Returned Value: * Return 2D vector magnitude * ****************************************************************************/ b16_t vector2d_mag_b16(b16_t x, b16_t y) { b16_t t0 = 0; b16_t t1 = 0; b16_t t3 = 0; t0 = b16sqr(x); t1 = b16sqr(y); t3 = t0 + t1; /* TODO: move to fixedmath sqrt */ if (t3 == 0) { return 0; } #if CONFIG_LIBDSP_PRECISION == 0 /* Use ub8 sqrt */ return ub8toub16(ub16sqrtub8(t3)); #else /* Too slow ! */ return ub16sqrtub16(t3); #endif } /**************************************************************************** * Name: vector2d_saturate_b16 * * 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_b16(FAR b16_t *x, FAR b16_t *y, b16_t max) { b16_t mag = 0; b16_t tmp = 0; /* Get vector magnitude */ mag = vector2d_mag_b16(*x, *y); if (mag < VECTOR2D_SATURATE_MAG_MIN) { mag = VECTOR2D_SATURATE_MAG_MIN; } if (mag > max) { /* Saturate vector */ tmp = b16divb16(max, mag); *x = b16mulb16(*x, tmp); *y = b16mulb16(*x, tmp); } } /**************************************************************************** * Name: dq_mag_b16 * * Description: * Get DQ vector magnitude. * * Input Parameters: * dq - (in/out) dq frame vector * * Returned Value: * Return dq vector magnitude * ****************************************************************************/ b16_t dq_mag_b16(FAR dq_frame_b16_t *dq) { return vector2d_mag_b16(dq->d, dq->q); } /**************************************************************************** * Name: dq_saturate_b16 * * 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_b16(FAR dq_frame_b16_t *dq, b16_t max) { vector2d_saturate_b16(&dq->d, &dq->q, max); } /**************************************************************************** * Name: fast_sin_b16 * * Description: * Fast sin calculation * * Input Parameters: * angle - (in) * * Returned Value: * Return estimated sine value * ****************************************************************************/ b16_t fast_sin_b16(b16_t angle) { b16_t sin = 0; b16_t n1 = ftob16(1.27323954f); b16_t n2 = ftob16(0.405284735f); b16_t tmp1 = 0; b16_t tmp2 = 0; /* Normalize angle */ angle_norm_2pi_b16(&angle, -b16PI, b16PI); /* Get estiamte sine value from quadratic equation */ if (angle < 0) { tmp1 = b16mulb16(n1, angle); tmp2 = b16mulb16(n2, b16sqr(angle)); sin = tmp1 + tmp2; } else { tmp1 = b16mulb16(n1, angle); tmp2 = b16mulb16(n2, b16sqr(angle)); sin = tmp1 - tmp2; } return sin; } /**************************************************************************** * Name:fast_cos_b16 * * Description: * Fast cos calculation * * Input Parameters: * angle - (in) * * Returned Value: * Return estimated cosine value * ****************************************************************************/ b16_t fast_cos_b16(b16_t angle) { /* Get cosine value from sine sin(x + PI/2) = cos(x) */ return fast_sin_b16(angle + b16HALFPI); } /**************************************************************************** * Name: fast_sin2_b16 * * Description: * Fast sin calculation with better accuracy (quadratic curve * approximation) * * Input Parameters: * angle - (in) * * Returned Value: * Return estimated sine value * ****************************************************************************/ b16_t fast_sin2_b16(b16_t angle) { return b16sin(angle); } /**************************************************************************** * Name:fast_cos2_b16 * * Description: * Fast cos calculation with better accuracy (quadratic curve * approximation) * * Input Parameters: * angle - (in) * * Returned Value: * Return estimated cosine value * ****************************************************************************/ b16_t fast_cos2_b16(b16_t angle) { return b16cos(angle); } /**************************************************************************** * Name: fast_atan2_b16 * * 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 * ****************************************************************************/ b16_t fast_atan2_b16(b16_t y, b16_t x) { b16_t angle = 0; b16_t abs_y = 0; b16_t rsq = 0; b16_t r = 0; b16_t n1 = ftob16(0.1963f); b16_t n2 = ftob16(0.9817f); b16_t tmp1 = 0; b16_t tmp2 = 0; b16_t tmp3 = 0; /* Get absolute value of y and add some small number to prevent 0/0 */ abs_y = ABS(y) + FAST_ATAN2_SMALLNUM; /* Calculate angle */ if (x >= 0) { r = b16divb16((x - abs_y), (x + abs_y)); rsq = b16mulb16(r, r); tmp1 = b16mulb16(n1, rsq); tmp2 = b16mulb16((tmp1 - n2), r); tmp3 = b16mulb16(b16PI, ftob16(0.25f)); angle = tmp2 + tmp3; } else { r = b16divb16((x + abs_y), (abs_y - x)); rsq = b16mulb16(r, r); tmp1 = b16mulb16(n1, rsq); tmp2 = b16mulb16((tmp1 - n2), r); tmp3 = b16mulb16(b16PI, ftob16(0.75f)); angle = tmp2 + tmp3; } /* Get angle sign */ if (y < 0) { angle = -angle; } return angle; } /**************************************************************************** * Name: angle_norm_b16 * * 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_b16(FAR b16_t *angle, b16_t per, b16_t bottom, b16_t 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_b16 * * 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_b16(FAR b16_t *angle, b16_t bottom, b16_t top) { angle_norm_b16(angle, b16TWOPI, bottom, top); } /**************************************************************************** * Name: phase_angle_update_b16 * * 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_b16(FAR struct phase_angle_b16_s *angle, b16_t val) { LIBDSP_DEBUGASSERT(angle != NULL); /* Normalize angle to <0.0, 2PI> */ angle_norm_2pi_b16(&val, 0, b16TWOPI); /* Update structure */ angle->angle = val; #if CONFIG_LIBDSP_PRECISION == 0 angle->sin = fast_sin_b16(val); angle->cos = fast_cos_b16(val); #else angle->sin = fast_sin2_b16(val); angle->cos = fast_cos2_b16(val); #endif }