/**************************************************************************** * examples/dsptest/test_foc.c * * Copyright (C) 2018 Gregory Nutt. All rights reserved. * Author: Mateusz Szafoni * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in * the documentation and/or other materials provided with the * distribution. * 3. Neither the name NuttX nor the names of its contributors may be * used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. * ****************************************************************************/ /**************************************************************************** * Included Files ****************************************************************************/ #include "dsptest.h" /**************************************************************************** * Pre-processor Definitions ****************************************************************************/ /* Set float precision for this module */ #undef UNITY_FLOAT_PRECISION #if CONFIG_LIBDSP_PRECISION == 1 # define UNITY_FLOAT_PRECISION (0.1f) #elif CONFIG_LIBDSP_PRECISION == 2 # define UNITY_FLOAT_PRECISION (0.001f) #else # define UNITY_FLOAT_PRECISION (0.999f) #endif /* Value close enough to zero */ #define TEST_ASSERT_EQUAL_FLOAT_ZERO(a) \ TEST_ASSERT_FLOAT_WITHIN(1e-6, 0.0, a); /**************************************************************************** * Private Types ****************************************************************************/ /**************************************************************************** * Private Function Protototypes ****************************************************************************/ /**************************************************************************** * Private Data ****************************************************************************/ /**************************************************************************** * Private Functions ****************************************************************************/ /* Initialize FOC data */ static void test_foc_init(void) { struct foc_data_s foc; float id_kp = 1.0; float id_ki = 2.0; float iq_kp = 3.0; float iq_ki = 4.0; foc_init(&foc, id_kp, id_ki, iq_kp, iq_ki); TEST_ASSERT_EQUAL_FLOAT(1.0, foc.id_pid.KP); TEST_ASSERT_EQUAL_FLOAT(2.0, foc.id_pid.KI); TEST_ASSERT_EQUAL_FLOAT(0.0, foc.id_pid.KD); TEST_ASSERT_EQUAL_FLOAT(3.0, foc.iq_pid.KP); TEST_ASSERT_EQUAL_FLOAT(4.0, foc.iq_pid.KI); TEST_ASSERT_EQUAL_FLOAT(0.0, foc.iq_pid.KD); foc_idq_ref_set(&foc, 1.0, 2.0); TEST_ASSERT_EQUAL_FLOAT(1.0, foc.i_dq_ref.d); TEST_ASSERT_EQUAL_FLOAT(2.0, foc.i_dq_ref.q); foc_vbase_update(&foc, 0.1); TEST_ASSERT_EQUAL_FLOAT(0.1, 1.0 / foc.vab_mod_scale); TEST_ASSERT_EQUAL_FLOAT(0.1, foc.vdq_mag_max); } /* Feed FOC with zeros */ static void test_foc_process_zeros(void) { struct foc_data_s foc; float id_kp = 1.00; float id_ki = 0.01; float iq_kp = 1.00; float iq_ki = 0.01; abc_frame_t i_abc; phase_angle_t angle; /* Initialize FOC */ foc_init(&foc, id_kp, id_ki, iq_kp, iq_ki); /* Input: * abc = (0.0, 0.0, 0.0) * i_dq_ref = (1.0, 1.0) * vab_scale = 1.0 * vdq_mag_max = 1.0 * angle = 0.0 * * Expected: * i_ab = (0.0, 0.0) * i_dq = (0.0, 0.0) * v_dq = (0.0, 0.0) * v_ab = (0.0, 0.0) * v_ab_mod = (0.0, 0.0) */ i_abc.a = 0.0; i_abc.b = 0.0; i_abc.c = 0.0; phase_angle_update(&angle, 0.0); foc_idq_ref_set(&foc, 0.0, 0.0); foc_vbase_update(&foc, 1.0); foc_process(&foc, &i_abc, &angle); TEST_ASSERT_EQUAL_FLOAT_ZERO(foc.v_ab.a); TEST_ASSERT_EQUAL_FLOAT_ZERO(foc.v_ab.b); TEST_ASSERT_EQUAL_FLOAT_ZERO(foc.v_ab_mod.a); TEST_ASSERT_EQUAL_FLOAT_ZERO(foc.v_ab_mod.b); /* Input: * abc = (1.0, -0.5, -0.5) * i_dq_ref = (1.0, 0.0) * vab_scale = 1.0 * vdq_mag_max = 1.0 * angle = 0.0 * * Expected: * i_ab = (1.0, 0.0) * i_dq = (1.0, 0.0) * v_dq = (0.0, 0.0) * v_ab = (0.0, 0.0) * v_ab_mod = (0.0, 0.0) */ i_abc.a = 1.0; i_abc.b = -0.5; i_abc.c = -0.5; foc_idq_ref_set(&foc, 1.0, 0.0); foc_vbase_update(&foc, 1.0); phase_angle_update(&angle, 0.0); foc_process(&foc, &i_abc, &angle); TEST_ASSERT_EQUAL_FLOAT_ZERO(foc.v_ab.a); TEST_ASSERT_EQUAL_FLOAT_ZERO(foc.v_ab.b); TEST_ASSERT_EQUAL_FLOAT_ZERO(foc.v_ab_mod.a); TEST_ASSERT_EQUAL_FLOAT_ZERO(foc.v_ab_mod.b); /* Input: * abc = (-1.0, 0.5, 0.5) * i_dq_ref = (-1.0, 0.0) * vab_scale = 1.0 * vdq_mag_max = 1.0 * angle = 0.0 * * Expected: * i_ab = (-1.0, 0.0) * i_dq = (-1.0, 0.0) * v_dq = (0.0, 0.0) * v_ab = (0.0, 0.0) * v_ab_mod = (0.0, 0.0) */ i_abc.a = -1.0; i_abc.b = 0.5; i_abc.c = 0.5; foc_idq_ref_set(&foc, -1.0, 0.0); foc_vbase_update(&foc, 1.0); phase_angle_update(&angle, 0.0); foc_process(&foc, &i_abc, &angle); TEST_ASSERT_EQUAL_FLOAT_ZERO(foc.v_ab.a); TEST_ASSERT_EQUAL_FLOAT_ZERO(foc.v_ab.b); TEST_ASSERT_EQUAL_FLOAT_ZERO(foc.v_ab_mod.a); TEST_ASSERT_EQUAL_FLOAT_ZERO(foc.v_ab_mod.b); } /* Process FOC with some test data */ static void test_foc_process(void) { struct foc_data_s foc; float id_kp = 1.00; float id_ki = 0.00; float iq_kp = 1.00; float iq_ki = 0.00; abc_frame_t i_abc; phase_angle_t angle; float i_d_ref = 0.0; float i_q_ref = 0.0; /* Initialize FOC. * For simplicity KP = 1.0, KI = 0.0 */ foc_init(&foc, id_kp, id_ki, iq_kp, iq_ki); /* Input: * abc = (1.0, 1.0, -2.0) * i_dq_ref = (2.0, 2.0) * vab_scale = 0.1 * vdq_mag_max = 10.0 * angle = PI * * Expected: * i_ab = (1.0, 1.7320) * i_dq = (-1.0, -1.7320) * v_dq = (3.0, 3.7320) * v_ab = (0.30, 0.37320) * v_ab_mod = (0.30, 0.37320) */ i_abc.a = 1.0; i_abc.b = 1.0; i_abc.c = -2.0; i_d_ref = 2.0; i_q_ref = 2.0; phase_angle_update(&angle, M_PI_F); foc_idq_ref_set(&foc, i_d_ref, i_q_ref); foc_vbase_update(&foc, 10.0); foc_process(&foc, &i_abc, &angle); TEST_ASSERT_EQUAL_FLOAT(1.0 , foc.i_ab.a); TEST_ASSERT_EQUAL_FLOAT(1.7320 , foc.i_ab.b); TEST_ASSERT_EQUAL_FLOAT(-1.0 , foc.i_dq.d); TEST_ASSERT_EQUAL_FLOAT(-1.7320, foc.i_dq.q); TEST_ASSERT_EQUAL_FLOAT(3.0 , foc.v_dq.d); TEST_ASSERT_EQUAL_FLOAT(3.7320 , foc.v_dq.q); TEST_ASSERT_EQUAL_FLOAT(-3.0 , foc.v_ab.a); TEST_ASSERT_EQUAL_FLOAT(-3.7320, foc.v_ab.b); TEST_ASSERT_EQUAL_FLOAT(-0.3 , foc.v_ab_mod.a); TEST_ASSERT_EQUAL_FLOAT(-0.3732, foc.v_ab_mod.b); /* Input: * abc = (1.0, 1.0, -2.0) * i_dq_ref = (2.0, 2.0) * vab_scale = 0.1 * vdq_mag_max = 10 * angle = -PI * * Expected: * i_ab = (1.0, 1.7320) * i_dq = (-1.0, -1.7320) * v_dq = (3.0, 3.7320) * v_ab = (-0.30, -0.37320) * v_ab_mod = (-0.30, -0.37320) */ i_abc.a = 1.0; i_abc.b = 1.0; i_abc.c = -2.0; i_d_ref = 2.0; i_q_ref = 2.0; phase_angle_update(&angle, -M_PI_F); foc_idq_ref_set(&foc, i_d_ref, i_q_ref); foc_vbase_update(&foc, 10.0); foc_process(&foc, &i_abc, &angle); TEST_ASSERT_EQUAL_FLOAT(1.0 , foc.i_ab.a); TEST_ASSERT_EQUAL_FLOAT(1.7320 , foc.i_ab.b); TEST_ASSERT_EQUAL_FLOAT(-1.0 , foc.i_dq.d); TEST_ASSERT_EQUAL_FLOAT(-1.7320, foc.i_dq.q); TEST_ASSERT_EQUAL_FLOAT(3.0 , foc.v_dq.d); TEST_ASSERT_EQUAL_FLOAT(3.7320 , foc.v_dq.q); TEST_ASSERT_EQUAL_FLOAT(-3.0 , foc.v_ab.a); TEST_ASSERT_EQUAL_FLOAT(-3.7320, foc.v_ab.b); TEST_ASSERT_EQUAL_FLOAT(-0.30 , foc.v_ab_mod.a); TEST_ASSERT_EQUAL_FLOAT(-0.3732, foc.v_ab_mod.b); /* Input: * abc = (1.0, 1.0, -2.0) * i_dq_ref = (2.0, 2.0) * vab_scale = 0.1 * vdq_mag_max = 10 * angle = 0.1 * * Expected: * i_ab = (1.0, 1.7320) * i_dq = (1.1679, 1.6236) * v_dq = (0.8321, 0.3764) * v_ab = (0.7903, 0.4576) * v_ab_mod = (0.07903, 0.04576) */ i_abc.a = 1.0; i_abc.b = 1.0; i_abc.c = -2.0; i_d_ref = 2.0; i_q_ref = 2.0; phase_angle_update(&angle, 0.1); foc_idq_ref_set(&foc, i_d_ref, i_q_ref); foc_vbase_update(&foc, 10.0); foc_process(&foc, &i_abc, &angle); TEST_ASSERT_EQUAL_FLOAT(1.0 , foc.i_ab.a); TEST_ASSERT_EQUAL_FLOAT(1.7320 , foc.i_ab.b); TEST_ASSERT_EQUAL_FLOAT(1.1679 , foc.i_dq.d); TEST_ASSERT_EQUAL_FLOAT(1.6236 , foc.i_dq.q); TEST_ASSERT_EQUAL_FLOAT(0.8321 , foc.v_dq.d); TEST_ASSERT_EQUAL_FLOAT(0.3764 , foc.v_dq.q); TEST_ASSERT_EQUAL_FLOAT(0.7903 , foc.v_ab.a); TEST_ASSERT_EQUAL_FLOAT(0.4576 , foc.v_ab.b); TEST_ASSERT_EQUAL_FLOAT(0.07903, foc.v_ab_mod.a); TEST_ASSERT_EQUAL_FLOAT(0.04576, foc.v_ab_mod.b); /* Input: * abc = (1.0, 1.0, -2.0) * i_dq_ref = (2.0, 2.0) * vab_scale = 0.1 * vdq_mag_max = 10.0 * angle = -0.1 * * Expected: * i_ab = (1.0 , 1.7320) * i_dq = (0.8221, 1.8232) * v_dq = (1.1779, 0.1768) * v_ab = (0.11897, 0.00583) * v_ab_mod = (0.11897, 0.00583) */ i_abc.a = 1.0; i_abc.b = 1.0; i_abc.c = -2.0; i_d_ref = 2.0; i_q_ref = 2.0; phase_angle_update(&angle, -0.1); foc_idq_ref_set(&foc, i_d_ref, i_q_ref); foc_vbase_update(&foc, 10.0); foc_process(&foc, &i_abc, &angle); TEST_ASSERT_EQUAL_FLOAT(1.0, foc.i_ab.a); TEST_ASSERT_EQUAL_FLOAT(1.7320, foc.i_ab.b); TEST_ASSERT_EQUAL_FLOAT(0.8221, foc.i_dq.d); TEST_ASSERT_EQUAL_FLOAT(1.8232, foc.i_dq.q); TEST_ASSERT_EQUAL_FLOAT(1.1779, foc.v_dq.d); TEST_ASSERT_EQUAL_FLOAT(0.1768, foc.v_dq.q); TEST_ASSERT_EQUAL_FLOAT(1.1897, foc.v_ab.a); TEST_ASSERT_EQUAL_FLOAT(0.0583, foc.v_ab.b); TEST_ASSERT_EQUAL_FLOAT(0.11897, foc.v_ab_mod.a); TEST_ASSERT_EQUAL_FLOAT(0.00583, foc.v_ab_mod.b); /* Input: * abc = (1.0, 1.0, -2.0) * i_dq_ref = (-2.0, 0.0) * vab_scale = 0.1 * vdq_mag_max = 10.0 * angle = 0.1 * * Expected: * i_ab = (1.0, 1.7320) * i_dq = (1.1679, 1.6236) * v_dq = (-3.1679, -1.6236) * v_ab = (-0.29900, -0.19317) * v_ab_mod = (-0.29900, -0.19317) */ i_abc.a = 1.0; i_abc.b = 1.0; i_abc.c = -2.0; i_d_ref = -2.0; i_q_ref = 0.0; phase_angle_update(&angle, 0.1); foc_idq_ref_set(&foc, i_d_ref, i_q_ref); foc_vbase_update(&foc, 10.0); foc_process(&foc, &i_abc, &angle); TEST_ASSERT_EQUAL_FLOAT(1.0 , foc.i_ab.a); TEST_ASSERT_EQUAL_FLOAT(1.7320 , foc.i_ab.b); TEST_ASSERT_EQUAL_FLOAT(1.1679 , foc.i_dq.d); TEST_ASSERT_EQUAL_FLOAT(1.6236 , foc.i_dq.q); TEST_ASSERT_EQUAL_FLOAT(-3.1679 , foc.v_dq.d); TEST_ASSERT_EQUAL_FLOAT(-1.6236 , foc.v_dq.q); TEST_ASSERT_EQUAL_FLOAT(-2.9900 , foc.v_ab.a); TEST_ASSERT_EQUAL_FLOAT(-1.9317 , foc.v_ab.b); TEST_ASSERT_EQUAL_FLOAT(-0.29900 , foc.v_ab_mod.a); TEST_ASSERT_EQUAL_FLOAT(-0.19317 , foc.v_ab_mod.b); /* Input: * abc = (1.0, 1.0, -2.0) * i_dq_ref = (-2.0, -2.0) * vab_scale = 0.1 * vdq_mag_max = 10.0 * angle = 0.1 * * Expected: * i_ab = (1.0, 1.7320) * i_dq = (1.1679, 1.6236) * v_dq = (-3.1679, -3.6236) * v_ab = (-0.27903, -0.39217) * v_ab_mod = (-0.27903, -0.39217) */ i_abc.a = 1.0; i_abc.b = 1.0; i_abc.c = -2.0; i_d_ref = -2.0; i_q_ref = -2.0; phase_angle_update(&angle, 0.1); foc_idq_ref_set(&foc, i_d_ref, i_q_ref); foc_vbase_update(&foc, 10.0); foc_process(&foc, &i_abc, &angle); TEST_ASSERT_EQUAL_FLOAT(1.0 , foc.i_ab.a); TEST_ASSERT_EQUAL_FLOAT(1.7320 , foc.i_ab.b); TEST_ASSERT_EQUAL_FLOAT(1.1679 , foc.i_dq.d); TEST_ASSERT_EQUAL_FLOAT(1.6236 , foc.i_dq.q); TEST_ASSERT_EQUAL_FLOAT(-3.1679 , foc.v_dq.d); TEST_ASSERT_EQUAL_FLOAT(-3.6236 , foc.v_dq.q); TEST_ASSERT_EQUAL_FLOAT(-2.7903 , foc.v_ab.a); TEST_ASSERT_EQUAL_FLOAT(-3.9217 , foc.v_ab.b); TEST_ASSERT_EQUAL_FLOAT(-0.27903, foc.v_ab_mod.a); TEST_ASSERT_EQUAL_FLOAT(-0.39217, foc.v_ab_mod.b); } /* Test FOC saturation */ static void test_foc_saturation(void) { TEST_FAIL_MESSAGE("not implemented"); } /* Test FOC vdq modulation */ static void test_foc_vdq_modulation(void) { TEST_FAIL_MESSAGE("not implemented"); } /**************************************************************************** * Public Functions ****************************************************************************/ /**************************************************************************** * Name: test_foc ****************************************************************************/ void test_foc(void) { UNITY_BEGIN(); TEST_SEPARATOR(); /* Test FOC */ RUN_TEST(test_foc_init); RUN_TEST(test_foc_process_zeros); RUN_TEST(test_foc_process); RUN_TEST(test_foc_saturation); RUN_TEST(test_foc_vdq_modulation); UNITY_END(); }