diff --git a/CMakeLists.txt b/CMakeLists.txt index 345a112..3106d51 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,50 +1,11 @@ -cmake_minimum_required (VERSION 3.5.1) +cmake_minimum_required (VERSION 3.0.0) project (OpenDiscon) -# OpenDiscon include directories -set (OPENDISCON_INCLUDE_DIRS ${OPENDISCON_INCLUDE_DIRS} ${PROJECT_SOURCE_DIR}/src/ikClwindconWTCon/) -set (OPENDISCON_INCLUDE_DIRS ${OPENDISCON_INCLUDE_DIRS} ${PROJECT_SOURCE_DIR}/src/ikClwindconWTConfig/) -set (OPENDISCON_INCLUDE_DIRS ${OPENDISCON_INCLUDE_DIRS} ${PROJECT_SOURCE_DIR}/src/ikTpman/) -set (OPENDISCON_INCLUDE_DIRS ${OPENDISCON_INCLUDE_DIRS} ${PROJECT_SOURCE_DIR}/src/ikPowman/) +# choose a configuration +set( CONFIGURATION "CL-Windcon" CACHE STRING +"Choose a configuration.\n\ +Available configurations are:\n\ +CL-Windcon" ) -# OpenDiscon source files -set (OPENDISCON_SOURCES ${OPENDISCON_SOURCES} ${PROJECT_SOURCE_DIR}/src/ikTpman/ikTpman.c) -set (OPENDISCON_SOURCES ${OPENDISCON_SOURCES} ${PROJECT_SOURCE_DIR}/src/ikPowman/ikPowman.c) -set (OPENDISCON_SOURCES ${OPENDISCON_SOURCES} ${PROJECT_SOURCE_DIR}/src/ikClwindconWTConfig/ikClwindconWTConfig.c) -set (OPENDISCON_SOURCES ${OPENDISCON_SOURCES} ${PROJECT_SOURCE_DIR}/src/ikClwindconWTCon/ikClwindconWTCon.c) -set (OPENDISCON_SOURCES ${OPENDISCON_SOURCES} ${PROJECT_SOURCE_DIR}/src/discon/discon.c) - -# OpenWitcon include directories -set (OPENWITCON_INCLUDE_DIRS ${OPENWITCON_INCLUDE_DIRS} ${PROJECT_SOURCE_DIR}/OpenWitcon/src/ikConLoop) -set (OPENWITCON_INCLUDE_DIRS ${OPENWITCON_INCLUDE_DIRS} ${PROJECT_SOURCE_DIR}/OpenWitcon/src/ikLinCon) -set (OPENWITCON_INCLUDE_DIRS ${OPENWITCON_INCLUDE_DIRS} ${PROJECT_SOURCE_DIR}/OpenWitcon/src/ikLutbl) -set (OPENWITCON_INCLUDE_DIRS ${OPENWITCON_INCLUDE_DIRS} ${PROJECT_SOURCE_DIR}/OpenWitcon/src/ikNotchList) -set (OPENWITCON_INCLUDE_DIRS ${OPENWITCON_INCLUDE_DIRS} ${PROJECT_SOURCE_DIR}/OpenWitcon/src/ikRegionSelector) -set (OPENWITCON_INCLUDE_DIRS ${OPENWITCON_INCLUDE_DIRS} ${PROJECT_SOURCE_DIR}/OpenWitcon/src/ikSlti) -set (OPENWITCON_INCLUDE_DIRS ${OPENWITCON_INCLUDE_DIRS} ${PROJECT_SOURCE_DIR}/OpenWitcon/src/ikStpgen) -set (OPENWITCON_INCLUDE_DIRS ${OPENWITCON_INCLUDE_DIRS} ${PROJECT_SOURCE_DIR}/OpenWitcon/src/ikTfList) -set (OPENWITCON_INCLUDE_DIRS ${OPENWITCON_INCLUDE_DIRS} ${PROJECT_SOURCE_DIR}/OpenWitcon/src/ikVfnotch) - -# OpenWitcon source files -set (OPENDISCON_SOURCES ${OPENDISCON_SOURCES} ${PROJECT_SOURCE_DIR}/OpenWitcon/src/ikConLoop/ikConLoop.c) -set (OPENDISCON_SOURCES ${OPENDISCON_SOURCES} ${PROJECT_SOURCE_DIR}/OpenWitcon/src/ikLinCon/ikLinCon.c) -set (OPENDISCON_SOURCES ${OPENDISCON_SOURCES} ${PROJECT_SOURCE_DIR}/OpenWitcon/src/ikLutbl/ikLutbl.c) -set (OPENDISCON_SOURCES ${OPENDISCON_SOURCES} ${PROJECT_SOURCE_DIR}/OpenWitcon/src/ikNotchList/ikNotchList.c) -set (OPENDISCON_SOURCES ${OPENDISCON_SOURCES} ${PROJECT_SOURCE_DIR}/OpenWitcon/src/ikRegionSelector/ikRegionSelector.c) -set (OPENDISCON_SOURCES ${OPENDISCON_SOURCES} ${PROJECT_SOURCE_DIR}/OpenWitcon/src/ikSlti/ikSlti.c) -set (OPENDISCON_SOURCES ${OPENDISCON_SOURCES} ${PROJECT_SOURCE_DIR}/OpenWitcon/src/ikStpgen/ikStpgen.c) -set (OPENDISCON_SOURCES ${OPENDISCON_SOURCES} ${PROJECT_SOURCE_DIR}/OpenWitcon/src/ikTfList/ikTfList.c) -set (OPENDISCON_SOURCES ${OPENDISCON_SOURCES} ${PROJECT_SOURCE_DIR}/OpenWitcon/src/ikVfnotch/ikVfnotch.c) - -# shared OpenDiscon library -include_directories ("${OPENDISCON_INCLUDE_DIRS}") -include_directories ("${OPENWITCON_INCLUDE_DIRS}") -include_directories ("${PROJECT_BINARY_DIR}") -include (GenerateExportHeader) -add_library (OpenDiscon SHARED ${OPENDISCON_SOURCES}) -GENERATE_EXPORT_HEADER (OpenDiscon - BASE_NAME OpenDiscon - EXPORT_MACRO_NAME OpenDiscon_EXPORT - EXPORT_FILE_NAME OpenDiscon_EXPORT.h - STATIC_DEFINE OpenDiscon_BUILT_AS_STATIC -) +# run configuration-specific cmake script +include( ${PROJECT_SOURCE_DIR}/CONFIGURATION/${CONFIGURATION}/CMakeLists.txt ) diff --git a/CONFIGURATION/CL-Windcon/CMakeLists.txt b/CONFIGURATION/CL-Windcon/CMakeLists.txt new file mode 100644 index 0000000..0fb7937 --- /dev/null +++ b/CONFIGURATION/CL-Windcon/CMakeLists.txt @@ -0,0 +1,55 @@ +# choose a distribution +set( DISTRIBUTION "DISCON" CACHE STRING +"Choose a distribution.\n\ +Available distributions are:\n\ +DISCON\n\ +S-Function" ) + +# OpenDiscon include directories +set (OPENDISCON_INCLUDE_DIRS ${OPENDISCON_INCLUDE_DIRS} ${PROJECT_SOURCE_DIR}/CONFIGURATION/${CONFIGURATION}/src/ikClwindconResetSensorSignals/) +set (OPENDISCON_INCLUDE_DIRS ${OPENDISCON_INCLUDE_DIRS} ${PROJECT_SOURCE_DIR}/CONFIGURATION/${CONFIGURATION}/src/ikClwindconWTCon/) +set (OPENDISCON_INCLUDE_DIRS ${OPENDISCON_INCLUDE_DIRS} ${PROJECT_SOURCE_DIR}/CONFIGURATION/${CONFIGURATION}/src/ikClwindconWTConfig/) +set (OPENDISCON_INCLUDE_DIRS ${OPENDISCON_INCLUDE_DIRS} ${PROJECT_SOURCE_DIR}/CONFIGURATION/${CONFIGURATION}/src/ikClwindconInputMod/) +set (OPENDISCON_INCLUDE_DIRS ${OPENDISCON_INCLUDE_DIRS} ${PROJECT_SOURCE_DIR}/CONFIGURATION/${CONFIGURATION}/src/ikTpman/) +set (OPENDISCON_INCLUDE_DIRS ${OPENDISCON_INCLUDE_DIRS} ${PROJECT_SOURCE_DIR}/CONFIGURATION/${CONFIGURATION}/src/ikPowman/) +set (OPENDISCON_INCLUDE_DIRS ${OPENDISCON_INCLUDE_DIRS} ${PROJECT_SOURCE_DIR}/CONFIGURATION/${CONFIGURATION}/src/ikSpdman/) + +# OpenDiscon source files +set (OPENDISCON_SOURCES ${OPENDISCON_SOURCES} ${PROJECT_SOURCE_DIR}/CONFIGURATION/${CONFIGURATION}/src/ikSpdman/ikSpdman.c) +set (OPENDISCON_SOURCES ${OPENDISCON_SOURCES} ${PROJECT_SOURCE_DIR}/CONFIGURATION/${CONFIGURATION}/src/ikTpman/ikTpman.c) +set (OPENDISCON_SOURCES ${OPENDISCON_SOURCES} ${PROJECT_SOURCE_DIR}/CONFIGURATION/${CONFIGURATION}/src/ikPowman/ikPowman.c) +set (OPENDISCON_SOURCES ${OPENDISCON_SOURCES} ${PROJECT_SOURCE_DIR}/CONFIGURATION/${CONFIGURATION}/src/ikClwindconInputMod/ikClwindconInputMod.c) +set (OPENDISCON_SOURCES ${OPENDISCON_SOURCES} ${PROJECT_SOURCE_DIR}/CONFIGURATION/${CONFIGURATION}/src/ikClwindconWTConfig/ikClwindconWTConfig.c) +set (OPENDISCON_SOURCES ${OPENDISCON_SOURCES} ${PROJECT_SOURCE_DIR}/CONFIGURATION/${CONFIGURATION}/src/ikClwindconWTCon/ikClwindconWTCon.c) +set (OPENDISCON_SOURCES ${OPENDISCON_SOURCES} ${PROJECT_SOURCE_DIR}/CONFIGURATION/${CONFIGURATION}/src/ikClwindconResetSensorSignals/ikClwindconResetSensorSignals.c) + +# OpenWitcon include directories +set (OPENWITCON_INCLUDE_DIRS ${OPENWITCON_INCLUDE_DIRS} ${PROJECT_SOURCE_DIR}/OpenWitcon/src/ikConLoop) +set (OPENWITCON_INCLUDE_DIRS ${OPENWITCON_INCLUDE_DIRS} ${PROJECT_SOURCE_DIR}/OpenWitcon/src/ikLinCon) +set (OPENWITCON_INCLUDE_DIRS ${OPENWITCON_INCLUDE_DIRS} ${PROJECT_SOURCE_DIR}/OpenWitcon/src/ikLutbl) +set (OPENWITCON_INCLUDE_DIRS ${OPENWITCON_INCLUDE_DIRS} ${PROJECT_SOURCE_DIR}/OpenWitcon/src/ikNotchList) +set (OPENWITCON_INCLUDE_DIRS ${OPENWITCON_INCLUDE_DIRS} ${PROJECT_SOURCE_DIR}/OpenWitcon/src/ikRegionSelector) +set (OPENWITCON_INCLUDE_DIRS ${OPENWITCON_INCLUDE_DIRS} ${PROJECT_SOURCE_DIR}/OpenWitcon/src/ikSlti) +set (OPENWITCON_INCLUDE_DIRS ${OPENWITCON_INCLUDE_DIRS} ${PROJECT_SOURCE_DIR}/OpenWitcon/src/ikStpgen) +set (OPENWITCON_INCLUDE_DIRS ${OPENWITCON_INCLUDE_DIRS} ${PROJECT_SOURCE_DIR}/OpenWitcon/src/ikTfList) +set (OPENWITCON_INCLUDE_DIRS ${OPENWITCON_INCLUDE_DIRS} ${PROJECT_SOURCE_DIR}/OpenWitcon/src/ikVfnotch) +set (OPENWITCON_INCLUDE_DIRS ${OPENWITCON_INCLUDE_DIRS} ${PROJECT_SOURCE_DIR}/OpenWitcon/src/ikIpc) +set (OPENWITCON_INCLUDE_DIRS ${OPENWITCON_INCLUDE_DIRS} ${PROJECT_SOURCE_DIR}/OpenWitcon/src/ikVector) +set (OPENWITCON_INCLUDE_DIRS ${OPENWITCON_INCLUDE_DIRS} ${PROJECT_SOURCE_DIR}/OpenWitcon/src/ikSensorDiagnoser) + +# OpenWitcon source files +set (OPENDISCON_SOURCES ${OPENDISCON_SOURCES} ${PROJECT_SOURCE_DIR}/OpenWitcon/src/ikConLoop/ikConLoop.c) +set (OPENDISCON_SOURCES ${OPENDISCON_SOURCES} ${PROJECT_SOURCE_DIR}/OpenWitcon/src/ikLinCon/ikLinCon.c) +set (OPENDISCON_SOURCES ${OPENDISCON_SOURCES} ${PROJECT_SOURCE_DIR}/OpenWitcon/src/ikLutbl/ikLutbl.c) +set (OPENDISCON_SOURCES ${OPENDISCON_SOURCES} ${PROJECT_SOURCE_DIR}/OpenWitcon/src/ikNotchList/ikNotchList.c) +set (OPENDISCON_SOURCES ${OPENDISCON_SOURCES} ${PROJECT_SOURCE_DIR}/OpenWitcon/src/ikRegionSelector/ikRegionSelector.c) +set (OPENDISCON_SOURCES ${OPENDISCON_SOURCES} ${PROJECT_SOURCE_DIR}/OpenWitcon/src/ikSlti/ikSlti.c) +set (OPENDISCON_SOURCES ${OPENDISCON_SOURCES} ${PROJECT_SOURCE_DIR}/OpenWitcon/src/ikStpgen/ikStpgen.c) +set (OPENDISCON_SOURCES ${OPENDISCON_SOURCES} ${PROJECT_SOURCE_DIR}/OpenWitcon/src/ikTfList/ikTfList.c) +set (OPENDISCON_SOURCES ${OPENDISCON_SOURCES} ${PROJECT_SOURCE_DIR}/OpenWitcon/src/ikVfnotch/ikVfnotch.c) +set (OPENDISCON_SOURCES ${OPENDISCON_SOURCES} ${PROJECT_SOURCE_DIR}/OpenWitcon/src/ikIpc/ikIpc.c) +set (OPENDISCON_SOURCES ${OPENDISCON_SOURCES} ${PROJECT_SOURCE_DIR}/OpenWitcon/src/ikVector/ikVector.c) +set (OPENDISCON_SOURCES ${OPENDISCON_SOURCES} ${PROJECT_SOURCE_DIR}/OpenWitcon/src/ikSensorDiagnoser/ikSensorDiagnoser.c) + +# run distribution-specific cmake script +include( ${PROJECT_SOURCE_DIR}/CONFIGURATION/${CONFIGURATION}/DISTRIBUTION/${DISTRIBUTION}/CMakeLists.txt ) diff --git a/CONFIGURATION/CL-Windcon/DISTRIBUTION/DISCON/CMakeLists.txt b/CONFIGURATION/CL-Windcon/DISTRIBUTION/DISCON/CMakeLists.txt new file mode 100644 index 0000000..f170fdc --- /dev/null +++ b/CONFIGURATION/CL-Windcon/DISTRIBUTION/DISCON/CMakeLists.txt @@ -0,0 +1,14 @@ +set (OPENDISCON_SOURCES ${OPENDISCON_SOURCES} ${PROJECT_SOURCE_DIR}/CONFIGURATION/${CONFIGURATION}/src/discon/discon.c) + +# shared OpenDiscon library +include_directories ("${OPENDISCON_INCLUDE_DIRS}") +include_directories ("${OPENWITCON_INCLUDE_DIRS}") +include_directories ("${PROJECT_BINARY_DIR}") +include (GenerateExportHeader) +add_library (OpenDiscon SHARED ${OPENDISCON_SOURCES}) +GENERATE_EXPORT_HEADER (OpenDiscon + BASE_NAME OpenDiscon + EXPORT_MACRO_NAME OpenDiscon_EXPORT + EXPORT_FILE_NAME OpenDiscon_EXPORT.h + STATIC_DEFINE OpenDiscon_BUILT_AS_STATIC +) diff --git a/CONFIGURATION/CL-Windcon/DISTRIBUTION/S-Function/CMakeLists.txt b/CONFIGURATION/CL-Windcon/DISTRIBUTION/S-Function/CMakeLists.txt new file mode 100644 index 0000000..7fd08ca --- /dev/null +++ b/CONFIGURATION/CL-Windcon/DISTRIBUTION/S-Function/CMakeLists.txt @@ -0,0 +1,10 @@ +set (OPENDISCON_SOURCES ${OPENDISCON_SOURCES} ${PROJECT_SOURCE_DIR}/CONFIGURATION/${CONFIGURATION}/src/sfunc/sfunc.c) + +string (REPLACE ";" " -I" OPENDISCON_INCLUDE_DIRS "${OPENDISCON_INCLUDE_DIRS}") +string (REPLACE ";" " " OPENDISCON_SOURCES "${OPENDISCON_SOURCES}") + +string (REPLACE ";" " -I" OPENWITCON_INCLUDE_DIRS "${OPENWITCON_INCLUDE_DIRS}") +string (REPLACE ";" " " OPENWITCON_SOURCES "${OPENWITCON_SOURCES}") + +configure_file (${PROJECT_SOURCE_DIR}/CONFIGURATION/${CONFIGURATION}/src/sfunc/makeOpenDiscon.m ${PROJECT_BINARY_DIR}/) +configure_file (${PROJECT_SOURCE_DIR}/CONFIGURATION/${CONFIGURATION}/src/sfunc/OpenDiscon_block.mdl ${PROJECT_BINARY_DIR}/) diff --git a/CONFIGURATION/CL-Windcon/src/discon/discon.c b/CONFIGURATION/CL-Windcon/src/discon/discon.c new file mode 100644 index 0000000..6c9cb14 --- /dev/null +++ b/CONFIGURATION/CL-Windcon/src/discon/discon.c @@ -0,0 +1,100 @@ +/* +Copyright (C) 2017 IK4-IKERLAN + +This file is part of OpenDiscon. + +OpenDiscon 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. + +OpenDiscon 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 OpenDiscon. If not, see . +*/ + +#define NINT(a) ((a) >= 0.0 ? (int) ((a)+0.5) : ((a)-0.5)) + +#include "ikClwindconResetSensorSignals.h" +#include "ikClwindconInputMod.h" +#include "ikClwindconWTConfig.h" +#include "OpenDiscon_EXPORT.h" +#include + +void OpenDiscon_EXPORT DISCON(float *DATA, int FLAG, const char *INFILE, const char *OUTNAME, char *MESSAGE) { + int err; + static ikClwindconWTCon con; + double output = -12.0; + static FILE *f = NULL; + const double deratingRatio = 0.0; /* later to be got via the supercontroller interface */ + + if (NINT(DATA[0]) == 0) { + ikClwindconWTConParams param; + ikClwindconWTCon_initParams(¶m); + setParams(¶m); + ikClwindconWTCon_init(&con, ¶m); + f = fopen("log.bin", "wb"); + } + + con.in.deratingRatio = deratingRatio; + con.in.externalMaximumTorque = 230.0; /* kNm */ + con.in.externalMinimumTorque = 0.0; /* kNm */ + con.in.externalMaximumPitch = 90.0; /* deg */ + con.in.externalMinimumPitch = 0.0; /* deg */ + con.in.generatorSpeed = (double) DATA[19]; /* rad/s */ + con.in.rotorSpeed = (double) DATA[20]; /* rad/s */ + con.in.maximumSpeed = 480.0/30*3.1416; /* rpm to rad/s */ + con.in.azimuth = 180.0/3.1416 * (double) DATA[59]; /* rad to deg */ + con.in.maximumIndividualPitch = 0.0; /* deg */ + con.in.yawErrorReference = 0.0; /* deg */ + con.in.yawError = 180.0/3.1416 * (double) DATA[23]; /* rad to deg */ + con.in.bladeRootMoments[0].c[0] = 1.0e-3 * (double) DATA[68]; /* Nm to kNm */ + con.in.bladeRootMoments[0].c[1] = 1.0e-3 * (double) DATA[29]; /* Nm to kNm */ + con.in.bladeRootMoments[0].c[2] = 0.0; /* kNm */ + con.in.bladeRootMoments[1].c[0] = 1.0e-3 * (double) DATA[69]; /* Nm to kNm */ + con.in.bladeRootMoments[1].c[1] = 1.0e-3 * (double) DATA[30]; /* Nm to kNm */ + con.in.bladeRootMoments[1].c[2] = 0.0; /* kNm */ + con.in.bladeRootMoments[2].c[0] = 1.0e-3 * (double) DATA[70]; /* Nm to kNm */ + con.in.bladeRootMoments[2].c[1] = 1.0e-3 * (double) DATA[31]; /* Nm to kNm */ + con.in.bladeRootMoments[2].c[2] = 0.0; /* kNm */ + + ikClwindconInputMod(&(con.in)); + ikClwindconResetSensorSignals(&(con.in)); + ikClwindconWTCon_step(&con); + + /* ####################################### */ + + DATA[46] = (float) (con.out.torqueDemand*1.0e3); /* kNm to Nm */ + DATA[41] = (float) (con.out.pitchDemandBlade1/180.0*3.1416); /* deg to rad */ + DATA[42] = (float) (con.out.pitchDemandBlade2/180.0*3.1416); /* deg to rad */ + DATA[43] = (float) (con.out.pitchDemandBlade3/180.0*3.1416); /* deg to rad */ + err = ikClwindconWTCon_getOutput(&con, &output, "collective pitch demand"); + DATA[44] = (float) (output/180.0*3.1416); /* deg to rad (collective pitch angle) */ + + err = ikClwindconWTCon_getOutput(&con, &output, "individual pitch control>pitch y from control"); + fwrite(&(output), 1, sizeof(output), f); + err = ikClwindconWTCon_getOutput(&con, &output, "individual pitch control>pitch z from control"); + fwrite(&(output), 1, sizeof(output), f); + err = ikClwindconWTCon_getOutput(&con, &output, "individual pitch control>My"); + fwrite(&(output), 1, sizeof(output), f); + err = ikClwindconWTCon_getOutput(&con, &output, "individual pitch control>Mz"); + fwrite(&(output), 1, sizeof(output), f); + err = ikClwindconWTCon_getOutput(&con, &output, "individual pitch control>pitch increment 1"); + fwrite(&(output), 1, sizeof(output), f); + err = ikClwindconWTCon_getOutput(&con, &output, "individual pitch control>pitch increment 2"); + fwrite(&(output), 1, sizeof(output), f); + err = ikClwindconWTCon_getOutput(&con, &output, "individual pitch control>pitch increment 3"); + fwrite(&(output), 1, sizeof(output), f); + err = ikClwindconWTCon_getOutput(&con, &output, "generator speed equivalent"); + fwrite(&(output), 1, sizeof(output), f); + err = ikClwindconWTCon_getOutput(&con, &output, "speed sensor manager>signal 1"); + fwrite(&(output), 1, sizeof(output), f); + err = ikClwindconWTCon_getOutput(&con, &output, "speed sensor manager>signal 2"); + fwrite(&(output), 1, sizeof(output), f); + err = ikClwindconWTCon_getOutput(&con, &output, "speed sensor manager>signal 3"); + fwrite(&(output), 1, sizeof(output), f); +} diff --git a/CONFIGURATION/CL-Windcon/src/ikClwindconInputMod/ikClwindconInputMod.c b/CONFIGURATION/CL-Windcon/src/ikClwindconInputMod/ikClwindconInputMod.c new file mode 100644 index 0000000..647ab40 --- /dev/null +++ b/CONFIGURATION/CL-Windcon/src/ikClwindconInputMod/ikClwindconInputMod.c @@ -0,0 +1,61 @@ +/* +Copyright (C) 2017 IK4-IKERLAN + +This file is part of OpenDiscon. + +OpenDiscon 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. + +OpenDiscon 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 OpenDiscon. If not, see . +*/ + +/** + * @file ikClwindconInputMod.c + * + * @brief CL-Windcon wind turbine controller input modification + */ + +#include "ikClwindconInputMod.h" + +void ikClwindconInputMod(ikClwindconWTConInputs *in) { + + ikGeneratorSpeedSingalFail(in); + +} + +void ikGeneratorSpeedSingalFail(ikClwindconWTConInputs *in) { + static int _n = 0; + + /*! [Speed sensor fault] */ + /* + #################################################################### + Speed sensor fault + + A generator speed measurement of val is enforced after N sampling + intervals (N <= 0 to disable). + + Set parameters here: + */ + const int N = 10000; + const double val = 0.0; + /* + #################################################################### + */ + /*! [Speed sensor fault] */ + + if (0 < N && _n < N) { + _n++; + return; + } + + in->generatorSpeed = val; + +} diff --git a/CONFIGURATION/CL-Windcon/src/ikClwindconInputMod/ikClwindconInputMod.h b/CONFIGURATION/CL-Windcon/src/ikClwindconInputMod/ikClwindconInputMod.h new file mode 100644 index 0000000..e2be026 --- /dev/null +++ b/CONFIGURATION/CL-Windcon/src/ikClwindconInputMod/ikClwindconInputMod.h @@ -0,0 +1,43 @@ +/* +Copyright (C) 2017 IK4-IKERLAN + +This file is part of OpenDiscon. + +OpenDiscon 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. + +OpenDiscon 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 OpenDiscon. If not, see . +*/ + +/** + * @file ikClwindconInputMod.h + * + * @brief CL-Windcon wind turbine controller input modification + */ + +#ifndef IKCLWINDCONINPUTMOD_H +#define IKCLWINDCONINPUTMOD_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include "ikClwindconWTCon.h" + + void ikClwindconInputMod(ikClwindconWTConInputs *in); + void ikGeneratorSpeedSingalFail(ikClwindconWTConInputs *in); + +#ifdef __cplusplus +} +#endif + +#endif /* IKCLWINDCONINPUTMOD_H */ + diff --git a/CONFIGURATION/CL-Windcon/src/ikClwindconResetSensorSignals/ikClwindconResetSensorSignals.c b/CONFIGURATION/CL-Windcon/src/ikClwindconResetSensorSignals/ikClwindconResetSensorSignals.c new file mode 100644 index 0000000..a323e74 --- /dev/null +++ b/CONFIGURATION/CL-Windcon/src/ikClwindconResetSensorSignals/ikClwindconResetSensorSignals.c @@ -0,0 +1,53 @@ +/* +Copyright (C) 2017 IK4-IKERLAN + +This file is part of OpenDiscon. + +OpenDiscon 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. + +OpenDiscon 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 OpenDiscon. If not, see . +*/ + +/** + * @file ikClwindconInputMod.c + * + * @brief CL-Windcon wind turbine controller input modification + */ + +#include "ikClwindconResetSensorSignals.h" + +void ikClwindconResetSensorSignals(ikClwindconWTConInputs *in) { + + ikAllOK(in); + +} + +void ikAllOK(ikClwindconWTConInputs *in) { + /*! [Reset Speed sensor fault] */ + /* + #################################################################### + + Set parameters here: + */ + const int nSteps = 3000; + static int _t; + /* + #################################################################### + */ + if (!(_t < nSteps) && !(nSteps < _t)){ + in->ResetSignal = 1; + _t++; + } else { + _t++; + in->ResetSignal = 0; + } +} diff --git a/CONFIGURATION/CL-Windcon/src/ikClwindconResetSensorSignals/ikClwindconResetSensorSignals.h b/CONFIGURATION/CL-Windcon/src/ikClwindconResetSensorSignals/ikClwindconResetSensorSignals.h new file mode 100644 index 0000000..50213e3 --- /dev/null +++ b/CONFIGURATION/CL-Windcon/src/ikClwindconResetSensorSignals/ikClwindconResetSensorSignals.h @@ -0,0 +1,44 @@ +/* +Copyright (C) 2017 IK4-IKERLAN + +This file is part of OpenDiscon. + +OpenDiscon 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. + +OpenDiscon 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 OpenDiscon. If not, see . +*/ + +/** + * @file ikClwindconInputMod.h + * + * @brief CL-Windcon wind turbine controller input modification + */ + +#ifndef IKCLWINDCONRESETSENSORSIGNALS_H +#define IKCLWINDCONRESETSENSORSIGNALS_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include "ikClwindconWTCon.h" +#include "ikSensorDiagnoser.h" + + void ikClwindconResetSensorSignals(ikClwindconWTConInputs *in); + void ikAllOK(ikClwindconWTConInputs *in); + +#ifdef __cplusplus +} +#endif + +#endif /* IKCLWINDCONINPUTMOD_H */ + diff --git a/src/ikClwindconWTCon/ikClwindconWTCon.c b/CONFIGURATION/CL-Windcon/src/ikClwindconWTCon/ikClwindconWTCon.c similarity index 68% rename from src/ikClwindconWTCon/ikClwindconWTCon.c rename to CONFIGURATION/CL-Windcon/src/ikClwindconWTCon/ikClwindconWTCon.c index 1b25e4e..4287561 100644 --- a/src/ikClwindconWTCon/ikClwindconWTCon.c +++ b/CONFIGURATION/CL-Windcon/src/ikClwindconWTCon/ikClwindconWTCon.c @@ -48,6 +48,12 @@ int ikClwindconWTCon_init(ikClwindconWTCon *self, const ikClwindconWTConParams * if (err) return -5; err = ikPowman_init(&(self->priv.powerManager), &(params_.powerManager)); if (err) return -6; + err = ikIpc_init(&(self->priv.ipc), &(params_.individualPitchControl)); + if (err) return -7; + err = ikConLoop_init(&(self->priv.yawByIpc), &(params_.yawByIpc)); + if (err) return -8; + err = ikSpdman_init(&(self->priv.speedSensorManager), &(params_.speedSensorManager)); + if (err) return -9; /* initialise feedback signals */ self->priv.torqueFromTorqueCon = 0.0; @@ -63,12 +69,20 @@ void ikClwindconWTCon_initParams(ikClwindconWTConParams *params) { ikConLoop_initParams(&(params->torqueControl)); ikTpman_initParams(&(params->torquePitchManager)); ikPowman_initParams(&(params->powerManager)); + ikIpc_initParams(&(params->individualPitchControl)); + ikConLoop_initParams(&(params->yawByIpc)); + ikSpdman_initParams(&(params->speedSensorManager)); } int ikClwindconWTCon_step(ikClwindconWTCon *self) { + int i; + + /* run speed sensor manager */ + ikSpdman_step(&(self->priv.speedSensorManager), self->in.generatorSpeed, self->in.rotorSpeed, self->in.azimuth, self->in.ResetSignal); + ikSpdman_getOutput(&(self->priv.speedSensorManager), &(self->priv.generatorSpeedEquivalent), "generator speed equivalent"); /* run power manager */ - self->priv.maxTorqueFromPowman = ikPowman_step(&(self->priv.powerManager), self->in.deratingRatio, self->in.maximumSpeed, self->in.generatorSpeed); + self->priv.maxTorqueFromPowman = ikPowman_step(&(self->priv.powerManager), self->in.deratingRatio, self->in.maximumSpeed, self->priv.generatorSpeedEquivalent); ikPowman_getOutput(&(self->priv.powerManager), &(self->priv.minPitchFromPowman), "minimum pitch"); ikPowman_getOutput(&(self->priv.powerManager), &(self->priv.belowRatedTorque), "below rated torque"); @@ -84,21 +98,39 @@ int ikClwindconWTCon_step(ikClwindconWTCon *self) { ikTpman_getOutput(&(self->priv.tpManager), &(self->priv.minTorque), "minimum torque"); /* run drivetrain damper */ - self->priv.torqueFromDtdamper = ikConLoop_step(&(self->priv.dtdamper), 0.0, self->in.generatorSpeed, -(self->in.externalMaximumTorque), self->in.externalMaximumTorque); + self->priv.torqueFromDtdamper = ikConLoop_step(&(self->priv.dtdamper), 0.0, self->priv.generatorSpeedEquivalent, -(self->in.externalMaximumTorque), self->in.externalMaximumTorque); /* run torque control */ - self->priv.torqueFromTorqueCon = ikConLoop_step(&(self->priv.torquecon), self->in.maximumSpeed, self->in.generatorSpeed, self->priv.minTorque, self->priv.maxTorque); + self->priv.torqueFromTorqueCon = ikConLoop_step(&(self->priv.torquecon), self->in.maximumSpeed, self->priv.generatorSpeedEquivalent, self->priv.minTorque, self->priv.maxTorque); /* calculate torque demand */ self->out.torqueDemand = self->priv.torqueFromDtdamper + self->priv.torqueFromTorqueCon; /* run collective pitch control */ - self->priv.collectivePitchDemand = ikConLoop_step(&(self->priv.colpitchcon), self->in.maximumSpeed, self->in.generatorSpeed, self->priv.minPitch, self->priv.maxPitch); + self->priv.collectivePitchDemand = ikConLoop_step(&(self->priv.colpitchcon), self->in.maximumSpeed, self->priv.generatorSpeedEquivalent, self->priv.minPitch, self->priv.maxPitch); + + /* run yaw by ipc */ + self->priv.individualPitchForYaw = ikConLoop_step(&(self->priv.yawByIpc), self->in.yawErrorReference, self->in.yawError, -self->in.maximumIndividualPitch, self->in.maximumIndividualPitch); + + /* run individual pitch control */ + self->priv.ipc.in.azimuth = self->in.azimuth; + self->priv.ipc.in.collectivePitch = self->priv.collectivePitchDemand; + self->priv.ipc.in.maximumPitch = self->priv.maxPitch; + self->priv.ipc.in.minimumPitch = self->priv.minPitch; + for (i = 0; i < 3; i++) { + self->priv.ipc.in.bladeRootMoments[i] = self->in.bladeRootMoments[i]; + } + self->priv.ipc.in.demandedMy = 0.0; + self->priv.ipc.in.demandedMz = 0.0; + self->priv.ipc.in.maximumIndividualPitch = self->in.maximumIndividualPitch; + self->priv.ipc.in.externalPitchY = self->priv.individualPitchForYaw; + self->priv.ipc.in.externalPitchZ = 0.0; + ikIpc_step(&(self->priv.ipc)); /* run IPC */ - self->out.pitchDemandBlade1 = self->priv.collectivePitchDemand; - self->out.pitchDemandBlade2 = self->priv.collectivePitchDemand; - self->out.pitchDemandBlade3 = self->priv.collectivePitchDemand; + self->out.pitchDemandBlade1 = self->priv.ipc.out.pitch[0]; + self->out.pitchDemandBlade2 = self->priv.ipc.out.pitch[1]; + self->out.pitchDemandBlade3 = self->priv.ipc.out.pitch[2]; return self->priv.tpManState; } @@ -144,6 +176,14 @@ int ikClwindconWTCon_getOutput(const ikClwindconWTCon *self, double *output, con *output = self->priv.minPitchFromPowman; return 0; } + if (!strcmp(name, "individual pitch for yaw")) { + *output = self->priv.individualPitchForYaw; + return 0; + } + if (!strcmp(name, "generator speed equivalent")) { + *output = self->priv.generatorSpeedEquivalent; + return 0; + } /* pick up the block names */ sep = strstr(name, ">"); @@ -173,6 +213,21 @@ int ikClwindconWTCon_getOutput(const ikClwindconWTCon *self, double *output, con if (err) return -1; else return 0; } + if (!strncmp(name, "individual pitch control", strlen(name) - strlen(sep))) { + err = ikIpc_getOutput(&(self->priv.ipc), output, sep + 1); + if (err) return -1; + else return 0; + } + if (!strncmp(name, "yaw by ipc", strlen(name) - strlen(sep))) { + err = ikConLoop_getOutput(&(self->priv.yawByIpc), output, sep + 1); + if (err) return -1; + else return 0; + } + if (!strncmp(name, "speed sensor manager", strlen(name) - strlen(sep))) { + err = ikSpdman_getOutput(&(self->priv.speedSensorManager), output, sep + 1); + if (err) return -1; + else return 0; + } return -2; diff --git a/src/ikClwindconWTCon/ikClwindconWTCon.h b/CONFIGURATION/CL-Windcon/src/ikClwindconWTCon/ikClwindconWTCon.h similarity index 81% rename from src/ikClwindconWTCon/ikClwindconWTCon.h rename to CONFIGURATION/CL-Windcon/src/ikClwindconWTCon/ikClwindconWTCon.h index 04ae6fb..ceac94c 100644 --- a/src/ikClwindconWTCon/ikClwindconWTCon.h +++ b/CONFIGURATION/CL-Windcon/src/ikClwindconWTCon/ikClwindconWTCon.h @@ -33,6 +33,8 @@ extern "C" { #include "ikConLoop.h" #include "ikTpman.h" #include "ikPowman.h" +#include "ikIpc.h" +#include "ikSpdman.h" /** * @struct ikClwindconWTConInputs @@ -45,7 +47,14 @@ extern "C" { double externalMinimumPitch; /**. #include "ikClwindconWTConfig.h" void setParams(ikClwindconWTConParams *param) { - double T = 0.01; + /*! [Sampling interval] */ + /* + #################################################################### + Sampling interval + + Set sampling interval here: + */ + const double T = 0.01; /* [s] */ + /* + #################################################################### + */ + /*! [Sampling interval] */ ikTuneDrivetrainDamper(&(param->drivetrainDamper), T); ikTuneSpeedRange(&(param->torqueControl)); @@ -40,12 +51,18 @@ void setParams(ikClwindconWTConParams *param) { ikTuneTorqueLowpassFilter(&(param->torqueControl), T); ikTuneTorqueNotches(&(param->torqueControl), T); ikTuneTorquePI(&(param->torqueControl), T); + ikConfigureRotorForIpc(&(param->individualPitchControl)); + ikTuneIpcMyPI(&(param->individualPitchControl.controlMy), T); + ikTuneIpcMzPI(&(param->individualPitchControl.controlMz), T); + ikTuneYawByIpc(&(param->yawByIpc), T); + ikTuneYawByIpcLowpassFilter(&(param->yawByIpc), T); + ikConfigureSpeedManager(&(param->speedSensorManager), T); } void ikTuneDrivetrainDamper(ikConLoopParams *params, double T) { - /*! [CL-Windcon drivetrain damper] */ + /*! [Drivetrain damper] */ /* #################################################################### Drivetrain damper @@ -58,13 +75,13 @@ void ikTuneDrivetrainDamper(ikConLoopParams *params, double T) { Set parameters here: */ - double G = 0.0382; /* [kNm s^2/rad] 4 Nm s/rpm */ - double d = 0.1; /* [-] */ - double w = 21.1; /* [rad/s] */ + const double G = 0.0382; /* [kNm s^2/rad] 4 Nm s/rpm */ + const double d = 0.1; /* [-] */ + const double w = 21.1; /* [rad/s] */ /* #################################################################### */ - /*! [CL-Windcon drivetrain damper] */ + /*! [Drivetrain damper] */ /* @@ -228,8 +245,8 @@ void ikTunePitchLowpassFilter(ikConLoopParams *params, double T) { Set parameters here: */ - double w = 5.6; /* [rad/s] */ - double d = 0.5; /* [-] */ + const double w = 5.6; /* [rad/s] */ + const double d = 0.5; /* [-] */ /* #################################################################### */ @@ -276,9 +293,9 @@ void ikTunePitchNotches(ikConLoopParams *params, double T) { Set parameters here: */ - double w = 1.59; /* [rad/s] */ - double dnum = 0.01; /* [-] */ - double dden = 0.2; /* [-] */ + const double w = 1.59; /* [rad/s] */ + const double dnum = 0.01; /* [-] */ + const double dden = 0.2; /* [-] */ /* #################################################################### */ @@ -307,8 +324,8 @@ void ikTunePitchPI(ikConLoopParams *params, double T) { Set parameters here: */ - double Kp = -0.3939; /* [degs/rad] 7.2e-4 rad/rpm */ - double Ki = -0.1313; /* [deg/rad] 2.4e-4 rad/rpms */ + const double Kp = -0.3939; /* [degs/rad] 7.2e-4 rad/rpm */ + const double Ki = -0.1313; /* [deg/rad] 2.4e-4 rad/rpms */ /* #################################################################### */ @@ -354,8 +371,8 @@ void ikTuneTorqueLowpassFilter(ikConLoopParams *params, double T) { Set parameters here: */ - double w = 3.39; /* [rad/s] */ - double d = 0.5; /* [-] */ + const double w = 3.39; /* [rad/s] */ + const double d = 0.5; /* [-] */ /* #################################################################### */ @@ -402,9 +419,9 @@ void ikTuneTorqueNotches(ikConLoopParams *params, double T) { Set parameters here: */ - double w = 1.59; /* [rad/s] */ - double dnum = 0.01; /* [-] */ - double dden = 0.2; /* [-] */ + const double w = 1.59; /* [rad/s] */ + const double dnum = 0.01; /* [-] */ + const double dden = 0.2; /* [-] */ /* #################################################################### */ @@ -433,14 +450,13 @@ void ikTuneTorquePI(ikConLoopParams *params, double T) { Set parameters here: */ - double Kp = -34.3775; /* [kNms/rad] 3600 Nm/rpm */ - double Ki = -11.4592; /* [kNm/rad] 1200 Nm/rpms */ + const double Kp = -34.3775; /* [kNms/rad] 3600 Nm/rpm */ + const double Ki = -11.4592; /* [kNm/rad] 1200 Nm/rpms */ /* #################################################################### */ /*! [Torque PI] */ - /* tune the torque control to this tf: (Kp + Ki*T/2)z - (Kp - Ki*T/2) @@ -465,3 +481,231 @@ void ikTuneTorquePI(ikConLoopParams *params, double T) { params->linearController.postGainTfs.tfParams[0].a[2] = 0.0; } + +void ikConfigureRotorForIpc(ikIpcParams *params) { + + params->azimuthOffset = 0.0; + params->bladeOrder = 1; + +} + +void ikTuneIpcMyPI(ikConLoopParams *params, double T) { + + /*! [IPC My PI] */ + /* + #################################################################### + IPC My PI + + Transfer function: + + C(s) = (Kp*s + Ki)/s + + The sampling time is given by function parameter T. + + Set parameters here: + */ + const double Kp = 0.0; /* [deg/kNm] */ + const double Ki = -0.1e-3; /* [deg/kNms] */ + /* + #################################################################### + */ + /*! [IPC My PI] */ + + /* + tune the ipc My control to this tf: + (Kp + Ki*T/2)z - (Kp - Ki*T/2) + C(z) = ------------------------------ + z - 1 + rad/s --> kNm + */ + params->linearController.errorTfs.tfParams[0].enable = 1; + params->linearController.errorTfs.tfParams[0].b[0] = (Kp + Ki*T/2); + params->linearController.errorTfs.tfParams[0].b[1] = -(Kp - Ki*T/2); + params->linearController.errorTfs.tfParams[0].b[2] = 0.0; + params->linearController.errorTfs.tfParams[0].a[0] = 1.0; + params->linearController.errorTfs.tfParams[0].a[1] = 0.0; + params->linearController.errorTfs.tfParams[0].a[2] = 0.0; + + params->linearController.postGainTfs.tfParams[0].enable = 1; + params->linearController.postGainTfs.tfParams[0].b[0] = 1.0; + params->linearController.postGainTfs.tfParams[0].b[1] = 0.0; + params->linearController.postGainTfs.tfParams[0].b[2] = 0.0; + params->linearController.postGainTfs.tfParams[0].a[0] = 1.0; + params->linearController.postGainTfs.tfParams[0].a[1] = -1.0; + params->linearController.postGainTfs.tfParams[0].a[2] = 0.0; + +} + +void ikTuneIpcMzPI(ikConLoopParams *params, double T) { + + /*! [IPC Mz PI] */ + /* + #################################################################### + IPC Mz PI + + Transfer function: + + C(s) = (Kp*s + Ki)/s + + The sampling time is given by function parameter T. + + Set parameters here: + */ + const double Kp = 0.0; /* [deg/kNm] */ + const double Ki = -0.1e-3; /* [deg/kNms] */ + /* + #################################################################### + */ + /*! [IPC Mz PI] */ + + /* + tune the ipc Mz control to this tf: + (Kp + Ki*T/2)z - (Kp - Ki*T/2) + C(z) = ------------------------------ + z - 1 + rad/s --> kNm + */ + params->linearController.errorTfs.tfParams[0].enable = 1; + params->linearController.errorTfs.tfParams[0].b[0] = (Kp + Ki*T/2); + params->linearController.errorTfs.tfParams[0].b[1] = -(Kp - Ki*T/2); + params->linearController.errorTfs.tfParams[0].b[2] = 0.0; + params->linearController.errorTfs.tfParams[0].a[0] = 1.0; + params->linearController.errorTfs.tfParams[0].a[1] = 0.0; + params->linearController.errorTfs.tfParams[0].a[2] = 0.0; + + params->linearController.postGainTfs.tfParams[0].enable = 1; + params->linearController.postGainTfs.tfParams[0].b[0] = 1.0; + params->linearController.postGainTfs.tfParams[0].b[1] = 0.0; + params->linearController.postGainTfs.tfParams[0].b[2] = 0.0; + params->linearController.postGainTfs.tfParams[0].a[0] = 1.0; + params->linearController.postGainTfs.tfParams[0].a[1] = -1.0; + params->linearController.postGainTfs.tfParams[0].a[2] = 0.0; + +} + +void ikTuneYawByIpc(ikConLoopParams *params, double T) { +/* +This is an original implementation of the yaw by IPC strategy in 87e4a2fe8e8ac8fc51305a3f840e23a0deaf6caa of https://github.com/TUDelft-DataDrivenControl/DRC_Fortran +*/ + + /*! [Yaw by IPC PI] */ + /* + #################################################################### + Yaw by IPC PI + + Transfer function: + + C(s) = (Kp*s + Ki)/s + + The sampling time is given by function parameter T. + + Set parameters here: + */ + const double Kp = 0.0; /* [-] */ + const double Ki = 0.0; /* [1/s] */ + /* + #################################################################### + */ + /*! [Yaw by IPC PI] */ + + /* + tune the yaw by ipc control to this tf: + (Kp + Ki*T/2)z - (Kp - Ki*T/2) + C(z) = ------------------------------ + z - 1 + deg --> deg + */ + params->linearController.errorTfs.tfParams[0].enable = 1; + params->linearController.errorTfs.tfParams[0].b[0] = (Kp + Ki*T/2); + params->linearController.errorTfs.tfParams[0].b[1] = -(Kp - Ki*T/2); + params->linearController.errorTfs.tfParams[0].b[2] = 0.0; + params->linearController.errorTfs.tfParams[0].a[0] = 1.0; + params->linearController.errorTfs.tfParams[0].a[1] = 0.0; + params->linearController.errorTfs.tfParams[0].a[2] = 0.0; + + params->linearController.postGainTfs.tfParams[0].enable = 1; + params->linearController.postGainTfs.tfParams[0].b[0] = 1.0; + params->linearController.postGainTfs.tfParams[0].b[1] = 0.0; + params->linearController.postGainTfs.tfParams[0].b[2] = 0.0; + params->linearController.postGainTfs.tfParams[0].a[0] = 1.0; + params->linearController.postGainTfs.tfParams[0].a[1] = -1.0; + params->linearController.postGainTfs.tfParams[0].a[2] = 0.0; + +} + +void ikTuneYawByIpcLowpassFilter(ikConLoopParams *params, double T) { +/* +This is an original implementation of the yaw by IPC strategy in 87e4a2fe8e8ac8fc51305a3f840e23a0deaf6caa of https://github.com/TUDelft-DataDrivenControl/DRC_Fortran +*/ + + /*! [Yaw by IPC lowpass filter] */ + /* + #################################################################### + Yaw error feedback low pass filter + + Transfer function: + H(s) = w^2 / (s^2 + 2*d*w*s + w^2) + + The sampling time is given by function parameter T. + + The default values have been kindly provided by TUDelft, who have calculated them to suit the DTU 10MW reference wind turbine from FP7 project INNWIND. + Set parameters here: + */ + const double w = 0.6283185; /* [rad/s] */ + const double d = 1.0; /* [-] */ + /* + #################################################################### + */ + /*! [Yaw by IPC lowpass filter] */ + + /* + tune the yaw by ipc control feedback filter to this tf: + (0.5*T*w)^2 z^2 + 2z + 1 + H(z) = ----------------------------- ------------------------------------------------------------------------------------------------------------------------ + 1 + T*d*w + (0.5*T*w)^2 z^2 - 2*(1 - (0.5*T*w)^2) / (1 + T*d*w + (0.5*T*w)^2)z + (1 - T*d*w + (0.5*T*w)^2) / (1 + T*d*w + (0.5*T*w)^2) + */ + params->linearController.measurementTfs.tfParams[0].enable = 1; + params->linearController.measurementTfs.tfParams[0].b[0] = 1.0; + params->linearController.measurementTfs.tfParams[0].b[1] = 2.0; + params->linearController.measurementTfs.tfParams[0].b[2] = 1.0; + params->linearController.measurementTfs.tfParams[0].a[0] = 1.0; + params->linearController.measurementTfs.tfParams[0].a[1] = -2 * (1 - (0.5*T*w)*(0.5*T*w)) / (1 + T*d*w + (0.5*T*w)*(0.5*T*w)); + params->linearController.measurementTfs.tfParams[0].a[2] = (1 - T*d*w + (0.5*T*w)*(0.5*T*w)) / (1 + T*d*w + (0.5*T*w)*(0.5*T*w)); + + params->linearController.measurementTfs.tfParams[1].enable = 1; + params->linearController.measurementTfs.tfParams[1].b[0] = (0.5*T*w)*(0.5*T*w) / (1 + T*d*w + (0.5*T*w)*(0.5*T*w)); + +} + +void ikConfigureSpeedManager(ikSpdmanParams *params, double T) { + + /*! [Speed sensor manager] */ + /* + #################################################################### + Speed sensor management + + Differences between the generator speed, rotor speed and azimuth derivative + (the latter two multiplied by the gearbox ratio) are considered a fault if + they are larger than tol for longer than N sampling intervals T. + + Set parameters here: + */ + const int N = 4; /* [-] */ + const double tol = 1.0; /* [rad/s] */ + const double gbRatio = 50.0; /* [-] */ + /* + #################################################################### + */ + /*! [Speed sensor manager] */ + + params->diagnoser.nStepsToFault = N; + params->diagnoser.tolerance = tol; + + params->gearboxRatio = gbRatio; + params->T = T; + params->minAzimuth = 0.0; + params->maxAzimuth = 360.0; + + + +} diff --git a/src/ikClwindconWTConfig/ikClwindconWTConfig.h b/CONFIGURATION/CL-Windcon/src/ikClwindconWTConfig/ikClwindconWTConfig.h similarity index 83% rename from src/ikClwindconWTConfig/ikClwindconWTConfig.h rename to CONFIGURATION/CL-Windcon/src/ikClwindconWTConfig/ikClwindconWTConfig.h index c5e3e7e..a02a7e9 100644 --- a/src/ikClwindconWTConfig/ikClwindconWTConfig.h +++ b/CONFIGURATION/CL-Windcon/src/ikClwindconWTConfig/ikClwindconWTConfig.h @@ -58,6 +58,18 @@ extern "C" { void ikTunePitchPIGainSchedule(ikConLoopParams *params); + void ikConfigureRotorForIpc(ikIpcParams *params); + + void ikTuneIpcMyPI(ikConLoopParams *params, double T); + + void ikTuneIpcMzPI(ikConLoopParams *params, double T); + + void ikTuneYawByIpc(ikConLoopParams *params, double T); + + void ikTuneYawByIpcLowpassFilter(ikConLoopParams *params, double T); + + void ikConfigureSpeedManager(ikSpdmanParams *params, double T); + #ifdef __cplusplus } #endif diff --git a/src/ikPowman/ikPowman.c b/CONFIGURATION/CL-Windcon/src/ikPowman/ikPowman.c similarity index 98% rename from src/ikPowman/ikPowman.c rename to CONFIGURATION/CL-Windcon/src/ikPowman/ikPowman.c index 29b515f..b7447c3 100644 --- a/src/ikPowman/ikPowman.c +++ b/CONFIGURATION/CL-Windcon/src/ikPowman/ikPowman.c @@ -25,6 +25,9 @@ along with OpenDiscon. If not, see . /* @cond */ +#include +#include + #include "ikPowman.h" int ikPowman_init(ikPowman *self, const ikPowmanParams *params) { diff --git a/src/ikPowman/ikPowman.h b/CONFIGURATION/CL-Windcon/src/ikPowman/ikPowman.h similarity index 100% rename from src/ikPowman/ikPowman.h rename to CONFIGURATION/CL-Windcon/src/ikPowman/ikPowman.h diff --git a/CONFIGURATION/CL-Windcon/src/ikSpdman/ikSpdman.c b/CONFIGURATION/CL-Windcon/src/ikSpdman/ikSpdman.c new file mode 100644 index 0000000..3bc7500 --- /dev/null +++ b/CONFIGURATION/CL-Windcon/src/ikSpdman/ikSpdman.c @@ -0,0 +1,143 @@ +/* +Copyright (C) 2017 IK4-IKERLAN + +This file is part of OpenDiscon. + +OpenDiscon 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. + +OpenDiscon 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 OpenDiscon. If not, see . +*/ + +/** + * @file ikSpdman.c + * + * @brief Class ikSpdman implementation + */ + +/* @cond */ + +#include +#include + +#include "ikSpdman.h" + +int ikSpdman_init(ikSpdman *self, const ikSpdmanParams *params) { + int err; + int err_ = 0; + + /* pass on member parameters */ + err = ikSensorDiagnoser_init(&(self->diagnoser), &(params->diagnoser)); + if (err && !err_) err_ = -1; + + /* register parameter values */ + self->gbratio = params->gearboxRatio; + if (0.0 > params->T) return -2; + self->T = params->T; + if (params->maxAzimuth <= params->minAzimuth) return -3; + self->azimuthRange = params->maxAzimuth - params->minAzimuth; + + return err_; +} + +void ikSpdman_initParams(ikSpdmanParams *params) { + + /* pass on member parameters */ + ikSensorDiagnoser_initParams(&(params->diagnoser)); + + /* set default values */ + params->gearboxRatio = 1.0; + params->T = 1.0; + params->minAzimuth = 0.0; + params->maxAzimuth = 360.0; +} + +int ikSpdman_step(ikSpdman *self, double generatorSpeed, double rotorSpeed, double azimuth, int ResetSignal) { + int i; + double diff; + + /* prepare signals */ + self->signals[0] = generatorSpeed; + self->signals[1] = self->gbratio*rotorSpeed; + diff = azimuth - self->lastAzimuth; + diff = diff < self->azimuthRange/2 ? diff : diff - self->azimuthRange; + diff = diff > -self->azimuthRange/2 ? diff : diff + self->azimuthRange; + self->signals[2] = self->gbratio * diff / self->T / 180.0 * 3.14159265358979; + self->lastAzimuth = azimuth; + self->diagnoser.ResetSignal = ResetSignal; + /* run diagnoser */ + ikSensorDiagnoser_step(&(self->diagnoser), self->ok, self->signals); + + /* compute status */ + self->status = 0; + for (i = 0; i < 3; i++) { + if (!self->ok[i]) { + if (!self->status) self->status = -1 - i; + else self->status = 4; + } + } + + /* choose the signal to output */ + switch (self->status) { + case -1 : + self->outputSpeed = self->signals[1]; + break; + default : + self->outputSpeed = self->signals[0]; + } + + return self->status; +} + +int ikSpdman_getOutput(const ikSpdman *self, double *output, const char *name) { + const char *sep; + + /* pick up the signal names */ + if (!strcmp(name, "generator speed equivalent")) { + *output = self->outputSpeed; + return 0; + } + if (!strcmp(name, "signal 1")) { + *output = self->signals[0]; + return 0; + } + if (!strcmp(name, "signal 2")) { + *output = self->signals[1]; + return 0; + } + if (!strcmp(name, "signal 3")) { + *output = self->signals[2]; + return 0; + } + if (!strcmp(name, "ok 1")) { + *output = self->ok[0]; + return 0; + } + if (!strcmp(name, "ok 2")) { + *output = self->ok[1]; + return 0; + } + if (!strcmp(name, "ok 3")) { + *output = self->ok[2]; + return 0; + } + + /* pick up the block names */ + sep = strstr(name, ">"); + if (NULL == sep) return -1; + + return -2; +} + +/* @endcond */ + + + diff --git a/CONFIGURATION/CL-Windcon/src/ikSpdman/ikSpdman.h b/CONFIGURATION/CL-Windcon/src/ikSpdman/ikSpdman.h new file mode 100644 index 0000000..94fe24f --- /dev/null +++ b/CONFIGURATION/CL-Windcon/src/ikSpdman/ikSpdman.h @@ -0,0 +1,159 @@ +/* +Copyright (C) 2017 IK4-IKERLAN + +This file is part of OpenDiscon. + +OpenDiscon 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. + +OpenDiscon 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 OpenDiscon. If not, see . +*/ + +/** + * @file ikSpdman.h + * + * @brief Class ikSpdman interface + */ + +#ifndef IKSPDMAN_H +#define IKSPDMAN_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include "ikSensorDiagnoser.h" + + /** + * @struct ikSpdman + * @brief Speed sensor manager + * + * This takes the generator speed, rotor speed and azimuth angle measurements, cross-checks them and outputs a usable generator speed signal when possible. + * + * @par Inputs + * @li generator speed: generator speed in rad/s, specify via @link ikSpdman_step @endlink + * @li rotor speed: rotor speed in rad/s, specify via @link ikSpdman_step @endlink + * @li azimuth: rotor azimuth angle in degrees, specify via @link ikSpdman_step @endlink + * + * @par Outputs + * @li generator speed equivalent: generator speed for use by controller, in rad/s, get via @link ikSpdman_getOutput @endlink + * @li status: status code, returned by @link ikSpdman_step @endlink, alternatively get via @link ikSpdman_getOutput @endlink + * + * @par Unit block + * + * @image html ikSpdman_unit_block.svg + * + * @par Block diagram + * + * @image html ikSpdman_block_diagram.svg + * + * @par Map + * + + + + + + + + + + +
ok 1 ok 2 ok 3 status mux
1 1 1 0 1
0 1 1 -1 2
1 0 1 -2 1
1 1 0 -3 1
0 0 1 -4 1
0 1 0 -4 1
1 0 0 -4 1
0 0 0 -4 1
+ * + * @par Methods + * @li @link ikSpdman_initParams @endlink initialise initialisation parameter structure + * @li @link ikSpdman_init @endlink initialise an instance + * @li @link ikSpdman_step @endlink execute periodic calculations + * @li @link ikSpdman_getOutput @endlink get output value + */ + typedef struct ikSpdman { + /** + * Private members + */ + /* @cond */ + ikSensorDiagnoser diagnoser; + double gbratio; + double T; + double azimuthRange; + double lastAzimuth; + double signals[3]; + int ok[3]; + double outputSpeed; + int status; + /* @endcond */ + } ikSpdman; + + /** + * @struct ikSpdmanParams + * @brief Speed sensor manager initialisation parameters + */ + typedef struct ikSpdmanParams { + ikSensorDiagnoserParams diagnoser; /**" + LastModifiedBy "ielorza" + ModifiedDateFormat "%" + LastModifiedDate "Fri Jan 05 11:00:16 2018" + RTWModifiedTimeStamp 437050811 + ModelVersionFormat "1.%" + ConfigurationManager "none" + SampleTimeColors off + SampleTimeAnnotations off + LibraryLinkDisplay "disabled" + WideLines off + ShowLineDimensions off + ShowPortDataTypes off + ShowDesignRanges off + ShowLoopsOnError on + IgnoreBidirectionalLines off + ShowStorageClass off + ShowTestPointIcons on + ShowSignalResolutionIcons on + ShowViewerIcons on + SortedOrder off + ExecutionContextIcon off + ShowLinearizationAnnotations on + BlockNameDataTip off + BlockParametersDataTip off + BlockDescriptionStringDataTip off + ToolBar on + StatusBar on + BrowserShowLibraryLinks off + BrowserLookUnderMasks off + SimulationMode "normal" + PauseTimes "5" + NumberOfSteps 1 + SnapshotBufferSize 10 + SnapshotInterval 10 + NumberOfLastSnapshots 0 + LinearizationMsg "none" + Profile off + ParamWorkspaceSource "MATLABWorkspace" + AccelSystemTargetFile "accel.tlc" + AccelTemplateMakefile "accel_default_tmf" + AccelMakeCommand "make_rtw" + TryForcingSFcnDF off + Object { + $PropName "DataLoggingOverride" + $ObjectID 6 + $ClassName "Simulink.SimulationData.ModelLoggingInfo" + model_ "OpenDiscon_block" + overrideMode_ [0.0] + Array { + Type "Cell" + Dimension 1 + Cell "OpenDiscon_block" + PropName "logAsSpecifiedByModels_" + } + Array { + Type "Cell" + Dimension 1 + Cell [] + PropName "logAsSpecifiedByModelsSSIDs_" + } + } + RecordCoverage off + CovPath "/" + CovSaveName "covdata" + CovMetricSettings "dw" + CovNameIncrementing off + CovHtmlReporting on + CovForceBlockReductionOff on + CovEnableCumulative on + covSaveCumulativeToWorkspaceVar on + CovSaveSingleToWorkspaceVar on + CovCumulativeVarName "covCumulativeData" + CovCumulativeReport off + CovReportOnPause on + CovModelRefEnable "Off" + CovExternalEMLEnable off + CovSFcnEnable off + CovBoundaryAbsTol 0.000010 + CovBoundaryRelTol 0.010000 + ExtModeBatchMode off + ExtModeEnableFloating on + ExtModeTrigType "manual" + ExtModeTrigMode "normal" + ExtModeTrigPort "1" + ExtModeTrigElement "any" + ExtModeTrigDuration 1000 + ExtModeTrigDurationFloating "auto" + ExtModeTrigHoldOff 0 + ExtModeTrigDelay 0 + ExtModeTrigDirection "rising" + ExtModeTrigLevel 0 + ExtModeArchiveMode "off" + ExtModeAutoIncOneShot off + ExtModeIncDirWhenArm off + ExtModeAddSuffixToVar off + ExtModeWriteAllDataToWs off + ExtModeArmWhenConnect on + ExtModeSkipDownloadWhenConnect off + ExtModeLogAll on + ExtModeAutoUpdateStatusClock on + BufferReuse on + ShowModelReferenceBlockVersion off + ShowModelReferenceBlockIO off + Array { + Type "Handle" + Dimension 1 + Simulink.ConfigSet { + $ObjectID 7 + Version "1.14.2" + Array { + Type "Handle" + Dimension 9 + Simulink.SolverCC { + $ObjectID 8 + Version "1.14.2" + StartTime "0.0" + StopTime "10.0" + AbsTol "auto" + FixedStep "auto" + InitialStep "auto" + MaxNumMinSteps "-1" + MaxOrder 5 + ZcThreshold "auto" + ConsecutiveZCsStepRelTol "10*128*eps" + MaxConsecutiveZCs "1000" + ExtrapolationOrder 4 + NumberNewtonIterations 1 + MaxStep "auto" + MinStep "auto" + MaxConsecutiveMinStep "1" + RelTol "1e-3" + SolverMode "Auto" + EnableConcurrentExecution off + ConcurrentTasks off + Solver "ode45" + SolverName "ode45" + SolverJacobianMethodControl "auto" + ShapePreserveControl "DisableAll" + ZeroCrossControl "UseLocalSettings" + ZeroCrossAlgorithm "Nonadaptive" + AlgebraicLoopSolver "TrustRegion" + SolverResetMethod "Fast" + PositivePriorityOrder off + AutoInsertRateTranBlk off + SampleTimeConstraint "Unconstrained" + InsertRTBMode "Whenever possible" + } + Simulink.DataIOCC { + $ObjectID 9 + Version "1.14.2" + Decimation "1" + ExternalInput "[t, u]" + FinalStateName "xFinal" + InitialState "xInitial" + LimitDataPoints on + MaxDataPoints "1000" + LoadExternalInput off + LoadInitialState off + SaveFinalState off + SaveCompleteFinalSimState off + SaveFormat "Array" + SignalLoggingSaveFormat "Dataset" + SaveOutput on + SaveState off + SignalLogging on + DSMLogging on + InspectSignalLogs off + VisualizeSimOutput on + SaveTime on + ReturnWorkspaceOutputs off + StateSaveName "xout" + TimeSaveName "tout" + OutputSaveName "yout" + SignalLoggingName "logsout" + DSMLoggingName "dsmout" + OutputOption "RefineOutputTimes" + OutputTimes "[]" + ReturnWorkspaceOutputsName "out" + Refine "1" + } + Simulink.OptimizationCC { + $ObjectID 10 + Version "1.14.2" + Array { + Type "Cell" + Dimension 8 + Cell "BooleansAsBitfields" + Cell "PassReuseOutputArgsAs" + Cell "PassReuseOutputArgsThreshold" + Cell "ZeroExternalMemoryAtStartup" + Cell "ZeroInternalMemoryAtStartup" + Cell "OptimizeModelRefInitCode" + Cell "NoFixptDivByZeroProtection" + Cell "UseSpecifiedMinMax" + PropName "DisabledProps" + } + BlockReduction on + BooleanDataType on + ConditionallyExecuteInputs on + InlineParams off + UseIntDivNetSlope off + UseFloatMulNetSlope off + DefaultUnderspecifiedDataType "double" + UseSpecifiedMinMax off + InlineInvariantSignals off + OptimizeBlockIOStorage on + BufferReuse on + EnhancedBackFolding off + CachingGlobalReferences off + GlobalBufferReuse on + StrengthReduction off + ExpressionFolding on + BooleansAsBitfields off + BitfieldContainerType "uint_T" + EnableMemcpy on + MemcpyThreshold 64 + PassReuseOutputArgsAs "Structure reference" + ExpressionDepthLimit 128 + FoldNonRolledExpr on + LocalBlockOutputs on + RollThreshold 5 + SystemCodeInlineAuto off + StateBitsets off + DataBitsets off + ActiveStateOutputEnumStorageType "Native Integer" + UseTempVars off + ZeroExternalMemoryAtStartup on + ZeroInternalMemoryAtStartup on + InitFltsAndDblsToZero off + NoFixptDivByZeroProtection off + EfficientFloat2IntCast off + EfficientMapNaN2IntZero on + OptimizeModelRefInitCode off + LifeSpan "inf" + MaxStackSize "Inherit from target" + BufferReusableBoundary on + SimCompilerOptimization "Off" + AccelVerboseBuild off + ParallelExecutionInRapidAccelerator on + } + Simulink.DebuggingCC { + $ObjectID 11 + Version "1.14.2" + RTPrefix "error" + ConsistencyChecking "none" + ArrayBoundsChecking "none" + SignalInfNanChecking "none" + SignalRangeChecking "none" + ReadBeforeWriteMsg "UseLocalSettings" + WriteAfterWriteMsg "UseLocalSettings" + WriteAfterReadMsg "UseLocalSettings" + AlgebraicLoopMsg "warning" + ArtificialAlgebraicLoopMsg "warning" + SaveWithDisabledLinksMsg "warning" + SaveWithParameterizedLinksMsg "warning" + CheckSSInitialOutputMsg on + UnderspecifiedInitializationDetection "Simplified" + MergeDetectMultiDrivingBlocksExec "none" + CheckExecutionContextPreStartOutputMsg off + CheckExecutionContextRuntimeOutputMsg off + SignalResolutionControl "UseLocalSettings" + BlockPriorityViolationMsg "warning" + MinStepSizeMsg "warning" + TimeAdjustmentMsg "none" + MaxConsecutiveZCsMsg "error" + MaskedZcDiagnostic "warning" + IgnoredZcDiagnostic "warning" + SolverPrmCheckMsg "warning" + InheritedTsInSrcMsg "warning" + DiscreteInheritContinuousMsg "warning" + MultiTaskDSMMsg "error" + MultiTaskCondExecSysMsg "error" + MultiTaskRateTransMsg "error" + SingleTaskRateTransMsg "none" + TasksWithSamePriorityMsg "warning" + SigSpecEnsureSampleTimeMsg "warning" + CheckMatrixSingularityMsg "none" + IntegerOverflowMsg "warning" + Int32ToFloatConvMsg "warning" + ParameterDowncastMsg "error" + ParameterOverflowMsg "error" + ParameterUnderflowMsg "none" + ParameterPrecisionLossMsg "warning" + ParameterTunabilityLossMsg "warning" + FixptConstUnderflowMsg "none" + FixptConstOverflowMsg "none" + FixptConstPrecisionLossMsg "none" + UnderSpecifiedDataTypeMsg "none" + UnnecessaryDatatypeConvMsg "none" + VectorMatrixConversionMsg "none" + InvalidFcnCallConnMsg "error" + FcnCallInpInsideContextMsg "EnableAllAsError" + SignalLabelMismatchMsg "none" + UnconnectedInputMsg "warning" + UnconnectedOutputMsg "warning" + UnconnectedLineMsg "warning" + SFcnCompatibilityMsg "none" + FrameProcessingCompatibilityMsg "warning" + UniqueDataStoreMsg "none" + BusObjectLabelMismatch "warning" + RootOutportRequireBusObject "warning" + AssertControl "UseLocalSettings" + EnableOverflowDetection off + ModelReferenceIOMsg "none" + ModelReferenceMultiInstanceNormalModeStructChecksumCheck "error" + ModelReferenceVersionMismatchMessage "none" + ModelReferenceIOMismatchMessage "none" + ModelReferenceCSMismatchMessage "none" + UnknownTsInhSupMsg "warning" + ModelReferenceDataLoggingMessage "warning" + ModelReferenceSymbolNameMessage "warning" + ModelReferenceExtraNoncontSigs "error" + StateNameClashWarn "none" + SimStateInterfaceChecksumMismatchMsg "warning" + SimStateOlderReleaseMsg "error" + InitInArrayFormatMsg "warning" + StrictBusMsg "ErrorLevel1" + BusNameAdapt "WarnAndRepair" + NonBusSignalsTreatedAsBus "none" + LoggingUnavailableSignals "error" + BlockIODiagnostic "none" + SFUnusedDataAndEventsDiag "warning" + SFUnexpectedBacktrackingDiag "warning" + SFInvalidInputDataAccessInChartInitDiag "warning" + SFNoUnconditionalDefaultTransitionDiag "warning" + SFTransitionOutsideNaturalParentDiag "warning" + SFUnconditionalTransitionShadowingDiag "warning" + SFUndirectedBroadcastEventsDiag "warning" + SFTransitionActionBeforeConditionDiag "warning" + } + Simulink.HardwareCC { + $ObjectID 12 + Version "1.14.2" + ProdBitPerChar 8 + ProdBitPerShort 16 + ProdBitPerInt 32 + ProdBitPerLong 32 + ProdBitPerLongLong 64 + ProdBitPerFloat 32 + ProdBitPerDouble 64 + ProdBitPerPointer 32 + ProdLargestAtomicInteger "Char" + ProdLargestAtomicFloat "None" + ProdIntDivRoundTo "Undefined" + ProdEndianess "Unspecified" + ProdWordSize 32 + ProdShiftRightIntArith on + ProdLongLongMode off + ProdHWDeviceType "32-bit Generic" + TargetBitPerChar 8 + TargetBitPerShort 16 + TargetBitPerInt 32 + TargetBitPerLong 32 + TargetBitPerLongLong 64 + TargetBitPerFloat 32 + TargetBitPerDouble 64 + TargetBitPerPointer 32 + TargetLargestAtomicInteger "Char" + TargetLargestAtomicFloat "None" + TargetShiftRightIntArith on + TargetLongLongMode off + TargetIntDivRoundTo "Undefined" + TargetEndianess "Unspecified" + TargetWordSize 32 + TargetTypeEmulationWarnSuppressLevel 0 + TargetPreprocMaxBitsSint 32 + TargetPreprocMaxBitsUint 32 + TargetHWDeviceType "Specified" + TargetUnknown off + ProdEqTarget on + } + Simulink.ModelReferenceCC { + $ObjectID 13 + Version "1.14.2" + UpdateModelReferenceTargets "IfOutOfDateOrStructuralChange" + CheckModelReferenceTargetMessage "error" + EnableParallelModelReferenceBuilds off + ParallelModelReferenceErrorOnInvalidPool on + ParallelModelReferenceMATLABWorkerInit "None" + ModelReferenceNumInstancesAllowed "Multi" + PropagateVarSize "Infer from blocks in model" + ModelReferencePassRootInputsByReference on + ModelReferenceMinAlgLoopOccurrences off + PropagateSignalLabelsOutOfModel off + SupportModelReferenceSimTargetCustomCode off + } + Simulink.SFSimCC { + $ObjectID 14 + Version "1.14.2" + SFSimEnableDebug on + SFSimOverflowDetection on + SFSimEcho on + SimBlas on + SimCtrlC on + SimExtrinsic on + SimIntegrity on + SimUseLocalCustomCode off + SimParseCustomCode on + SimBuildMode "sf_incremental_build" + SimGenImportedTypeDefs off + } + Simulink.RTWCC { + $BackupClass "Simulink.RTWCC" + $ObjectID 15 + Version "1.14.2" + Array { + Type "Cell" + Dimension 15 + Cell "IncludeHyperlinkInReport" + Cell "GenerateTraceInfo" + Cell "GenerateTraceReport" + Cell "GenerateTraceReportSl" + Cell "GenerateTraceReportSf" + Cell "GenerateTraceReportEml" + Cell "PortableWordSizes" + Cell "GenerateWebview" + Cell "GenerateCodeMetricsReport" + Cell "GenerateCodeReplacementReport" + Cell "GenerateErtSFunction" + Cell "CreateSILPILBlock" + Cell "CodeExecutionProfiling" + Cell "CodeProfilingSaveOptions" + Cell "CodeProfilingInstrumentation" + PropName "DisabledProps" + } + SystemTargetFile "grt.tlc" + TLCOptions "" + GenCodeOnly off + MakeCommand "make_rtw" + GenerateMakefile on + PackageGeneratedCodeAndArtifacts off + PackageName "" + TemplateMakefile "grt_default_tmf" + PostCodeGenCommand "" + Description "" + GenerateReport off + SaveLog off + RTWVerbose on + RetainRTWFile off + ProfileTLC off + TLCDebug off + TLCCoverage off + TLCAssert off + ProcessScriptMode "Default" + ConfigurationMode "Optimized" + ProcessScript "" + ConfigurationScript "" + ConfigAtBuild off + RTWUseLocalCustomCode off + RTWUseSimCustomCode off + CustomSourceCode "" + CustomHeaderCode "" + CustomInclude "" + CustomSource "" + CustomLibrary "" + CustomInitializer "" + CustomTerminator "" + Toolchain "Automatically locate an installed toolchain" + BuildConfiguration "Faster Builds" + IncludeHyperlinkInReport off + LaunchReport off + PortableWordSizes off + CreateSILPILBlock "None" + CodeExecutionProfiling off + CodeExecutionProfileVariable "executionProfile" + CodeProfilingSaveOptions "SummaryOnly" + CodeProfilingInstrumentation off + SILDebugging off + TargetLang "C" + IncludeBusHierarchyInRTWFileBlockHierarchyMap off + IncludeERTFirstTime off + GenerateTraceInfo off + GenerateTraceReport off + GenerateTraceReportSl off + GenerateTraceReportSf off + GenerateTraceReportEml off + GenerateCodeInfo off + GenerateWebview off + GenerateCodeMetricsReport off + GenerateCodeReplacementReport off + RTWCompilerOptimization "Off" + RTWCustomCompilerOptimizations "" + CheckMdlBeforeBuild "Off" + CustomRebuildMode "OnUpdate" + DataInitializer "" + SharedConstantsCachingThreshold 1024 + Array { + Type "Handle" + Dimension 2 + Simulink.CodeAppCC { + $ObjectID 16 + Version "1.14.2" + Array { + Type "Cell" + Dimension 23 + Cell "IgnoreCustomStorageClasses" + Cell "IgnoreTestpoints" + Cell "InsertBlockDesc" + Cell "InsertPolySpaceComments" + Cell "SFDataObjDesc" + Cell "MATLABFcnDesc" + Cell "SimulinkDataObjDesc" + Cell "DefineNamingRule" + Cell "SignalNamingRule" + Cell "ParamNamingRule" + Cell "InternalIdentifier" + Cell "InlinedPrmAccess" + Cell "CustomSymbolStr" + Cell "CustomSymbolStrGlobalVar" + Cell "CustomSymbolStrType" + Cell "CustomSymbolStrField" + Cell "CustomSymbolStrFcn" + Cell "CustomSymbolStrFcnArg" + Cell "CustomSymbolStrBlkIO" + Cell "CustomSymbolStrTmpVar" + Cell "CustomSymbolStrMacro" + Cell "CustomSymbolStrUtil" + Cell "ReqsInCode" + PropName "DisabledProps" + } + ForceParamTrailComments off + GenerateComments on + CommentStyle "Auto" + IgnoreCustomStorageClasses on + IgnoreTestpoints off + IncHierarchyInIds off + MaxIdLength 31 + PreserveName off + PreserveNameWithParent off + ShowEliminatedStatement off + OperatorAnnotations off + IncAutoGenComments off + SimulinkDataObjDesc off + SFDataObjDesc off + MATLABFcnDesc off + IncDataTypeInIds off + MangleLength 1 + CustomSymbolStrGlobalVar "$R$N$M" + CustomSymbolStrType "$N$R$M_T" + CustomSymbolStrField "$N$M" + CustomSymbolStrFcn "$R$N$M$F" + CustomSymbolStrFcnArg "rt$I$N$M" + CustomSymbolStrBlkIO "rtb_$N$M" + CustomSymbolStrTmpVar "$N$M" + CustomSymbolStrMacro "$R$N$M" + CustomSymbolStrUtil "$N$C" + DefineNamingRule "None" + ParamNamingRule "None" + SignalNamingRule "None" + InsertBlockDesc off + InsertPolySpaceComments off + SimulinkBlockComments on + MATLABSourceComments off + EnableCustomComments off + InternalIdentifier "Shortened" + InlinedPrmAccess "Literals" + ReqsInCode off + UseSimReservedNames off + } + Simulink.GRTTargetCC { + $BackupClass "Simulink.TargetCC" + $ObjectID 17 + Version "1.14.2" + Array { + Type "Cell" + Dimension 14 + Cell "GeneratePreprocessorConditionals" + Cell "IncludeMdlTerminateFcn" + Cell "CombineOutputUpdateFcns" + Cell "SuppressErrorStatus" + Cell "ERTCustomFileBanners" + Cell "GenerateSampleERTMain" + Cell "GenerateTestInterfaces" + Cell "ModelStepFunctionPrototypeControlCompliant" + Cell "GenerateAllocFcn" + Cell "PurelyIntegerCode" + Cell "SupportComplex" + Cell "SupportAbsoluteTime" + Cell "SupportContinuousTime" + Cell "SupportNonInlinedSFcns" + PropName "DisabledProps" + } + TargetFcnLib "ansi_tfl_table_tmw.mat" + TargetLibSuffix "" + TargetPreCompLibLocation "" + GenFloatMathFcnCalls "NOT IN USE" + TargetLangStandard "C89/C90 (ANSI)" + CodeReplacementLibrary "None" + UtilityFuncGeneration "Auto" + ERTMultiwordTypeDef "System defined" + ERTMultiwordLength 256 + MultiwordLength 2048 + GenerateFullHeader on + GenerateSampleERTMain off + GenerateTestInterfaces off + ModelReferenceCompliant on + ParMdlRefBuildCompliant on + CompOptLevelCompliant on + ConcurrentExecutionCompliant on + IncludeMdlTerminateFcn on + GeneratePreprocessorConditionals "Disable all" + CombineOutputUpdateFcns on + CombineSignalStateStructs off + SuppressErrorStatus off + ERTFirstTimeCompliant off + IncludeFileDelimiter "Auto" + ERTCustomFileBanners off + SupportAbsoluteTime on + LogVarNameModifier "rt_" + MatFileLogging on + MultiInstanceERTCode off + CodeInterfacePackaging "Nonreusable function" + SupportNonFinite on + SupportComplex on + PurelyIntegerCode off + SupportContinuousTime on + SupportNonInlinedSFcns on + SupportVariableSizeSignals off + EnableShiftOperators on + ParenthesesLevel "Nominal" + MATLABClassNameForMDSCustomization "Simulink.SoftwareTarget.GRTCustomization" + ModelStepFunctionPrototypeControlCompliant off + CPPClassGenCompliant on + AutosarCompliant off + GRTInterface off + GenerateAllocFcn off + UseMalloc off + ExtMode off + ExtModeStaticAlloc off + ExtModeTesting off + ExtModeStaticAllocSize 1000000 + ExtModeTransport 0 + ExtModeMexFile "ext_comm" + ExtModeIntrfLevel "Level1" + RTWCAPISignals off + RTWCAPIParams off + RTWCAPIStates off + RTWCAPIRootIO off + GenerateASAP2 off + MultiInstanceErrorCode "Error" + } + PropName "Components" + } + } + hdlcoderui.hdlcc { + $ObjectID 18 + Version "1.14.2" + Description "HDL Coder custom configuration component" + Name "HDL Coder" + Array { + Type "Cell" + Dimension 1 + Cell " " + PropName "HDLConfigFile" + } + HDLCActiveTab "0" + } + PropName "Components" + } + Name "Configuration" + CurrentDlgPage "Solver" + ConfigPrmDlgPosition [ 420, 220, 1500, 860 ] + } + PropName "ConfigurationSets" + } + Simulink.ConfigSet { + $PropName "ActiveConfigurationSet" + $ObjectID 7 + } + Object { + $PropName "DataTransfer" + $ObjectID 19 + $ClassName "Simulink.GlobalDataTransfer" + DefaultTransitionBetweenSyncTasks "Ensure deterministic transfer (maximum delay)" + DefaultTransitionBetweenAsyncTasks "Ensure data integrity only" + DefaultTransitionBetweenContTasks "Ensure deterministic transfer (minimum delay)" + DefaultExtrapolationMethodBetweenContTasks "None" + AutoInsertRateTranBlk [0] + } + ExplicitPartitioning off + BlockDefaults { + ForegroundColor "black" + BackgroundColor "white" + DropShadow off + NamePlacement "normal" + FontName "Helvetica" + FontSize 10 + FontWeight "normal" + FontAngle "normal" + ShowName on + BlockRotation 0 + BlockMirror off + } + AnnotationDefaults { + HorizontalAlignment "center" + VerticalAlignment "middle" + ForegroundColor "black" + BackgroundColor "white" + DropShadow off + FontName "Helvetica" + FontSize 10 + FontWeight "normal" + FontAngle "normal" + UseDisplayTextAsClickCallback off + } + LineDefaults { + FontName "Helvetica" + FontSize 9 + FontWeight "normal" + FontAngle "normal" + } + MaskDefaults { + SelfModifiable "off" + IconFrame "on" + IconOpaque "on" + RunInitForIconRedraw "off" + IconRotate "none" + PortRotate "default" + IconUnits "autoscale" + } + MaskParameterDefaults { + Evaluate "on" + Tunable "on" + NeverSave "off" + Internal "off" + ReadOnly "off" + Enabled "on" + Visible "on" + ToolTip "on" + } + BlockParameterDefaults { + Block { + BlockType Inport + Port "1" + OutputFunctionCall off + OutMin "[]" + OutMax "[]" + OutDataTypeStr "Inherit: auto" + LockScale off + BusOutputAsStruct off + PortDimensions "-1" + VarSizeSig "Inherit" + SampleTime "-1" + SignalType "auto" + SamplingMode "auto" + LatchByDelayingOutsideSignal off + LatchInputForFeedbackSignals off + Interpolate on + } + Block { + BlockType Outport + Port "1" + OutMin "[]" + OutMax "[]" + OutDataTypeStr "Inherit: auto" + LockScale off + BusOutputAsStruct off + PortDimensions "-1" + VarSizeSig "Inherit" + SampleTime "-1" + SignalType "auto" + SamplingMode "auto" + SourceOfInitialOutputValue "Dialog" + OutputWhenDisabled "held" + InitialOutput "[]" + } + Block { + BlockType S-Function + FunctionName "system" + SFunctionModules "''" + PortCounts "[]" + SFunctionDeploymentMode off + } + Block { + BlockType SubSystem + ShowPortLabels "FromPortIcon" + Permissions "ReadWrite" + PermitHierarchicalResolution "All" + TreatAsAtomicUnit off + MinAlgLoopOccurrences off + PropExecContextOutsideSubsystem off + CheckFcnCallInpInsideContextMsg off + SystemSampleTime "-1" + RTWSystemCode "Auto" + RTWFcnNameOpts "Auto" + RTWFileNameOpts "Auto" + FunctionInterfaceSpec "void_void" + FunctionWithSeparateData off + RTWMemSecFuncInitTerm "Inherit from model" + RTWMemSecFuncExecute "Inherit from model" + RTWMemSecDataConstants "Inherit from model" + RTWMemSecDataInternal "Inherit from model" + RTWMemSecDataParameters "Inherit from model" + SimViewingDevice off + DataTypeOverride "UseLocalSettings" + DataTypeOverrideAppliesTo "AllNumericTypes" + MinMaxOverflowLogging "UseLocalSettings" + Opaque off + MaskHideContents off + SFBlockType "NONE" + Variant off + GeneratePreprocessorConditionals off + ContentPreviewEnabled off + } + } + System { + Name "OpenDiscon_block" + Location [750, 130, 1550, 843] + Open on + ModelBrowserVisibility off + ModelBrowserWidth 200 + ScreenColor "white" + PaperOrientation "landscape" + PaperPositionMode "auto" + PaperType "A4" + PaperUnits "centimeters" + TiledPaperMargins [1.270000, 1.270000, 1.270000, 1.270000] + TiledPageScale 1 + ShowPageBoundaries off + ZoomFactor "100" + ReportName "simulink-default.rpt" + SIDHighWatermark "17" + Block { + BlockType SubSystem + Name "OpenDiscon" + SID "2" + Ports [11, 4] + Position [105, 101, 440, 499] + ZOrder 2 + RequestExecContextInheritance off + System { + Name "OpenDiscon" + Location [750, 130, 1550, 843] + Open off + ModelBrowserVisibility off + ModelBrowserWidth 200 + ScreenColor "white" + PaperOrientation "landscape" + PaperPositionMode "auto" + PaperType "A4" + PaperUnits "centimeters" + TiledPaperMargins [1.270000, 1.270000, 1.270000, 1.270000] + TiledPageScale 1 + ShowPageBoundaries off + ZoomFactor "100" + Block { + BlockType Inport + Name "derating ratio [-]" + SID "3" + Position [20, 38, 50, 52] + ZOrder 2 + IconDisplay "Port number" + } + Block { + BlockType Inport + Name "generator speed [rad/s]" + SID "4" + Position [20, 68, 50, 82] + ZOrder 3 + Port "2" + IconDisplay "Port number" + } + Block { + BlockType Inport + Name "rotor speed [rad/s]" + SID "17" + Position [20, 338, 50, 352] + ZOrder 16 + Port "3" + IconDisplay "Port number" + } + Block { + BlockType Inport + Name "azimuth [deg]" + SID "5" + Position [20, 98, 50, 112] + ZOrder 4 + Port "4" + IconDisplay "Port number" + } + Block { + BlockType Inport + Name "yaw error [deg]" + SID "6" + Position [20, 128, 50, 142] + ZOrder 5 + Port "5" + IconDisplay "Port number" + } + Block { + BlockType Inport + Name "blade 1 root Mx [kNm]" + SID "7" + Position [20, 158, 50, 172] + ZOrder 6 + Port "6" + IconDisplay "Port number" + } + Block { + BlockType Inport + Name "blade 1 root My [kNm]" + SID "8" + Position [20, 188, 50, 202] + ZOrder 7 + Port "7" + IconDisplay "Port number" + } + Block { + BlockType Inport + Name "blade 2 root Mx [kNm]" + SID "9" + Position [20, 218, 50, 232] + ZOrder 8 + Port "8" + IconDisplay "Port number" + } + Block { + BlockType Inport + Name "blade 2 root My [kNm]" + SID "10" + Position [20, 248, 50, 262] + ZOrder 9 + Port "9" + IconDisplay "Port number" + } + Block { + BlockType Inport + Name "blade 3 root Mx [kNm]" + SID "11" + Position [20, 278, 50, 292] + ZOrder 10 + Port "10" + IconDisplay "Port number" + } + Block { + BlockType Inport + Name "blade 3 root My [kNm]" + SID "12" + Position [20, 308, 50, 322] + ZOrder 11 + Port "11" + IconDisplay "Port number" + } + Block { + BlockType S-Function + Name "S-Function" + SID "1" + Ports [11, 4] + Position [165, 20, 305, 370] + ZOrder 1 + FunctionName "OpenDiscon" + EnableBusSupport off + } + Block { + BlockType Outport + Name "generator torque demand [kNm]" + SID "13" + Position [395, 63, 425, 77] + ZOrder 12 + IconDisplay "Port number" + } + Block { + BlockType Outport + Name "blade 1 pitch demand [deg]" + SID "14" + Position [395, 148, 425, 162] + ZOrder 13 + Port "2" + IconDisplay "Port number" + } + Block { + BlockType Outport + Name "blade 2 pitch demand [deg]" + SID "15" + Position [395, 233, 425, 247] + ZOrder 14 + Port "3" + IconDisplay "Port number" + } + Block { + BlockType Outport + Name "blade 3 pitch demand [deg]" + SID "16" + Position [395, 318, 425, 332] + ZOrder 15 + Port "4" + IconDisplay "Port number" + } + Line { + ZOrder 15 + SrcBlock "derating ratio [-]" + SrcPort 1 + Points [95, 0] + DstBlock "S-Function" + DstPort 1 + } + Line { + ZOrder 17 + SrcBlock "azimuth [deg]" + SrcPort 1 + Points [95, 0] + DstBlock "S-Function" + DstPort 3 + } + Line { + ZOrder 22 + SrcBlock "blade 2 root My [kNm]" + SrcPort 1 + Points [95, 0] + DstBlock "S-Function" + DstPort 8 + } + Line { + ZOrder 23 + SrcBlock "blade 3 root Mx [kNm]" + SrcPort 1 + DstBlock "S-Function" + DstPort 9 + } + Line { + ZOrder 18 + SrcBlock "yaw error [deg]" + SrcPort 1 + Points [95, 0] + DstBlock "S-Function" + DstPort 4 + } + Line { + ZOrder 19 + SrcBlock "blade 1 root Mx [kNm]" + SrcPort 1 + Points [95, 0] + DstBlock "S-Function" + DstPort 5 + } + Line { + ZOrder 20 + SrcBlock "blade 1 root My [kNm]" + SrcPort 1 + Points [95, 0] + DstBlock "S-Function" + DstPort 6 + } + Line { + ZOrder 21 + SrcBlock "blade 2 root Mx [kNm]" + SrcPort 1 + Points [95, 0] + DstBlock "S-Function" + DstPort 7 + } + Line { + ZOrder 16 + SrcBlock "generator speed [rad/s]" + SrcPort 1 + Points [95, 0] + DstBlock "S-Function" + DstPort 2 + } + Line { + ZOrder 28 + SrcBlock "S-Function" + SrcPort 4 + DstBlock "blade 3 pitch demand [deg]" + DstPort 1 + } + Line { + ZOrder 27 + SrcBlock "S-Function" + SrcPort 3 + DstBlock "blade 2 pitch demand [deg]" + DstPort 1 + } + Line { + ZOrder 26 + SrcBlock "S-Function" + SrcPort 2 + DstBlock "blade 1 pitch demand [deg]" + DstPort 1 + } + Line { + ZOrder 29 + SrcBlock "S-Function" + SrcPort 1 + DstBlock "generator torque demand [kNm]" + DstPort 1 + } + Line { + ZOrder 35 + SrcBlock "blade 3 root My [kNm]" + SrcPort 1 + DstBlock "S-Function" + DstPort 10 + } + Line { + ZOrder 36 + SrcBlock "rotor speed [rad/s]" + SrcPort 1 + DstBlock "S-Function" + DstPort 11 + } + } + } + } +} diff --git a/CONFIGURATION/CL-Windcon/src/sfunc/makeOpenDiscon.m b/CONFIGURATION/CL-Windcon/src/sfunc/makeOpenDiscon.m new file mode 100644 index 0000000..15c87bc --- /dev/null +++ b/CONFIGURATION/CL-Windcon/src/sfunc/makeOpenDiscon.m @@ -0,0 +1,3 @@ +function makeOpenDiscon + +mex -output OpenDiscon -I${OPENWITCON_INCLUDE_DIRS} -I${OPENDISCON_INCLUDE_DIRS} ${OPENWITCON_SOURCES} ${OPENDISCON_SOURCES} diff --git a/CONFIGURATION/CL-Windcon/src/sfunc/sfunc.c b/CONFIGURATION/CL-Windcon/src/sfunc/sfunc.c new file mode 100644 index 0000000..67969c9 --- /dev/null +++ b/CONFIGURATION/CL-Windcon/src/sfunc/sfunc.c @@ -0,0 +1,1612 @@ +/* +Copyright (C) 2015-2017 IK4-IKERLAN + +This file is part of OpenDiscon. + +OpenDiscon 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. + +OpenDiscon 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 OpenDiscon. If not, see . +*/ + +/* + * This file is based on the following template: + * File: sfuntmpl_doc.c + * Abstract: + * A 'C' template for a level 2 S-function. + * + * See sfuntmpl_basic.c + * for a basic C-MEX template file that uses the + * most common methods. + * + * Copyright 1990-2013 The MathWorks, Inc. + */ + + +/* + * You must specify the S_FUNCTION_NAME as the name of your S-function. + */ + +#define S_FUNCTION_NAME OpenDiscon +#define S_FUNCTION_LEVEL 2 + +#include "ikClwindconWTConfig.h" + +/* + * Need to include simstruc.h for the definition of the SimStruct and + * its associated macro definitions. + * + * The following headers are included by matlabroot/simulink/include/simstruc.h + * when compiling as a MEX file: + * + * matlabroot/extern/include/tmwtypes.h - General types, e.g. real_T + * matlabroot/extern/include/mex.h - MATLAB MEX file API routines + * matlabroot/extern/include/matrix.h - MATLAB MEX file API routines + * + * The following headers are included by matlabroot/simulink/include/simstruc.h + * when compiling your S-function with RTW: + * + * matlabroot/extern/include/tmwtypes.h - General types, e.g. real_T + * matlabroot/rtw/c/libsrc/rt_matrx.h - Macros for MATLAB API routines + * + */ +#include "simstruc.h" + + +/* Error handling + * -------------- + * + * You should use the following technique to report errors encountered within + * an S-function: + * + * ssSetErrorStatus(S,"error encountered due to ..."); + * return; + * + * Note that the 2nd argument to ssSetErrorStatus must be persistent memory. + * It cannot be a local variable in your procedure. For example the following + * will cause unpredictable errors: + * + * mdlOutputs() + * { + * char msg[256]; {ILLEGAL: to fix use "static char msg[256];"} + * sprintf(msg,"Error due to %s", string); + * ssSetErrorStatus(S,msg); + * return; + * } + * + * The ssSetErrorStatus error handling approach is the suggested alternative + * to using the mexErrMsgTxt function. MexErrMsgTxt uses "exception handling" + * to immediately terminate S-function execution and return control to + * Simulink. In order to support exception handling inside of S-functions, + * Simulink must setup exception handlers prior to each S-function invocation. + * This introduces overhead into simulation. + * + * If you do not call mexErrMsgTxt or any other routines that cause exceptions, + * then you should use SS_OPTION_EXCEPTION_FREE_CODE S-function option. This + * is done by issuing the following command in the mdlInitializeSizes function: + * + * ssSetOptions(S, SS_OPTION_EXCEPTION_FREE_CODE); + * + * Setting this option, will increase the performance of your S-function by + * allowing Simulink to bypass the exception handling setup that is usually + * performed prior to each S-function invocation. Extreme care must be taken + * to verify that your code is exception free when using the + * SS_OPTION_EXCEPTION_FREE_CODE option. If your S-function generates + * an exception when this option is set, unpredictable results will occur. + * + * Exception free code refers to code which never "long jumps". Your S-function + * is not exception free if it contains any routine which when called has + * the potential of long jumping. For example mexErrMsgTxt throws an exception + * (i.e. long jumps) when called, thus ending execution of your S-function. + * Use of mxCalloc may cause unpredictable problems in the event of a memory + * allocation error since mxCalloc will long jump. If memory allocation is + * needed, you should use the stdlib.h calloc routine directly and perform + * your own error handling. + * + * All mex* routines have the potential of long jumping (i.e. throwing an + * exception). In addition several mx* routines have the potential of + * long jumping. To avoid any difficulties, only the routines which get + * a pointer or determine the size of parameters should be used. For example + * the following will never throw an exception: mxGetPr, mxGetData, + * mxGetNumberOfDimensions, mxGetM, mxGetN, mxGetNumberOfElements. + * + * If all of your "run-time" methods within your S-function are exception + * free, then you can use the option: + * ssSetOptions(S, SS_OPTION_RUNTIME_EXCEPTION_FREE_CODE); + * The other methods in your S-function need not be exception free. The + * run-time methods include any of the following: + * mdlGetTimeOfNextVarHit, mdlOutputs, mdlUpdate, and mdlDerivatives + * + * Warnings & Printf's + * ------------------- + * You can use ssWarning(S,msg) to display a warning. + * - When the S-function is compiled via mex for use with Simulink, + * ssWarning equates to mexWarnMsgTxt. + * - When the S-function is used with Real-Time Workshop, + * ssWarning(S,msg) equates to + * printf("Warning: in block '%s', '%s'\n", ssGetPath(S),msg); + * if the target has stdio facilities, otherwise it becomes a comment and + * is disabled. + * + * You can use ssPrintf(fmt, ...) to print a message. + * - When the S-function is compiled via mex for use with Simulink, + * ssPrintf equates to mexPrintf. + * - When the S-function is used with Real-Time Workshop, + * ssPrintf equates to printf, if the target has stdio facilities, + * otherwise it becomes a call to a empty function (rtPrintfNoOp). + * - In the case of Real-Time Workshop which may or may not have stdio + * facilities, to generate the most efficient code use: + * #if defined(SS_STDIO_AVAILABLE) + * ssPrintf("my message ..."); + * #endif + * - You can also use this technique to do other standard I/O related items, + * such as: + * #if defined(SS_STDIO_AVAILABLE) + * if ((fp=fopen(file,"w")) == NULL) { + * ssSetErrorStatus(S,"open failed"); + * return; + * } + * ... + * #endif + */ + +/*====================* + * S-function methods * + *====================*/ + +/* + * Level 2 S-function methods + * -------------------------- + * Notation: "=>" indicates method is required. + * [method] indicates method is optional. + * + * Note, many of the methods below are only available for use in level 2 + * C-MEX S-functions. + * + * Model Initialization in Simulink + * -------------------------------- + *=> mdlInitializeSizes - Initialize SimStruct sizes array + * + * [mdlSetInputPortFrameData] - Optional method. Check and set input and + * output port frame data attributes. + * + * NOTE: An S-function cannot use mdlSetInput(Output)PortWidth and + * mdlSetInput(Output)PortDimensionInfo at the same time. It can use + * either a width or dimension method, but not both. + * + * [mdlSetInputPortWidth] - Optional method. Check and set input and + * optionally other port widths. + * [mdlSetOutputPortWidth] - Optional method. Check and set output + * and optionally other port widths. + * + * [mdlSetInputPortDimensionInfo] + * - Optional method. Check and set input and + * optionally other port dimensions. + * [mdlSetOutputPortDimensionInfo] + * - Optional method. Check and set output + * and optionally other port dimensions. + * [mdlSetDefaultPortDimensionInfo] + * - Optional method. Set dimensions of all + * input and output ports that have unknown + * dimensions. + * + * [mdlSetInputPortSampleTime] - Optional method. Check and set input + * port sample time and optionally other port + * sample times. + * [mdlSetOutputPortSampleTime]- Optional method. Check and set output + * port sample time and optionally other port + * sample times. + *=> mdlInitializeSampleTimes - Initialize sample times and optionally + * function-call connections. + * + * [mdlSetInputPortDataType] - Optional method. Check and set input port + * data type. See SS_DOUBLE to SS_BOOLEAN in + * simstruc_types.h for built-in data types. + * [mdlSetOutputPortDataType] - Optional method. Check and set output port + * data type. See SS_DOUBLE to SS_BOOLEAN in + * simstruc_types.h for built-in data types. + * [mdlSetDefaultPortDataTypes] - Optional method. Set data types of all + * dynamically typed input and output ports. + * + * [mdlInputPortComplexSignal] - Optional method. Check and set input + * port complexity attribute (COMPLEX_YES, + * COMPLEX_NO). + * [mdlOutputPortComplexSignal] - Optional method. Check and set output + * port complexity attribute (COMPLEX_YES, + * COMPLEX_NO). + * [mdlSetDefaultPortComplexSignals] + * - Optional method. Set complex signal flags + * of all input and output ports who + * have their complex signals set to + * COMPLEX_INHERITED (dynamic complexity). + * + * [mdlSetWorkWidths] - Optional method. Set the state, iwork, + * rwork, pwork, dwork, etc sizes. + * + * [mdlStart] - Optional method. Perform actions such + * as allocating memory and attaching to pwork + * elements. + * + * [mdlInitializeConditions] - Initialize model parameters (usually + * states). Will not be called if your + * S-function does not have an initialize + * conditions method. + * + * ['constant' mdlOutputs] - Execute blocks with constant sample + * times. These are only executed once + * here. + * + * [mdlSetSimState] - Optional method. Load the complete simulation + * state for this block, which is called when + * starting the simulation from an initial + * simulation state and this s-function has set + * its ssSetSimStateCompliance to + * USE_CUSTOM_SIM_STATE. See also mdlGetSimState + * + * Model simulation loop in Simulink + * --------------------------------- + * [mdlCheckParameters] - Optional method. Will be called at + * any time during the simulation loop when + * parameters change. + * SimulationLoop: + * [mdlProcessParameters] - Optional method. Called during + * simulation after parameters have been + * changed and verified to be okay by + * mdlCheckParameters. The processing is + * done at the "top" of the simulation loop + * when it is safe to process the changed + * parameters. + * [mdlGetTimeOfNextVarHit] - Optional method. If your S-function + * has a variable step sample time, then + * this method will be called. + * [mdlInitializeConditions]- Optional method. Only called if your + * S-function resides in an enabled + * subsystem configured to reset states, + * and the subsystem has just enabled. + * => mdlOutputs - Major output call (usually updates + * output signals). + * [mdlUpdate] - Update the discrete states, etc. + * + * Integration (Minor time step) + * [mdlDerivatives] - Compute the derivatives. + * Do + * [mdlOutputs] + * [mdlDerivatives] + * EndDo - number of iterations depends on solver + * Do + * [mdlOutputs] + * [mdlZeroCrossings] + * EndDo - number of iterations depends on zero crossings signals + * EndIntegration + * + * EndSimulationLoop + * + * [mdlGetSimState] - Optional method. Called to get the complete simulation + * state for this block if the model is configured to + * save its final simulation state and this + * S-Function has set its ssSetSimStateCompliance to + * USE_CUSTOM_SIM_STATE. See also mdlSetSimState + * + * => mdlTerminate - End of model housekeeping - free memory, etc. + * + * Model initialization for code generation (rtwgen) + * ------------------------------------------------- + * + * + * [mdlRTW] - Optional method. Only called when + * generating code to add information to the + * model.rtw file which is used by the + * Real-Time Workshop. + * + * mdlTerminate - End of model housekeeping - free memory, + * etc. + * + * Noninlined S-function execution in Real-Time Workshop + * ----------------------------------------------------- + * 1) The results of most initialization methods are 'compiled' into + * the generated code and many methods are not called. + * 2) Noninlined S-functions are limited in several ways, for example + * parameter must be real (non-complex) double vectors or strings. More + * capability is provided via the Target Language Compiler. See the + * Target Language Compiler Reference Guide. + * + * => mdlInitializeSizes - Initialize SimStruct sizes array + * => mdlInitializeSampleTimes - Initialize sample times and optionally + * function-call connections. + * [mdlInitializeConditions] - Initialize model parameters (usually + * states). Will not be called if your + * S-function does not have an initialize + * conditions method. + * [mdlStart] - Optional method. Perform actions such + * as allocating memory and attaching to pwork + * elements. + * ExecutionLoop: + * => mdlOutputs - Major output call (usually updates + * output signals). + * [mdlUpdate] - Update the discrete states, etc. + * + * Integration (Minor time step) + * [mdlDerivatives] - Compute the derivatives. + * Do + * [mdlOutputs] + * [mdlDerivatives] + * EndDo - number of iterations depends on solver + * Do + * [mdlOutputs] + * [mdlZeroCrossings] + * EndDo - number of iterations depends on zero crossings signals + * EndExecutionLoop + * mdlTerminate - End of model housekeeping - free memory, + * etc. + */ + + +/*====================================================================* + * Parameter handling methods. These methods are not supported by RTW * + *====================================================================*/ + +#define MDL_CHECK_PARAMETERS /* Change to #undef to remove function */ +#if defined(MDL_CHECK_PARAMETERS) && defined(MATLAB_MEX_FILE) + /* Function: mdlCheckParameters ============================================= + * Abstract: + * mdlCheckParameters verifies new parameter settings whenever parameter + * change or are re-evaluated during a simulation. When a simulation is + * running, changes to S-function parameters can occur at any time during + * the simulation loop. + * + * This method can be called at any point after mdlInitializeSizes. + * You should add a call to this method from mdlInitalizeSizes + * to check the parameters. After setting the number of parameters + * you expect in your S-function via ssSetNumSFcnParams(S,n), you should: + * #if defined(MATLAB_MEX_FILE) + * if (ssGetNumSFcnParams(S) == ssGetSFcnParamsCount(S)) { + * mdlCheckParameters(S); + * if (ssGetErrorStatus(S) != NULL) return; + * } else { + * return; Simulink will report a parameter mismatch error + * } + * #endif + * + * When a Simulation is running, changes to S-function parameters can + * occur either at the start of a simulation step, or during a + * simulation step. When changes to S-function parameters occur during + * a simulation step, this method is called twice, for the same + * parameter changes. The first call during the simulation step is + * used to verify that the parameters are correct. After verifying the + * new parameters, the simulation continues using the original + * parameter values until the next simulation step at which time the + * new parameter values will be used. Redundant calls are needed to + * maintain simulation consistency. Note that you cannot access the + * work, state, input, output, etc. vectors in this method. This + * method should only be used to validate the parameters. Processing + * of the parameters should be done in mdlProcessParameters. + * + * See matlabroot/simulink/src/sfun_errhdl.c for an example. + */ + static void mdlCheckParameters(SimStruct *S) + { + } +#endif /* MDL_CHECK_PARAMETERS */ + + +#define MDL_PROCESS_PARAMETERS /* Change to #undef to remove function */ +#if defined(MDL_PROCESS_PARAMETERS) && defined(MATLAB_MEX_FILE) + /* Function: mdlProcessParameters =========================================== + * Abstract: + * This method will be called after mdlCheckParameters, whenever + * parameters change or get re-evaluated. The purpose of this method is + * to process the newly changed parameters. For example "caching" the + * parameter changes in the work vectors. Note this method is not + * called when it is used with the Real-Time Workshop. Therefore, + * if you use this method in an S-function which is being used with the + * Real-Time Workshop, you must write your S-function such that it doesn't + * rely on this method. This can be done by inlining your S-function + * via the Target Language Compiler. + */ + static void mdlProcessParameters(SimStruct *S) + { + } +#endif /* MDL_PROCESS_PARAMETERS */ + + + +/*=====================================* + * Configuration and execution methods * + *=====================================*/ + +/* Function: mdlInitializeSizes =============================================== + * Abstract: + * The sizes information is used by Simulink to determine the S-function + * block's characteristics (number of inputs, outputs, states, etc.). + * + * Direct Feedthough: + * The direct feedthrough flag can be either 1=yes or 0=no. It should be + * set to 1 if the input, "u", is used in the mdlOutput or + * mdlGetTimeOfNextVarHit() function. Setting this to 0 is akin to making + * a promise that "u" will not be used in the mdlOutput or + * mdlGetTimeOfNextVarHit() function. If you break the promise, then + * unpredictable results will occur. + * + * It is very common for S-function authors to write incorrect S-functions + * when configuring the S-function direct feedthrough flag. We often find + * that S-function authors are confused about what the correct setting for + * the direct feedthrough flag should be. Part of the confusion is because + * the term direct feedthrough is misleading. To reduce the confusion, you + * can think of the direct feedthrough setting as a 'needs input' + * setting. Specifically, if the S-function access an input signal in + * either mdlOutputs() or mdlGetTimeOfNextVarHit(), then the direct + * feedthrough flag must be set to 1 (true). + * + * For example, if a level 2 C-MEX S-function uses: + * ssGetInputPortSignal(S,inputPortIndex) + * in its mdlOutputs() or mdlGetTimeOfNextVarHit() methods, then the + * S-function is required to set the direct feedthrough flag to true in + * its mdlInitializeSizes() method: + * ssSetInputPortDirectFeedThrough(S, inputPortIdx, 1); + * + * If your S-Function uses ssGetInputPortSignal() in mdlOutputs() or + * mdlGetTimeOfNextVarHit() and fails to specify + * the direct feedthrough, or specifes it wrongly using + * ssSetInputPortDirectFeedThrough(S, inputPortIdx, 0); + * then the S-Function is incorreclty written and Simulink will not + * operate correctly and may crash. + * + * When you compile your S-function with debugging, e.g., + * mex -g sfunction_name.c + * your S-function will be instrumented such that an incorrect access to + * an input signal will generate a diagnostic message. + * + * The NumContStates, NumDiscStates, NumInputs, NumOutputs, NumRWork, + * NumIWork, NumPWork NumModes, and NumNonsampledZCs widths can be set to: + * DYNAMICALLY_SIZED - In this case, they will be set to the actual + * input width, unless you have a mdlSetWorkWidths + * to set the widths. + * 0 or positive number - This explicitly sets item to the specified + * value. + */ +static void mdlInitializeSizes(SimStruct *S) +{ + /* + * inputs are: + * 0 - derating ratio [-] + * 1 - generator speed [rad/s] + * 2 - azimuth [deg] + * 3 - yaw error [deg] + * 4 - blade 1 root Mx [kNm] + * 5 - blade 1 root My [kNm] + * 6 - blade 2 root Mx [kNm] + * 7 - blade 2 root My [kNm] + * 8 - blade 3 root Mx [kNm] + * 9 - blade 3 root My [kNm] + * 10 - rotor speed [rad/s] + * + * outputs are: + * 0 - generator torque demand [kNm] + * 1 - blade 1 pitch demand [deg] + * 2 - blade 2 pitch demand [deg] + * 3 - blade 3 pitch demand [deg] + */ + int_T nInputPorts = 11; /* number of input ports */ + int_T nOutputPorts = 4; /* number of output ports */ + int_T needsInput = 1; /* direct feed through */ + + int_T inputPortIdx = 0; + int_T outputPortIdx = 0; + + + ssSetNumSFcnParams(S, 0); /* Number of expected parameters */ + if (ssGetNumSFcnParams(S) != ssGetSFcnParamsCount(S)) { + /* + * If the the number of expected input parameters is not equal + * to the number of parameters entered in the dialog box return. + * Simulink will generate an error indicating that there is a + * parameter mismatch. + */ + return; + } + + /* + * Configure tunability of parameters. By default, all parameters are + * tunable (changeable) during simulation. If there are parameters that + * cannot change during simulation, such as any parameters that would change + * the number of ports on the block, the sample time of the block, or the + * data type of a signal, mark these as non-tunable using a call like this: + * + * ssSetSFcnParamTunable(S, 0, 0); + * + * which sets parameter 0 to be non-tunable (0). + * + */ + + + /* Register the number and type of states the S-Function uses */ + + ssSetNumContStates( S, 0); /* number of continuous states */ + ssSetNumDiscStates( S, 0); /* number of discrete states */ + + + /* + * Configure the input ports. First set the number of input ports. + */ + if (!ssSetNumInputPorts(S, nInputPorts)) return; + /* + * Set input port dimensions for each input port index starting at 0. + * The following options summarize different ways for setting the input + * port dimensions. + * + * (1) If the input port dimensions are unknown, use + * ssSetInputPortDimensionInfo(S, inputPortIdx, DYNAMIC_DIMENSION)) + * + * (2) If the input signal is an unoriented vector, and the input port + * width is w, use + * ssSetInputPortVectorDimension(S, inputPortIdx, w) + * w (or width) can be DYNAMICALLY_SIZED or greater than 0. + * This is equivalent to ssSetInputPortWidth(S, inputPortIdx, w). + * + * (3) If the input signal is a matrix of dimension mxn, use + * ssSetInputPortMatrixDimensions(S, inputPortIdx, m, n) + * m and n can be DYNAMICALLY_SIZED or greater than zero. + * + * (4) Otherwise use: + * ssSetInputPortDimensionInfo(S, inputPortIdx, dimsInfo) + * This function can be used to fully or partially initialize the port + * dimensions. dimsInfo is a structure containing width, number of + * dimensions, and dimensions of the port. + */ + for (inputPortIdx = 0; inputPortIdx < nInputPorts; inputPortIdx++) + if(!ssSetInputPortVectorDimension(S, inputPortIdx, 1)) return; + /* + * Set direct feedthrough flag (1=yes, 0=no). + * A port has direct feedthrough if the input is used in either + * the mdlOutputs or mdlGetTimeOfNextVarHit functions. + * See sfuntmpl_directfeed.txt. + */ + for (inputPortIdx = 0; inputPortIdx < nInputPorts; inputPortIdx++) + ssSetInputPortDirectFeedThrough(S, inputPortIdx, needsInput); + + /* + * Configure the output ports. First set the number of output ports. + */ + if (!ssSetNumOutputPorts(S, nOutputPorts)) return; + + /* + * Set output port dimensions for each output port index starting at 0. + * See comments for setting input port dimensions. + */ + for(outputPortIdx = 0; outputPortIdx < nOutputPorts; outputPortIdx++) + if(!ssSetOutputPortVectorDimension(S,outputPortIdx,1)) return; + + /* + * Set the number of sample times. This must be a positive, nonzero + * integer indicating the number of sample times or it can be + * PORT_BASED_SAMPLE_TIMES. For multi-rate S-functions, the + * suggested approach to setting sample times is via the port + * based sample times method. When you create a multirate + * S-function, care needs to be taking to verify that when + * slower tasks are preempted that your S-function correctly + * manages data as to avoid race conditions. When port based + * sample times are specified, the block cannot inherit a constant + * sample time at any port. + */ + ssSetNumSampleTimes( S, 1); /* number of sample times */ + + /* + * Set size of the work vectors. + */ + ssSetNumRWork( S, 0); /* number of real work vector elements */ + ssSetNumIWork( S, 0); /* number of integer work vector elements*/ + ssSetNumPWork( S, 1); /* number of pointer work vector elements*/ + ssSetNumModes( S, 0); /* number of mode work vector elements */ + ssSetNumNonsampledZCs( S, 0); /* number of nonsampled zero crossings */ + + /* Specify the sim state compliance to be same as a built-in block */ + /* see sfun_simstate.c for example of other possible settings */ + ssSetSimStateCompliance(S, USE_DEFAULT_SIM_STATE); + + /* + * All options have the form SS_OPTION_ and are documented in + * matlabroot/simulink/include/simstruc.h. The options should be + * bitwise or'd together as in + * ssSetOptions(S, (SS_OPTION_name1 | SS_OPTION_name2)) + */ + + ssSetOptions( S, 0); /* general options (SS_OPTION_xx) */ + +} /* end mdlInitializeSizes */ + + +#define MDL_SET_INPUT_PORT_FRAME_DATA /* Change to #undef to remove function */ +#if defined(MDL_SET_INPUT_PORT_FRAME_DATA) && defined(MATLAB_MEX_FILE) + /* Function: mdlSetInputPortFrameData ======================================== + * Abstract: + * This method is called with the candidate frame setting (FRAME_YES, or + * FRAME_NO) for an input port. If the proposed setting is acceptable, + * the method should go ahead and set the actual frame data setting using + * ssSetInputPortFrameData(S,portIndex,frameData). If + * the setting is unacceptable an error should be generated via + * ssSetErrorStatus. Note that any other dynamic frame input or + * output ports whose frame data setting are implicitly defined by virtue + * of knowing the frame data setting of the given port can also have their + * frame data settings set via calls to ssSetInputPortFrameData and + * ssSetOutputPortFrameData. + */ + static void mdlSetInputPortFrameData(SimStruct *S, + int portIndex, + Frame_T frameData) + { + } /* end mdlSetInputPortFrameData */ +#endif /* MDL_SET_INPUT_PORT_FRAME_DATA */ + + +#define MDL_SET_INPUT_PORT_WIDTH /* Change to #undef to remove function */ +#if defined(MDL_SET_INPUT_PORT_WIDTH) && defined(MATLAB_MEX_FILE) + /* Function: mdlSetInputPortWidth =========================================== + * Abstract: + * This method is called with the candidate width for a dynamically + * sized port. If the proposed width is acceptable, the method should + * go ahead and set the actual port width using ssSetInputPortWidth. If + * the size is unacceptable an error should generated via + * ssSetErrorStatus. Note that any other dynamically sized input or + * output ports whose widths are implicitly defined by virtue of knowing + * the width of the given port can also have their widths set via calls + * to ssSetInputPortWidth or ssSetOutputPortWidth. + */ + static void mdlSetInputPortWidth(SimStruct *S, int portIndex, int width) + { + } /* end mdlSetInputPortWidth */ +#endif /* MDL_SET_INPUT_PORT_WIDTH */ + + +#define MDL_SET_OUTPUT_PORT_WIDTH /* Change to #undef to remove function */ +#if defined(MDL_SET_OUTPUT_PORT_WIDTH) && defined(MATLAB_MEX_FILE) + /* Function: mdlSetOutputPortWidth ========================================== + * Abstract: + * This method is called with the candidate width for a dynamically + * sized port. If the proposed width is acceptable, the method should + * go ahead and set the actual port width using ssSetOutputPortWidth. If + * the size is unacceptable an error should generated via + * ssSetErrorStatus. Note that any other dynamically sized input or + * output ports whose widths are implicitly defined by virtue of knowing + * the width of the given port can also have their widths set via calls + * to ssSetInputPortWidth or ssSetOutputPortWidth. + */ + static void mdlSetOutputPortWidth(SimStruct *S, int portIndex, int width) + { + } /* end mdlSetOutputPortWidth */ +#endif /* MDL_SET_OUTPUT_PORT_WIDTH */ + + +#undef MDL_SET_INPUT_PORT_DIMENSION_INFO /* Change to #define to add function */ +#if defined(MDL_SET_INPUT_PORT_DIMENSION_INFO) && defined(MATLAB_MEX_FILE) + /* Function: mdlSetInputPortDimensionInfo ==================================== + * Abstract: + * This method is called with the candidate dimensions for an input port + * with unknown dimensions. If the proposed dimensions are acceptable, the + * method should go ahead and set the actual port dimensions. + * If they are unacceptable an error should be generated via + * ssSetErrorStatus. + * Note that any other input or output ports whose dimensions are + * implicitly defined by virtue of knowing the dimensions of the given + * port can also have their dimensions set. + * + * See matlabroot/simulink/src/sfun_matadd.c for an example. + */ + static void mdlSetInputPortDimensionInfo(SimStruct *S, + int_T portIndex, + const DimsInfo_T *dimsInfo) + { + } /* mdlSetInputPortDimensionInfo */ +#endif /* MDL_SET_INPUT_PORT_DIMENSION_INFO */ + + +#undef MDL_SET_OUTPUT_PORT_DIMENSION_INFO /* Change to #define to add function*/ +#if defined(MDL_SET_OUTPUT_PORT_DIMENSION_INFO) && defined(MATLAB_MEX_FILE) + /* Function: mdlSetOutputPortDimensionInfo =================================== + * Abstract: + * This method is called with the candidate dimensions for an output port + * with unknown dimensions. If the proposed dimensions are acceptable, the + * method should go ahead and set the actual port dimensions. + * If they are unacceptable an error should be generated via + * ssSetErrorStatus. + * Note that any other input or output ports whose dimensions are + * implicitly defined by virtue of knowing the dimensions of the given + * port can also have their dimensions set. + * + * See matlabroot/simulink/src/sfun_matadd.c for an example. + */ + static void mdlSetOutputPortDimensionInfo(SimStruct *S, + int_T portIndex, + const DimsInfo_T *dimsInfo) + { + } /* mdlSetOutputPortDimensionInfo */ +#endif /* MDL_SET_OUTPUT_PORT_DIMENSION_INFO */ + + +#undef MDL_SET_DEFAULT_PORT_DIMENSION_INFO /* Change to #define to add fcn */ +#if defined(MDL_SET_DEFAULT_PORT_DIMENSION_INFO) && defined(MATLAB_MEX_FILE) + /* Function: mdlSetDefaultPortDimensionInfo ================================== + * Abstract: + * This method is called when there is not enough information in your + * model to uniquely determine the port dimensionality of signals + * entering or leaving your block. When this occurs, Simulink's + * dimension propagation engine calls this method to ask you to set + * your S-functions default dimensions for any input and output ports + * that are dynamically sized. + * + * If you do not provide this method and you have dynamically sized ports + * where Simulink does not have enough information to propagate the + * dimensionality to your S-function, then Simulink will set these unknown + * ports to the 'block width' which is determined by examining any known + * ports. If there are no known ports, the width will be set to 1. + * + * See matlabroot/simulink/src/sfun_matadd.c for an example. + */ + static void mdlSetDefaultPortDimensionInfo(SimStruct *S) + { + } /* mdlSetDefaultPortDimensionInfo */ +#endif /* MDL_SET_DEFAULT_PORT_DIMENSION_INFO */ + + +#define MDL_SET_INPUT_PORT_SAMPLE_TIME +#if defined(MDL_SET_INPUT_PORT_SAMPLE_TIME) && defined(MATLAB_MEX_FILE) + /* Function: mdlSetInputPortSampleTime ======================================= + * Abstract: + * This method is called with the candidate sample time for an inherited + * sample time input port. If the proposed sample time is acceptable, the + * method should go ahead and set the actual port sample time using + * ssSetInputPortSampleTime. If the sample time is unacceptable an error + * should generated via ssSetErrorStatus. Note that any other inherited + * input or output ports whose sample times are implicitly defined by + * virtue of knowing the sample time of the given port can also have + * their sample times set via calls to ssSetInputPortSampleTime or + * ssSetOutputPortSampleTime. + * + * When inherited port based sample times are specified, we are guaranteed + * that the sample time will be one of the following: + * [sampleTime, offsetTime] + * continuous [0.0 , 0.0 ] + * discrete [period , offset ] where 0.0 < period < inf + * 0.0 <= offset < period + * Constant, triggered, and variable step sample times will not be + * propagated to S-functions with port based sample times. + * + * Generally the mdlSetInputPortSampleTime or mdlSetOutputPortSampleTime + * is called once with the input port sample time. However, there can be + * cases where this function will be called more than once. This happens + * when the simulation engine is converting continuous sample times to + * continuous but fixed in minor steps sample times. When this occurs, the + * original values of the sample times specified in mdlInitializeSizes + * will be restored before calling this method again. + * + * The final sample time specified at the port may be different (but + * equivalent to) from what was specified in this method. This occurs + * when: + * o) Using a fixed step solver and the port has a continuous but fixed + * in minor step sample time. In this case the sample time will + * be converted to the fundamental sample time for the model. + * o) We are adjusting sample times for numerical correctness. For + * example [0.2499999999999, 0] is converted to [0.25, 0]. + * S-functions are not explicitly notified of "converted" sample times. + * They can examine the final sample times in mdlInitializeSampleTimes. + */ + static void mdlSetInputPortSampleTime(SimStruct *S, + int_T portIdx, + real_T sampleTime, + real_T offsetTime) + { + } /* end mdlSetInputPortSampleTime */ +#endif /* MDL_SET_INPUT_PORT_SAMPLE_TIME */ + + +#define MDL_SET_OUTPUT_PORT_SAMPLE_TIME +#if defined(MDL_SET_OUTPUT_PORT_SAMPLE_TIME) && defined(MATLAB_MEX_FILE) + /* Function: mdlSetOutputPortSampleTime ====================================== + * Abstract: + * This method is called with the candidate sample time for an inherited + * sample time output port. If the proposed sample time is acceptable, the + * method should go ahead and set the actual port sample time using + * ssSetOutputPortSampleTime. If the sample time is unacceptable an error + * should generated via ssSetErrorStatus. Note that any other inherited + * input or output ports whose sample times are implicitly defined by + * virtue of knowing the sample time of the given port can also have + * their sample times set via calls to ssSetInputPortSampleTime or + * ssSetOutputPortSampleTime. + * + * Normally, sample times are propagated forwards, however if sources + * feeding this block have an inherited sample time, then Simulink + * may choose to back propagate known sample times to this block. + * When back propagating sample times, we call this method in succession + * for all inherited output port signals. + * + * See mdlSetInputPortSampleTimes for more information about when this + * method is called. + */ + static void mdlSetOutputPortSampleTime(SimStruct *S, + int_T portIdx, + real_T sampleTime, + real_T offsetTime) + { + } /* end mdlSetOutputPortSampleTime */ +#endif /* MDL_SET_OUTPUT_PORT_SAMPLE_TIME */ + + +/* Function: mdlInitializeSampleTimes ========================================= + * Abstract: + * + * This function is used to specify the sample time(s) for your S-function. + * You must register the same number of sample times as specified in + * ssSetNumSampleTimes. If you specify that you have no sample times, then + * the S-function is assumed to have one inherited sample time. + * + * The sample times are specified as pairs "[sample_time, offset_time]" + * via the following macros: + * ssSetSampleTime(S, sampleTimePairIndex, sample_time) + * ssSetOffsetTime(S, offsetTimePairIndex, offset_time) + * Where sampleTimePairIndex starts at 0. + * + * The valid sample time pairs are (upper case values are macros defined + * in simstruc.h): + * + * [CONTINUOUS_SAMPLE_TIME, 0.0 ] + * [CONTINUOUS_SAMPLE_TIME, FIXED_IN_MINOR_STEP_OFFSET] + * [discrete_sample_period, offset ] + * [VARIABLE_SAMPLE_TIME , 0.0 ] + * + * Alternatively, you can specify that the sample time is inherited from the + * driving block in which case the S-function can have only one sample time + * pair: + * + * [INHERITED_SAMPLE_TIME, 0.0 ] + * or + * [INHERITED_SAMPLE_TIME, FIXED_IN_MINOR_STEP_OFFSET] + * + * The following guidelines may help aid in specifying sample times: + * + * o A continuous function that changes during minor integration steps + * should register the [CONTINUOUS_SAMPLE_TIME, 0.0] sample time. + * o A continuous function that does not change during minor integration + * steps should register the + * [CONTINUOUS_SAMPLE_TIME, FIXED_IN_MINOR_STEP_OFFSET] + * sample time. + * o A discrete function that changes at a specified rate should register + * the discrete sample time pair + * [discrete_sample_period, offset] + * where + * discrete_sample_period > 0.0 and + * 0.0 <= offset < discrete_sample_period + * o A discrete function that changes at a variable rate should + * register the variable step discrete [VARIABLE_SAMPLE_TIME, 0.0] + * sample time. The mdlGetTimeOfNextVarHit function is called to get + * the time of the next sample hit for the variable step discrete task. + * Note, the VARIABLE_SAMPLE_TIME can be used with variable step + * solvers only. + * o Discrete blocks which can operate in triggered subsystems. For your + * block to operate correctly in a triggered subsystem or a periodic + * system it must register [INHERITED_SAMPLE_TIME, 0.0]. In a triggered + * subsystem after sample times have been propagated throughout the + * block diagram, the assigned sample time to the block will be + * [INHERITED_SAMPLE_TIME, INHERITED_SAMPLE_TIME]. Typically discrete + * blocks which can be periodic or reside within triggered subsystems + * need to register the inherited sample time and the option + * SS_OPTION_DISALLOW_CONSTANT_SAMPLE_TIME. Then in mdlSetWorkWidths, they + * need to verify that they were assigned a discrete or triggered + * sample time. To do this: + * mdlSetWorkWidths: + * if (ssGetSampleTime(S, 0) == CONTINUOUS_SAMPLE_TIME) { + * ssSetErrorStatus(S, "This block cannot be assigned a " + * "continuous sample time"); + * } + * + * If your function has no intrinsic sample time, then you should indicate + * that your sample time is inherited according to the following guidelines: + * + * o A function that changes as its input changes, even during minor + * integration steps should register the [INHERITED_SAMPLE_TIME, 0.0] + * sample time. + * o A function that changes as its input changes, but doesn't change + * during minor integration steps (i.e., held during minor steps) should + * register the [INHERITED_SAMPLE_TIME, FIXED_IN_MINOR_STEP_OFFSET] + * sample time. + * + * To check for a sample hit during execution (in mdlOutputs or mdlUpdate), + * you should use the ssIsSampleHit or ssIsContinuousTask macros. + * For example, if your first sample time is continuous, then you + * used the following code-fragment to check for a sample hit. Note, + * you would get incorrect results if you used ssIsSampleHit(S,0,tid). + * if (ssIsContinuousTask(S,tid)) { + * } + * If say, you wanted to determine if the third (discrete) task has a hit, + * then you would use the following code-fragment: + * if (ssIsSampleHit(S,2,tid) { + * } + * + */ +static void mdlInitializeSampleTimes(SimStruct *S) +{ + /* Register one pair for each sample time */ + ssSetSampleTime(S, 0, INHERITED_SAMPLE_TIME); + ssSetOffsetTime(S, 0, 0.0); + +} /* end mdlInitializeSampleTimes */ + + +#define MDL_SET_INPUT_PORT_DATA_TYPE /* Change to #undef to remove function */ +#if defined(MDL_SET_INPUT_PORT_DATA_TYPE) && defined(MATLAB_MEX_FILE) + /* Function: mdlSetInputPortDataType ========================================= + * Abstract: + * This method is called with the candidate data type id for a dynamically + * typed input port. If the proposed data type is acceptable, the method + * should go ahead and set the actual port data type using + * ssSetInputPortDataType. If the data type is unacceptable an error + * should generated via ssSetErrorStatus. Note that any other dynamically + * typed input or output ports whose data types are implicitly defined by + * virtue of knowing the data type of the given port can also have their + * data types set via calls to ssSetInputPortDataType or + * ssSetOutputPortDataType. + * + * See matlabroot/simulink/include/simstruc_types.h for built-in + * type defines: SS_DOUBLE, SS_BOOLEAN, etc. + * + * See matlabroot/simulink/src/sfun_dtype_io.c for an example. + */ + static void mdlSetInputPortDataType(SimStruct *S, int portIndex,DTypeId dType) + { + } /* mdlSetInputPortDataType */ +#endif /* MDL_SET_INPUT_PORT_DATA_TYPE */ + + +#define MDL_SET_OUTPUT_PORT_DATA_TYPE /* Change to #undef to remove function */ +#if defined(MDL_SET_OUTPUT_PORT_DATA_TYPE) && defined(MATLAB_MEX_FILE) + /* Function: mdlSetOutputPortDataType ======================================== + * Abstract: + * This method is called with the candidate data type id for a dynamically + * typed output port. If the proposed data type is acceptable, the method + * should go ahead and set the actual port data type using + * ssSetOutputPortDataType. If the data type is unacceptable an error + * should generated via ssSetErrorStatus. Note that any other dynamically + * typed input or output ports whose data types are implicitly defined by + * virtue of knowing the data type of the given port can also have their + * data types set via calls to ssSetInputPortDataType or + * ssSetOutputPortDataType. + * + * See matlabroot/simulink/src/sfun_dtype_io.c for an example. + */ + static void mdlSetOutputPortDataType(SimStruct *S,int portIndex,DTypeId dType) + { + } /* mdlSetOutputPortDataType */ +#endif /* MDL_SET_OUTPUT_PORT_DATA_TYPE */ + + +#define MDL_SET_DEFAULT_PORT_DATA_TYPES /* Change to #undef to remove function*/ +#if defined(MDL_SET_DEFAULT_PORT_DATA_TYPES) && defined(MATLAB_MEX_FILE) + /* Function: mdlSetDefaultPortDataTypes ===================================== + * Abstract: + * This method is called when there is not enough information in your + * model to uniquely determine the input and output data types + * for your block. When this occurs, Simulink's data type propagation + * engine calls this method to ask you to set your S-function default + * data type for any dynamically typed input and output ports. + * + * If you do not provide this method and you have dynamically typed + * ports where Simulink does not have enough information to propagate + * data types to your S-function, then Simulink will assign the + * data type to the largest known port data type of your S-function. + * If there are no known data types, then Simulink will set the + * data type to double. + * + * See matlabroot/simulink/src/sfun_dtype_io.c for an example. + */ + static void mdlSetDefaultPortDataTypes(SimStruct *S) + { + } /* mdlSetDefaultPortDataTypes */ +#endif /* MDL_SET_DEFAULT_PORT_DATA_TYPES */ + + +#define MDL_SET_INPUT_PORT_COMPLEX_SIGNAL /* Change to #undef to remove */ +#if defined(MDL_SET_INPUT_PORT_COMPLEX_SIGNAL) && defined(MATLAB_MEX_FILE) + /* Function: mdlSetInputPortComplexSignal ==================================== + * Abstract: + * This method is called with the candidate complexity signal setting + * (COMPLEX_YES or COMPLEX_NO) for an input port whos complex signal + * attribute is set to COMPLEX_INHERITED. If the proposed complexity is + * acceptable, the method should go ahead and set the actual complexity + * using ssSetInputPortComplexSignal. If the complex setting is + * unacceptable an error should generated via ssSetErrorStatus. Note that + * any other unknown ports whose complexity is implicitly defined by virtue + * of knowing the complexity of the given port can also have their + * complexity set via calls to ssSetInputPortComplexSignal or + * ssSetOutputPortComplexSignal. + * + * See matlabroot/simulink/src/sfun_cplx.c for an example. + */ + static void mdlSetInputPortComplexSignal(SimStruct *S, + int portIndex, + CSignal_T cSignalSetting) + { + } /* mdlSetInputPortComplexSignal */ +#endif /* MDL_SET_INPUT_PORT_COMPLEX_SIGNAL */ + + +#define MDL_SET_OUTPUT_PORT_COMPLEX_SIGNAL /* Change to #undef to remove */ +#if defined(MDL_SET_OUTPUT_PORT_COMPLEX_SIGNAL) && defined(MATLAB_MEX_FILE) + /* Function: mdlSetOutputPortComplexSignal =================================== + * Abstract: + * This method is called with the candidate complexity signal setting + * (COMPLEX_YES or COMPLEX_NO) for an output port whos complex signal + * attribute is set to COMPLEX_INHERITED. If the proposed complexity is + * acceptable, the method should go ahead and set the actual complexity + * using ssSetOutputPortComplexSignal. If the complex setting is + * unacceptable an error should generated via ssSetErrorStatus. Note that + * any other unknown ports whose complexity is implicitly defined by virtue + * of knowing the complexity of the given port can also have their + * complexity set via calls to ssSetInputPortComplexSignal or + * ssSetOutputPortComplexSignal. + * + * See matlabroot/simulink/src/sfun_cplx.c for an example. + */ + static void mdlSetOutputPortComplexSignal(SimStruct *S, + int portIndex, + CSignal_T cSignalSetting) + { + } /* mdlSetOutputPortComplexSignal */ +#endif /* MDL_SET_OUTPUT_PORT_COMPLEX_SIGNAL */ + + +#define MDL_SET_DEFAULT_PORT_COMPLEX_SIGNALS /* Change to #undef to remove */ +#if defined(MDL_SET_DEFAULT_PORT_COMPLEX_SIGNALS) && defined(MATLAB_MEX_FILE) + /* Function: mdlSetDefaultPortComplexSignals ================================ + * Abstract: + * This method is called when there is not enough information in your + * model to uniquely determine the complexity (COMPLEX_NO, COMPLEX_YES) + * of signals entering your block. When this occurs, Simulink's + * complex signal propagation engine calls this method to ask you to set + * your S-function default complexity type for any input and output ports + * who's complex signal attribute is set to COMPLEX_INHERITED. + * + * If you do not provide this method and you have COMPLEX_INHERITED + * ports where Simulink does not have enough information to propagate + * the complexity to your S-function, then Simulink will set + * these unkown ports to COMPLEX_YES if any of your S-function + * ports are currently set to COMPLEX_YES, otherwise the unknown + * ports will be set to COMPLEX_NO. + * + * See matlabroot/simulink/src/sfun_cplx.c for an example. + */ + static void mdlSetDefaultPortComplexSignals(SimStruct *S) + { + } /* mdlSetDefaultPortComplexSignals */ +#endif /* MDL_SET_DEFAULT_PORT_COMPLEX_SIGNALS */ + + +#define MDL_SET_WORK_WIDTHS /* Change to #undef to remove function */ +#if defined(MDL_SET_WORK_WIDTHS) && defined(MATLAB_MEX_FILE) + /* Function: mdlSetWorkWidths =============================================== + * Abstract: + * The optional method, mdlSetWorkWidths is called after input port + * width, output port width, and sample times of the S-function have + * been determined to set any state and work vector sizes which are + * a function of the input, output, and/or sample times. This method + * is used to specify the nonzero work vector widths via the macros + * ssNumContStates, ssSetNumDiscStates, ssSetNumRWork, ssSetNumIWork, + * ssSetNumPWork, ssSetNumModes, and ssSetNumNonsampledZCs. + * + * Run-time parameters are registered in this method using methods + * ssSetNumRunTimeParams, ssSetRunTimeParamInfo, and related methods. + * + * If you are using mdlSetWorkWidths, then any work vectors you are + * using in your S-function should be set to DYNAMICALLY_SIZED in + * mdlInitializeSizes, even if the exact value is known at that point. + * The actual size to be used by the S-function should then be specified + * in mdlSetWorkWidths. + */ + static void mdlSetWorkWidths(SimStruct *S) + { + } +#endif /* MDL_SET_WORK_WIDTHS */ + + +#define MDL_INITIALIZE_CONDITIONS /* Change to #undef to remove function */ +#if defined(MDL_INITIALIZE_CONDITIONS) + /* Function: mdlInitializeConditions ======================================== + * Abstract: + * In this function, you should initialize the continuous and discrete + * states for your S-function block. The initial states are placed + * in the state vector, ssGetContStates(S) or ssGetDiscStates(S). + * You can also perform any other initialization activities that your + * S-function may require. Note, this method will be called at the + * start of simulation and if it is present in an enabled subsystem + * configured to reset states, it will be call when the enabled subsystem + * restarts execution to reset the states. + * + * You can use the ssIsFirstInitCond(S) macro to determine if this is + * is the first time mdlInitializeConditions is being called. + */ + static void mdlInitializeConditions(SimStruct *S) + { + ikClwindconWTCon *con = (ikClwindconWTCon*) ssGetPWorkValue(S, 0); + /* Initialise controller */ + ikClwindconWTConParams param; + ikClwindconWTCon_initParams(¶m); + setParams(¶m); + ikClwindconWTCon_init(con, ¶m); + } +#endif /* MDL_INITIALIZE_CONDITIONS */ + + +#define MDL_START /* Change to #undef to remove function */ +#if defined(MDL_START) + /* Function: mdlStart ======================================================= + * Abstract: + * This function is called once at start of model execution. If you + * have states that should be initialized once, this is the place + * to do it. + */ + static void mdlStart(SimStruct *S) + { + /* Allocate controller */ + void *ptr = calloc(1, sizeof(ikClwindconWTCon)); + if (ptr == NULL) { + /* report memory allocation error */ + ssSetErrorStatus(S, "Memory allocation error"); + return; + } + ssSetPWorkValue(S, 0, ptr); + } +#endif /* MDL_START */ + + +/* Define to indicate that this S-Function has the mdlG[S]etSimState methods */ +#define MDL_SIM_STATE /* Change to #undef to remove this function */ +#if defined(MDL_SIM_STATE) + /* Function: mdlGetSimState =========================================== + * Abstract: + * Package complete simulation state (this includes all the run-time data + * that can change *after* mdlStart) as a MATLAB data structure and return + * it. + */ + static mxArray* mdlGetSimState(SimStruct* S) + { + } + + /* Function: mdlSetSimState =========================================== + * Abstract: + * Unack inSimState MATLAB data structure which contains the complete + * simulation state (this includes all the run-time data that can change + * *after* mdlStart) into the appropriate locations. + */ + static void mdlSetSimState(SimStruct* S, const mxArray* inSimState) + { + } +#endif /* MDL_SIM_STATE */ + + +#define MDL_GET_TIME_OF_NEXT_VAR_HIT /* Change to #undef to remove function */ +#if defined(MDL_GET_TIME_OF_NEXT_VAR_HIT) && (defined(MATLAB_MEX_FILE) || \ + defined(NRT)) + /* Function: mdlGetTimeOfNextVarHit ========================================= + * Abstract: + * This function is called to get the time of the next variable sample + * time hit. This function is called once for every major integration time + * step. It must return time of next hit by using ssSetTNext. The time of + * the next hit must be greater than ssGetT(S). + * + * Note, the time of next hit can be a function of the input signal(s). + */ + + static void mdlGetTimeOfNextVarHit(SimStruct *S) + { + time_T timeOfNextHit = ssGetT(S) /* + offset */ ; + ssSetTNext(S, timeOfNextHit); + } +#endif /* MDL_GET_TIME_OF_NEXT_VAR_HIT */ + + +#define MDL_ZERO_CROSSINGS /* Change to #undef to remove function */ +#if defined(MDL_ZERO_CROSSINGS) && (defined(MATLAB_MEX_FILE) || defined(NRT)) + /* Function: mdlZeroCrossings =============================================== + * Abstract: + * If your S-function has registered CONTINUOUS_SAMPLE_TIME and there + * are signals entering the S-function or internally generated signals + * which have discontinuities, you can use this method to locate the + * discontinuities. When called, this method must update the + * ssGetNonsampleZCs(S) vector. + */ + static void mdlZeroCrossings(SimStruct *S) + { + } +#endif /* MDL_ZERO_CROSSINGS */ + + +/* Function: mdlOutputs ======================================================= + * Abstract: + * In this function, you compute the outputs of your S-function + * block. Generally outputs are placed in the output vector(s), + * ssGetOutputPortSignal. + */ +static void mdlOutputs(SimStruct *S, int_T tid) +{ +} /* end mdlOutputs */ + + +#define MDL_UPDATE /* Change to #undef to remove function */ +#if defined(MDL_UPDATE) + /* Function: mdlUpdate ====================================================== + * Abstract: + * This function is called once for every major integration time step. + * Discrete states are typically updated here, but this function is useful + * for performing any tasks that should only take place once per + * integration step. + */ + static void mdlUpdate(SimStruct *S, int_T tid) + { + ikClwindconWTCon *con = (ikClwindconWTCon*) ssGetPWorkValue(S, 0); + + con->in.deratingRatio = **(ssGetInputPortRealSignalPtrs(S,0)); + con->in.externalMaximumTorque = 230.0; /* kNm */ + con->in.externalMinimumTorque = 0.0; /* kNm */ + con->in.externalMaximumPitch = 90.0; /* deg */ + con->in.externalMinimumPitch = 0.0; /* deg */ + con->in.generatorSpeed = **(ssGetInputPortRealSignalPtrs(S,1)); /* rad/s */ + con->in.rotorSpeed = **(ssGetInputPortRealSignalPtrs(S,10)); /* rad/s */ + con->in.maximumSpeed = 480.0/30*3.1416; /* rpm to rad/s */ + con->in.azimuth = **(ssGetInputPortRealSignalPtrs(S,2)); /* deg */ + con->in.maximumIndividualPitch = 10.0; /* deg */ + con->in.yawErrorReference = 0.0; /* deg */ + con->in.yawError = **(ssGetInputPortRealSignalPtrs(S,3)); /* deg */ + con->in.bladeRootMoments[0].c[0] = **(ssGetInputPortRealSignalPtrs(S,4)); /* kNm */ + con->in.bladeRootMoments[0].c[1] = **(ssGetInputPortRealSignalPtrs(S,5)); /* kNm */ + con->in.bladeRootMoments[0].c[2] = 0.0; /* kNm */ + con->in.bladeRootMoments[1].c[0] = **(ssGetInputPortRealSignalPtrs(S,6)); /* kNm */ + con->in.bladeRootMoments[1].c[1] = **(ssGetInputPortRealSignalPtrs(S,7)); /* kNm */ + con->in.bladeRootMoments[1].c[2] = 0.0; /* kNm */ + con->in.bladeRootMoments[2].c[0] = **(ssGetInputPortRealSignalPtrs(S,8)); /* kNm */ + con->in.bladeRootMoments[2].c[1] = **(ssGetInputPortRealSignalPtrs(S,9)); /* kNm */ + con->in.bladeRootMoments[2].c[2] = 0.0; /* kNm */ + + ikClwindconWTCon_step(con); + + *(ssGetOutputPortRealSignal(S,0)) = con->out.torqueDemand; /* kNm */ + *(ssGetOutputPortRealSignal(S,1)) = con->out.pitchDemandBlade1; /* deg */ + *(ssGetOutputPortRealSignal(S,2)) = con->out.pitchDemandBlade2; /* deg */ + *(ssGetOutputPortRealSignal(S,3)) = con->out.pitchDemandBlade3; /* deg */ + } +#endif /* MDL_UPDATE */ + + +#define MDL_DERIVATIVES /* Change to #undef to remove function */ +#if defined(MDL_DERIVATIVES) + /* Function: mdlDerivatives ================================================= + * Abstract: + * In this function, you compute the S-function block's derivatives. + * The derivatives are placed in the derivative vector, ssGetdX(S). + */ + static void mdlDerivatives(SimStruct *S) + { + } +#endif /* MDL_DERIVATIVES */ + + +/* Function: mdlTerminate ===================================================== + * Abstract: + * In this function, you should perform any actions that are necessary + * at the termination of a simulation. For example, if memory was allocated + * in mdlStart, this is the place to free it. + * + * Suppose your S-function allocates a few few chunks of memory in mdlStart + * and saves them in PWork. The following code fragment would free this + * memory. + * { + * int i; + * for (i = 0; i(S) + * Recall that you would have to set ssSetNum(S) + * in one of the initialization functions (mdlInitializeSizes + * or mdlSetWorkVectorWidths). + * + * See simulink/include/simulink.c for the definition (implementation) + * of this function, and ... no example yet :( + * + * 4) Finally the following functions/macros give you the ability to write + * arbitrary strings and [name, value] pairs directly into the .rtw + * file. + * + * if (!ssWriteRTWStr(S, const_char_*_string)) { + * return; + * } + * + * if (!ssWriteRTWStrParam(S, const_char_*_name, const_char_*_value)) { + * return; + * } + * + * if (!ssWriteRTWScalarParam(S, const_char_*_name, + * const_void_*_value, + * DTypeId_dtypeId)) { + * return; + * } + * + * if (!ssWriteRTWStrVectParam(S, const_char_*_name, + * const_char_*_value, + * int_num_items)) { + * return; + * } + * + * if (!ssWriteRTWVectParam(S, const_char_*_name, const_void_*_value, + * int_data_type_of_value, int_vect_len)){ + * return; + * } + * + * if (!ssWriteRTW2dMatParam(S, const_char_*_name, const_void_*_value, + * int_data_type_of_value, int_nrows, int_ncols)){ + * return; + * } + * + * The 'data_type_of_value' input argument for the above two macros is + * obtained using + * DTINFO(dTypeId, isComplex), + * where + * dTypeId: can be any one of the enum values in BuitlInDTypeID + * (SS_DOUBLE, SS_SINGLE, SS_INT8, SS_UINT8, SS_INT16, + * SS_UINT16, SS_INT32, SS_UINT32, SS_BOOLEAN defined + * in simstuc_types.h) + * isComplex: is either 0 or 1, as explained in Note-2 for + * ssWriteRTWParameters. + * + * For example DTINFO(SS_INT32,0) is a non-complex 32-bit signed + * integer. + * + * If isComplex==1, then it is assumed that 'const_void_*_value' array + * has the real and imaginary parts arranged in an interleaved manner + * (i.e., Simulink Format). + * + * If you prefer to pass the real and imaginary parts as two seperate + * arrays, you should use the follwing macros: + * + * if (!ssWriteRTWMxVectParam(S, const_char_*_name, + * const_void_*_rvalue, const_void_*_ivalue, + * int_data_type_of_value, int_vect_len)){ + * return; + * } + * + * if (!ssWriteRTWMx2dMatParam(S, const_char_*_name, + * const_void_*_rvalue, const_void_*_ivalue, + * int_data_type_of_value, + * int_nrows, int_ncols)){ + * return; + * } + * + * See simulink/include/simulink.c and simstruc.h for the definition + * (implementation) of these functions and simulink/src/ml2rtw.c for + * examples of using these functions. + * + */ + static void mdlRTW(SimStruct *S) + { + } +#endif /* MDL_RTW */ + + +/*=============================* + * Required S-function trailer * + *=============================*/ + +#ifdef MATLAB_MEX_FILE /* Is this file being compiled as a MEX-file? */ +#include "simulink.c" /* MEX-file interface mechanism */ +#else +#include "cg_sfun.h" /* Code generation registration function */ +#endif diff --git a/OpenWitcon b/OpenWitcon index 5b3537a..c9e5f02 160000 --- a/OpenWitcon +++ b/OpenWitcon @@ -1 +1 @@ -Subproject commit 5b3537a935cde8f88c80a1d012c4136a2657384e +Subproject commit c9e5f024663cbe8f3ddafb7da4d8086991fe9ccb diff --git a/README.md b/README.md index 03d6685..deb0b14 100644 --- a/README.md +++ b/README.md @@ -1,11 +1,15 @@ # OpenDiscon -Open source implementation of the legacy GHBladed external controller interface. +Open source implementation of popular wind turbine controller interfaces. -This code is for the production of a shared library implementing the DISCON interface for the DTU 10MW model. +Currently available distributions are for: +- The legacy GHBladed external controller interface (typically called DISCON, hence the name OpenDiscon). +- Level-2 MATLAB S-Function for Simulink. -The source code is at ./src. The main implementation file is discon.c. -It uses IK4-IKERLAN's library OpenWitcon, included as a submodule. +Currently available configurations are for: +- CL-Windcon (DTU 10MW model). + +This uses [IK4-IKERLAN](http://www.ikerlan.es/en/)'s library OpenWitcon, included as a submodule. Documentation is provided in Doxygen format. To generate it, run Doxygen on ./doc/Doxyfile. For compilation, run cmake here. -This will generate the VS solution or makefiles for straightforward compilation, depending on your toolchain. +This will generate the VS solution, makefiles or MATLAB function for straightforward compilation, depending on your toolchain. diff --git a/doc/CL-Windcon.dox b/doc/CL-Windcon.dox index 5a905b8..83983ac 100644 --- a/doc/CL-Windcon.dox +++ b/doc/CL-Windcon.dox @@ -34,11 +34,18 @@ along with OpenDiscon. If not, see . * OpenDiscon caters to the needs of H2020 project CL-Windcon, by providing a controller specifically * tuned for compatibility with the Innwind 10 MW reference wind turbine model. * -* @section basic Basic controller +* @section intro Introduction * -* The basic controller provided by OpenDiscon for CL-Windcon is an instance of @link ikClwindconWTCon @endlink, +* The controller provided by OpenDiscon for CL-Windcon is an instance of @link ikClwindconWTCon @endlink, * with the control loop parameters given by @link ikClwindconWTConfig.c @endlink. * +* @subsection samplinginterval Sampling interval +* +* The controller is a discrete-time implementation, with a sampling interval given in @link ikClwindconWTConfig.c @endlink, conveniently commented as follows: +* @snippet ikClwindconWTConfig.c Sampling interval +* +* @section basic Basic controller +* * @subsection dtdamper Drivetrain damper * * The drivetrain damper is an implementation of the following transfer function from [1]: @@ -47,13 +54,14 @@ along with OpenDiscon. If not, see . * @f] * * The parameters are in @link ikClwindconWTConfig.c @endlink, conveniently commented as follows: -* @snippet ikClwindconWTConfig.c CL-Windcon drivetrain damper +* @snippet ikClwindconWTConfig.c Drivetrain damper * * @subsection torquecon Torque control * * @subsubsection torquepi PI * -* The torque controller is a PI, with its parameters given in @link ikClwindconWTConfig.c @endlink, conveniently commented as follows: +* The torque controller forces the generator torque against the optimum torque curve and regulates the generator speed when the minimum speed is reached. +* It is a PI, with its parameters given in @link ikClwindconWTConfig.c @endlink, conveniently commented as follows: * @snippet ikClwindconWTConfig.c Torque PI * Note the negative gains, which are necessary to make the torque increase when the speed error (setpoint minus measurement) is negative, and viceversa. * @@ -77,17 +85,12 @@ along with OpenDiscon. If not, see . * The parameters are in @link ikClwindconWTConfig.c @endlink, conveniently commented as follows: * @snippet ikClwindconWTConfig.c 1st side-side tower mode filter * -* @subsubsection kopt Optimum torque curve -* -* Below rated wind speed, the torque controller uses a quadratic speed-torque curve, along the lines of that described by [1]. -* The parameters are in @link ikClwindconWTConfig.c @endlink, conveniently commented as follows: -* @snippet ikClwindconWTConfig.c Optimum torque -* * @subsection pitchcon Pitch control * * @subsubsection pitchpi PI * -* The pitch controller is a PI, with its parameters given in @link ikClwindconWTConfig.c @endlink, conveniently commented as follows: +* The pitch controller forces the collective blade pitch against the minimum pitch and regulates the generator speed when the maximum speed is reached. +* It is a PI, with its parameters given in @link ikClwindconWTConfig.c @endlink, conveniently commented as follows: * @snippet ikClwindconWTConfig.c Pitch PI * Note the negative gains, which are necessary to make the pitch angle increase when the speed error (setpoint minus measurement) is negative, and viceversa. * @@ -118,13 +121,69 @@ along with OpenDiscon. If not, see . * The parameters are in @link ikClwindconWTConfig.c @endlink, conveniently commented as follows: * @snippet ikClwindconWTConfig.c Gain schedule * -* @subsubsection minpitch Minimum pitch +* @section derating Derating +* +* @subsection minpitch Minimum pitch * * The minimum pitch is altered depending on the derating ratio. This is implemented as a look-up table. * * The parameters are in @link ikClwindconWTConfig.c @endlink, conveniently commented as follows: * @snippet ikClwindconWTConfig.c Minimum pitch * +* @subsection kopt Optimum torque curve +* +* Below rated wind speed, the torque controller uses a quadratic speed-torque curve, along the lines of that described by [1]. +* The gain of said curve varies depending on the derating ratio. This is implemented as a look-up table. +* +* The parameters are in @link ikClwindconWTConfig.c @endlink, conveniently commented as follows: +* @snippet ikClwindconWTConfig.c Optimum torque +* +* @section ipc Individual pitch control +* +* @subsection regipc Regular IPC +* Regular individual pitch control consists of two PI controllers, for moments around axes y and z, respectively. +* The gains are in @link ikClwindconWTConfig.c @endlink, conveniently commented as follows: +* @snippet ikClwindconWTConfig.c IPC My PI +* @snippet ikClwindconWTConfig.c IPC Mz PI +* +* Note that regular IPC gains are zero, i.e. regular IPC is disabled, in this configuration. Yaw by IPC is used instead. +* +* @subsection yawbyipc Yaw by IPC +* +* Yaw by IPC introduces a moment around axis z to make the turbine yaw. It consists of a PI controller, which regulates the yaw error. +* Its gains are in @link ikClwindconWTConfig.c @endlink, conveniently commented as follows: +* @snippet ikClwindconWTConfig.c Yaw by IPC PI +* +* The yaw error measurement is low-pass filtered with a second order filter with the following transfer function: +* @f[ +* H(s) = \frac{\omega^2}{s^2 + 2 \xi \omega s + \omega^2} +* @f] +* +* The parameters are in @link ikClwindconWTConfig.c @endlink, conveniently commented as follows: +* @snippet ikClwindconWTConfig.c Yaw by IPC lowpass filter +* +* @section ftc Fault tolerance +* +* @subsection spdman Generator speed sensor redundancy +* +* The controller uses three different generator speed measurements: +* - generator speed +* - rotor speed (approx. generator speed divided by gearbox ratio) +* - azimuth (approx. integral of rotor speed) +* +* If all three measurements are similar, the generator speed is used. +* Dissimilarities are used for sensor failure diagnosis and, if the generator speed sensor is found to have failed, the rotor speed is used instead. +* +* The parameters dictating when the measurements are dissimilar are in @link ikClwindconWTConfig.c @endlink, conveniently commented as follows: +* @snippet ikClwindconWTConfig.c Speed sensor manager +* +* @subsection faults Sensor faults +* +* [Only for DISTRIBUTION = DISCON] A simple generator speed sensor fault introduction facility is provided. +* +* The parameters governing this fault are in @link ikClwindconInputMod.c @endlink, conveniently commented as follows: +* @snippet ikClwindconInputMod.c Speed sensor fault +* * @section references References * * [1] Tony Burton, Nick Jenkins, David Sharpe, Ervin Bossanyi, Wind Energy Handbook , ISBN: 978-0-470-69975-1. diff --git a/doc/Doxyfile b/doc/Doxyfile index 0b4589a..cbb2306 100644 --- a/doc/Doxyfile +++ b/doc/Doxyfile @@ -790,9 +790,9 @@ WARN_LOGFILE = # spaces. See also FILE_PATTERNS and EXTENSION_MAPPING # Note: If this tag is empty the current directory is searched. -INPUT = ../src \ - ../OpenWitcon/src \ - ./ +INPUT = ../OpenWitcon/src \ + ./ \ + ../CONFIGURATION/CL-Windcon/src # This tag can be used to specify the character encoding of the source files # that doxygen parses. Internally doxygen uses the UTF-8 encoding. Doxygen uses @@ -906,7 +906,7 @@ EXCLUDE_SYMBOLS = # that contain example code fragments that are included (see the \include # command). -EXAMPLE_PATH = ../src +EXAMPLE_PATH = ../CONFIGURATION/CL-Windcon/src # If the value of the EXAMPLE_PATH tag contains directories, you can use the # EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp and diff --git a/doc/OpenDiscon.dox b/doc/OpenDiscon.dox index 107e101..51717c9 100644 --- a/doc/OpenDiscon.dox +++ b/doc/OpenDiscon.dox @@ -19,18 +19,22 @@ along with OpenDiscon. If not, see . /** @mainpage * -* OpenDiscon is Ikerlan's open source implementation of the legacy GHBladed DISCON interface, -* which is also used by other wind turbine simulation software packages such as FAST. -* It is based on Ikerlan's open source wind turbine control software library @ref openwitcon. +* OpenDiscon is Ikerlan's open source implementation of popular wind turbine controller interfaces. +* based on Ikerlan's open source wind turbine control software library @ref openwitcon. * * OpenDiscon is publicly available under a rather permissive @subpage license "license", so * it may be used in many ways, possibly some of which have not been imagined by the authors. -* However, it provides out-of-the-box implementations for the following uses: -* - @ref clwindcon +* However, it provides some out-of-the-box implementations. Using them requires compilation and linking. +* CMake files are provided for easy production of the project files, makefiles or MATLAB functions suitable for the +* user's toolchain and options. * -* Using OpenDiscon's out-of-the-box implementations starts with their compilation and linking. -* CMake files are provided for easy production of the project or makefiles suitable for the -* user's toolchain. Simply run CMake on the OpenDiscon directory. +* To make the above happen, run CMake on the OpenDiscon directory. +* Two options are available: +* - CONFIGURATION +* - CL-Windcon: a controller for @ref clwindcon +* - DISTRIBUTION +* - DISCON: an implementation of the legacy GHBladed DISCON interface, which is also used by other wind turbine simulation software packages such as FAST. +* - S-Function: a Simulink block implementation. * */ @@ -54,7 +58,7 @@ along with OpenDiscon. If not, see . /** @page openwitcon OpenWitcon * -* OpenWitcon is Ikerlan's open source wind turbine controller software library. It is publicly available +* OpenWitcon is Ikerlan's open source wind turbine controller software library. It is publicly available * here. * * OpenDiscon makes heavy use of OpenWitcon, and the OpenDiscon repository has the OpenWitcon repository diff --git a/doc/svg/ikClwindconWTCon_block_diagram.svg b/doc/svg/ikClwindconWTCon_block_diagram.svg index 743aa47..1b93350 100644 --- a/doc/svg/ikClwindconWTCon_block_diagram.svg +++ b/doc/svg/ikClwindconWTCon_block_diagram.svg @@ -9,9 +9,9 @@ xmlns="http://www.w3.org/2000/svg" xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" - width="570.57776mm" - height="322.54263mm" - viewBox="0 0 2021.732 1142.8675" + width="857.19366mm" + height="440.59653mm" + viewBox="0 0 3037.3 1561.1687" id="svg2" version="1.1" inkscape:version="0.92.1 r15371" @@ -807,38 +807,6 @@ transform="matrix(-0.8,0,0,-0.8,-10,0)" inkscape:connector-curvature="0" /> - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml - + @@ -1702,7 +2006,7 @@ inkscape:label="Capa 1" inkscape:groupmode="layer" id="layer1" - transform="translate(-118.09022,-48.801925)"> + transform="translate(492.13748,-48.801925)"> ikTpman - - pitch demand for blade 1 pitch demand for blade 2 pitch demand for blade 3 - generator speed - + + individual pitch control + ikIpc + 0 + blade root moments + azimuth + 0 + 0 + maximum individual pitch + + + + + + + + + + + + + + + + + yaw by ipc + ikConLoop + + + yaw error reference + yaw error + individual pitch for yaw + + -1 + + + + + + generator speed + + speed sensor manager + ikSpdman + rotor speed + azimuth + generator speed equivalent + + + diff --git a/doc/svg/ikClwindconWTCon_unit_block.svg b/doc/svg/ikClwindconWTCon_unit_block.svg index 1f3c89e..78e9f37 100644 --- a/doc/svg/ikClwindconWTCon_unit_block.svg +++ b/doc/svg/ikClwindconWTCon_unit_block.svg @@ -9,9 +9,9 @@ xmlns="http://www.w3.org/2000/svg" xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" - width="197.91646mm" - height="132.59679mm" - viewBox="0 0 701.2788 469.83109" + width="207.45264mm" + height="182.10945mm" + viewBox="0 0 735.06841 645.26965" id="svg2" version="1.1" inkscape:version="0.92.1 r15371" @@ -31,8 +31,8 @@ inkscape:stockid="Arrow1Lend"> @@ -47,8 +47,8 @@ + + + + + + + + + + + + + + + + + + + transform="translate(62.20234,-256.24311)"> external minimum torque external maximum pitch ikClwindconWTCon generator speed external maximum torque maximum speed external minimum pitch derating ratio + + + + + + blade root moments + maximum individual pitch + yaw error reference + yaw error + azimuth + + rotor speed diff --git a/doc/svg/ikSpdman_block_diagram.svg b/doc/svg/ikSpdman_block_diagram.svg new file mode 100644 index 0000000..43661b8 --- /dev/null +++ b/doc/svg/ikSpdman_block_diagram.svg @@ -0,0 +1,2194 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + generator speed + + diagnoser + ikSensorDiagnoser + rotor speed + azimuth + + + + + + + + + + + + + signal 1 + + + gearboxratio + + signal 2 + + + + gearboxratio + + + + + + + signal 3 + + + ok 1 + ok 2 + ok 3 + + + MUX + + + + + + + + + + map + + + + + generator speed equivalent + status + + diff --git a/doc/svg/ikSpdman_unit_block.svg b/doc/svg/ikSpdman_unit_block.svg new file mode 100644 index 0000000..865d71e --- /dev/null +++ b/doc/svg/ikSpdman_unit_block.svg @@ -0,0 +1,1622 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + generator speed + + speed sensor manager + ikSpdman + rotor speed + azimuth + generator speed equivalent + + + + + + diff --git a/src/discon/discon.c b/src/discon/discon.c deleted file mode 100644 index 3f6c221..0000000 --- a/src/discon/discon.c +++ /dev/null @@ -1,59 +0,0 @@ -/* -Copyright (C) 2017 IK4-IKERLAN - -This file is part of OpenDiscon. - -OpenDiscon 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. - -OpenDiscon 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 OpenDiscon. If not, see . -*/ - -#define NINT(a) ((a) >= 0.0 ? (int) ((a)+0.5) : ((a)-0.5)) - -#include "ikClwindconWTConfig.h" -#include "OpenDiscon_EXPORT.h" -#include - -void OpenDiscon_EXPORT DISCON(float *DATA, int FLAG, const char *INFILE, const char *OUTNAME, char *MESSAGE) { - int err; - static ikClwindconWTCon con; - double output = -12.0; - static FILE *f = NULL; - const double deratingRatio = 0.2; /* later to be got via the supercontroller interface */ - - if (NINT(DATA[0]) == 0) { - ikClwindconWTConParams param; - ikClwindconWTCon_initParams(¶m); - setParams(¶m); - ikClwindconWTCon_init(&con, ¶m); - f = fopen("log.bin", "wb"); - } -//TODO lower maximum torque according to maximum power with derating (it may be time to bring the power manager back) - con.in.deratingRatio = deratingRatio; - con.in.externalMaximumTorque = 230.0; /* kNm */ - con.in.externalMinimumTorque = 0.0; /* kNm */ - con.in.externalMaximumPitch = 90.0; /* deg */ - con.in.externalMinimumPitch = 0.0; /* deg */ - con.in.generatorSpeed = (double) DATA[19]; /* rad/s */ - con.in.maximumSpeed = 480.0/30*3.1416; /* rpm to rad/s */ - - ikClwindconWTCon_step(&con); - - DATA[46] = (float) (con.out.torqueDemand*1.0e3); /* kNm to Nm */ - DATA[41] = (float) (con.out.pitchDemandBlade1/180.0*3.1416); /* deg to rad */ - DATA[42] = (float) (con.out.pitchDemandBlade2/180.0*3.1416); /* deg to rad */ - DATA[43] = (float) (con.out.pitchDemandBlade3/180.0*3.1416); /* deg to rad */ - DATA[44] = (float) (con.out.pitchDemandBlade1/180.0*3.1416); /* deg to rad (collective pitch angle) */ - - err = ikClwindconWTCon_getOutput(&con, &output, "maximum torque"); - fwrite(&(output), 1, sizeof(output), f); -}