/**************************************************************************
	Description: Программа моделирует работу процессора - осуществляет
	вызов функций init28335, detcoeff, isr.
	Также моделирует различные периферийные устройства процессора
	TMS320F28335/TMS320F28379D (ADC, PWM, QEP и т.д.).

	Автор: Улитовский Д.И.
	Дата последнего обновления: 2021.11.08
**************************************************************************/


#include "simstruc.h"
#include "wrapper_inu.h"
#include "def.h"
#include "controller.h"


extern void init28335(void);
extern void detcoeff(void);
extern void isr(void);
extern void input_param(unsigned short num, unsigned short val);


void controller(SimStruct *S, const real_T *u, real_T *xD, real_T *rW, int_T *iW) {
	// ВХОДЫ (begin)
	nn = 0;
	// аналоговые величины
	udc1_ml = u[nn++];//В
	ia1_ml = u[nn++];//А
	ib1_ml = u[nn++];//А
	ic1_ml = u[nn++];//А
	wm_ml = u[nn++];//рад/с
	// управление (например, с ВУ)
	mst.faultReset = (unsigned short)u[nn++];
	mst.start = (unsigned short)u[nn++];
	mst.pzMode = (unsigned short)u[nn++];
	mst.wmZz = u[nn++];//o.e. (от N_BAZ)
	mst.pmZz = u[nn++]*(P_NOM/S_BAZ);//o.e. (от S_BAZ)
	mst.wmLim = u[nn++];//o.e. (от N_BAZ)
	mst.pmLim = u[nn++]*(P_NOM/S_BAZ);//o.e. (от S_BAZ)
	mst.pIncrMaxTy = u[nn++]*TY*DECIM_PSI_WM_PM*(P_NOM/S_BAZ);//o.e. (от S_BAZ)
	mst.pDecrMaxTy = u[nn++]*TY*DECIM_PSI_WM_PM*(P_NOM/S_BAZ);//o.e. (от S_BAZ)
	iref = u[nn++];//о.е.
	// параметры (например, с ПУ)
	paramNo = FIRST_WRITE_PAR_NUM;
	paramNew[paramNo++] = (unsigned short)u[nn++];
	paramNew[paramNo++] = (unsigned short)u[nn++];
	// ВХОДЫ (end)

	//wm_ml = wm_ml_0*wm_err;//угловая скорость с учетом погрешности


	// обрабатываем параметры S-Function каждый раз, когда они изменились
	if ( iW[0] == 1 ) {
		iW[0] = 0;
		kkk = 0;
		for ( lll = 0; lll < NPARAMS; lll++ ) {
			// определяем кол-во элементов в параметре
			dimen = mxGetNumberOfElements(ssGetSFcnParam(S,lll));
			// обрабатываем параметр в зависимости от его размера
			if ( dimen > LEN_PARAM_MATR*2 ) {
				ssSetErrorStatus(S,"В параметре-массиве слишком много элементов");
				return;
			}
			else if ( dimen > 1 ) {
				// запоминаем кол-во элементов параметра-матрицы
				paramMatrDimen = dimen;
				// запоминаем значения элементов параметра-матрицы
				for ( mmm = 0; mmm < dimen; mmm++ )
					paramMatr[mmm] = mxGetPr(ssGetSFcnParam(S,lll))[mmm];
			}
			else {
				// запоминаем значения параметров-скаляров
				paramScal[kkk++] = mxGetPr(ssGetSFcnParam(S,lll))[0];
			}
		}
		// ПАРАМЕТРЫ (begin)
		nn = 0;
		dt = paramScal[nn++];//шаг дискретизации (всегда должен передаваться в S-function последним!)
		// ПАРАМЕТРЫ (end)
	} //if ( iW[0] == 1 )




	// кое-что выполняем один раз при запуске модели
	if ( iW[1] == 1 ) {
		iW[1] = 0;

		// инициализация процессора
		init28335();

		// имитация считывания параметров из EEPROM
		// ... параметры из модели (см. блок "Parameters")
		for ( j = FIRST_WRITE_PAR_NUM; j < paramNo; j++ ) {
			param[j] = paramNew[j];
		}
		// ... параметры из файла
		param[180] = 930;//rf.PsiZ, %*10 от PSI_BAZ

		param[200] = 2048;//offset.Ia1, ед. АЦП
		param[201] = 2048;//offset.Ib1, ед. АЦП
		param[202] = 2048;//offset.Ic1, ед. АЦП
		param[203] = 2048;//offset.Udc1, ед. АЦП
		param[206] = 2048;//offset.Ia2, ед. АЦП
		param[207] = 2048;//offset.Ib2, ед. АЦП
		param[208] = 2048;//offset.Ic2, ед. АЦП
		param[209] = 2048;//offset.Udc2, ед. АЦП

		param[210] = 100;//cc.Kp, %
		param[211] = 100;//cc.Ki, %
		param[212] = 100;//cf.Kp, %
		param[213] = 100;//cf.Ki, %
		param[214] = 100;//csp.Kp, %
		param[215] = 100;//csp.Ki, %

		param[220] = 99;//protect.IacMax, % от IAC_SENS_MAX
		param[221] = 130;//protect.UdcMax, % от U_NOM
		param[222] = 110;//IzLim, % от I_BAZ (д.б. больше cf.IdLim)
		param[223] = 105;//cf.IdLim, % от I_BAZ (д.б. меньше IzLim)
		param[224] = 105;//csp.IqLim, % от I_BAZ
		param[225] = 97;//protect.UdcMin, % от U_NOM
		param[226] = 115;//protect.WmMax, % от N_NOM
		param[228] = 103;//rf.WmNomPsi, % от N_NOM
		param[229] = 97;//rf.YlimPsi, % от Y_LIM
		param[231] = 300;//protect.TudcMin, мс
		param[233] = 1000;//protect.TwmMax, мс

		param[244] = 26000;//rs.WlimIncr, мс
		param[245] = 2000;//csp.IlimIncr, мс
		param[248] = 6000;//rp.PlimIncr, мс

		param[269] = 9964;//9700;//KmeCorr, %*100

		param[285] = 10;//Kudc, мс*10
		param[286] = 700;//Kwm, мс*10
		param[288] = 250;//rs.Kwmz, мс
		param[289] = 50;//rf.Kpsiz, мс
		param[290] = 40;//Kme, мс
		param[292] = 80;//rp.Kpmz, мс

		param[303] = (unsigned short)(19200.);//sgmPar.Rs, мкОм
		param[304] = (unsigned short)(19364.);//sgmPar.Lls, мкГн*10
		param[305] = (unsigned short)(8500.);//sgmPar.Rr, мкОм
		param[306] = (unsigned short)(10212.);//sgmPar.Llr, мкГн*10
		param[307] = (unsigned short)(35810.);//sgmPar.Lm, мкГн

		// инициализация программы
		detcoeff();

		// для моделирования таймеров
		T1Pr = (double)EPwm1Regs.TBPRD;
		T2Pr = (double)EPwm2Regs.TBPRD;
		T3Pr = (double)EPwm3Regs.TBPRD;
		T4Pr = (double)EPwm4Regs.TBPRD;
		T5Pr = (double)EPwm5Regs.TBPRD;
		T6Pr = (double)EPwm6Regs.TBPRD;
		t1cntAux = (double)EPwm1Regs.TBCTR;
		t2cntAux = (double)EPwm2Regs.TBCTR;
		t3cntAux = (double)EPwm3Regs.TBCTR;
		t4cntAux = (double)EPwm4Regs.TBCTR;
		t5cntAux = (double)EPwm5Regs.TBCTR;
		t6cntAux = (double)EPwm6Regs.TBCTR;
		// ... приращение счётчиков таймеров за шаг дискретизации
		TxCntPlus = FTBCLK*dt;

		// для моделирования eQEP
		Qposmax = (double)EQep2Regs.QPOSMAX;
		qposcnt = 1.;//(double)EQep2Regs.QPOSCNT;

		// для моделирования АЦП
		// (на счёт 1e-6 см. SetupAdc(), хотя там скорее не 1.0 мкс, а 0.8 мкс)
		Tadc = (int)(1e-6/dt);
		// ... на всякий случай
		if ( Tadc < 1 )
			Tadc = 1;
		tAdc = 0;
		// ... чтобы АЦП ждал запуска
		nAdc = 10;

		// для моделирования Dead-Band Unit
		CntDt = (int)(DT/dt);
		stateDt1 = stateDt2 = stateDt3 = stateDt4 = stateDt5 = stateDt6 = 1;
		cntDt1 = cntDt2 = cntDt3 = cntDt4 = cntDt5 = cntDt6 = 0;

		// для защит
		DI_24V_SOURCE_FAULT = 0;

		// для вывода
		inuWork = 0;
		ivc.psi = 0;
		rf.psiZ = 0;
		rs.wmZ = 0;
		csp.wmLimZi = 0;
		pm = 0;
		rp.pmZ = 0;
		csp.pmLimZi = 0;
		id1 = 0;
		iq1 = 0;
		idZ = 0;
		iqZ = 0;
		cf.idP = 0;
		cf.idFF = 0;
		cf.idI = 0;
		csp.iqP = 0;
		csp.iqFF = 0;
		csp.iqI = 0;
		cc.yd1 = 0;
		cc.yq1 = 0;
		cc.y1 = 0;
	} //if ( iW[1] == 1 )




	// Моделируем Time-Base Submodule, Counter-Compare Submodule и
	// Event-Trigger Submodule
	// ePWM1 (up-down-count mode)
	// -------------------------
	t1cntAuxPrev = t1cntAux;
	t1cntAux += TxCntPlus;
	if ( t1cntAux > T1Pr ) {
		t1cntAux -= T1Pr*2.;
		// active CMPA load from shadow when TBCTR == TBPRD
		cmp1A = (double)EPwm1Regs.CMPA.half.CMPA;
		// запуск АЦП
		tAdc = Tadc;
		nAdc = 0;
	}
	if ( (t1cntAuxPrev < 0) && (t1cntAux >= 0) ) {
		// active CMPA load from shadow when TBCTR == 0
		cmp1A = (double)EPwm1Regs.CMPA.half.CMPA;
		// запуск АЦП
		tAdc = Tadc;
		nAdc = 0;
	}
	t1cnt = fabs(t1cntAux);

	// ePWM2 (up-down-count mode)
	// -------------------------
	t2cntAuxPrev = t2cntAux;
	t2cntAux += TxCntPlus;
	if ( t2cntAux > T2Pr ) {
		t2cntAux -= T2Pr*2.;
		// active CMPA load from shadow when TBCTR == TBPRD
		cmp2A = (double)EPwm2Regs.CMPA.half.CMPA;
	}
	if ( (t2cntAuxPrev < 0) && (t2cntAux >= 0) ) {
		// active CMPA load from shadow when TBCTR == 0
		cmp2A = (double)EPwm2Regs.CMPA.half.CMPA;
	}
	t2cnt = fabs(t2cntAux);

	// ePWM3 (up-down-count mode)
	// -------------------------
	t3cntAuxPrev = t3cntAux;
	t3cntAux += TxCntPlus;
	if ( t3cntAux > T3Pr ) {
		t3cntAux -= T3Pr*2.;
		// active CMPA load from shadow when TBCTR == TBPRD
		cmp3A = (double)EPwm3Regs.CMPA.half.CMPA;
	}
	if ( (t3cntAuxPrev < 0) && (t3cntAux >= 0) ) {
		// active CMPA load from shadow when TBCTR == 0
		cmp3A = (double)EPwm3Regs.CMPA.half.CMPA;
	}
	t3cnt = fabs(t3cntAux);

	// ePWM4 (up-down-count mode)
	// -------------------------
	t4cntAuxPrev = t4cntAux;
	t4cntAux += TxCntPlus;
	if ( t4cntAux > T4Pr ) {
		t4cntAux -= T4Pr*2.;
		// active CMPA load from shadow when TBCTR == TBPRD
		cmp4A = (double)EPwm4Regs.CMPA.half.CMPA;
	}
	if ( (t4cntAuxPrev < 0) && (t4cntAux >= 0) ) {
		// active CMPA load from shadow when TBCTR == 0
		cmp4A = (double)EPwm4Regs.CMPA.half.CMPA;
	}
	t4cnt = fabs(t4cntAux);

	// ePWM5 (up-down-count mode)
	// -------------------------
	t5cntAuxPrev = t5cntAux;
	t5cntAux += TxCntPlus;
	if ( t5cntAux > T5Pr ) {
		t5cntAux -= T5Pr*2.;
		// active CMPA load from shadow when TBCTR == TBPRD
		cmp5A = (double)EPwm5Regs.CMPA.half.CMPA;
	}
	if ( (t5cntAuxPrev < 0) && (t5cntAux >= 0) ) {
		// active CMPA load from shadow when TBCTR == 0
		cmp5A = (double)EPwm5Regs.CMPA.half.CMPA;
	}
	t5cnt = fabs(t5cntAux);

	// ePWM6 (up-down-count mode)
	// -------------------------
	t6cntAuxPrev = t6cntAux;
	t6cntAux += TxCntPlus;
	if ( t6cntAux > T6Pr ) {
		t6cntAux -= T6Pr*2.;
		// active CMPA load from shadow when TBCTR == TBPRD
		cmp6A = (double)EPwm6Regs.CMPA.half.CMPA;
	}
	if ( (t6cntAuxPrev < 0) && (t6cntAux >= 0) ) {
		// active CMPA load from shadow when TBCTR == 0
		cmp6A = (double)EPwm6Regs.CMPA.half.CMPA;
	}
	t6cnt = fabs(t6cntAux);


	// Моделируем работу счётчика в eQEP
	qposcnt += wm_ml/PI2*NOP*4.*dt;
	if ( qposcnt >= (Qposmax + 1.) )
		qposcnt -= (Qposmax + 1.);
	else if ( qposcnt < 0 )
		qposcnt += (Qposmax + 1.);
	EQep2Regs.QPOSCNT = (short)qposcnt;


	/* Моделируем преобразования измеряемых величин датчиками,
	операционниками и АЦП (с помощью nAdc учитываем сдвиг по времени
	между АЦП разных сигналов) */
	if ( tAdc < Tadc ) {
		tAdc++;
	}
	else {
		tAdc = 1;
		nAdc++;
		switch ( nAdc ) {
		case 5:
			// Udc1
			if ( udc1_ml > UDC_SENS_MAX )
				udc1_ml = UDC_SENS_MAX;
			else if ( udc1_ml < -UDC_SENS_MAX )
				udc1_ml = -UDC_SENS_MAX;
			AdcMirror.ADCRESULT0 =
				(unsigned short)(udc1_ml/UDC_SENS_MAX*2048. + (float)offset.Udc1);
			// Ic1
			if ( ic1_ml > IAC_SENS_MAX )
				ic1_ml = IAC_SENS_MAX;
			else if ( ic1_ml < -IAC_SENS_MAX )
				ic1_ml = -IAC_SENS_MAX;
			AdcMirror.ADCRESULT2 =
				(unsigned short)(ic1_ml/IAC_SENS_MAX*2048. + (float)offset.Ic1);
			break;
		case 6:
			// Ia1
			if ( ia1_ml > IAC_SENS_MAX )
				ia1_ml = IAC_SENS_MAX;
			else if ( ia1_ml < -IAC_SENS_MAX )
				ia1_ml = -IAC_SENS_MAX;
			AdcMirror.ADCRESULT4 =
				(unsigned short)(ia1_ml/IAC_SENS_MAX*2048. + (float)offset.Ia1);
			// Ib1
			if ( ib1_ml > IAC_SENS_MAX )
				ib1_ml = IAC_SENS_MAX;
			else if ( ib1_ml < -IAC_SENS_MAX )
				ib1_ml = -IAC_SENS_MAX;
			AdcMirror.ADCRESULT6 =
				(unsigned short)(ib1_ml/IAC_SENS_MAX*2048. + (float)offset.Ib1);
			break;
		case 7:
			// как бы с ПУ
			for ( j = FIRST_WRITE_PAR_NUM; j < paramNo; j++ ) {
				if ( paramNew[j] != param[j] ) {
					input_param((short)j, paramNew[j]);
					break;
				}
			}
			// после завершения серии АЦП вызываем isr()
			isr();
			break;
		} //switch ( nAdc )
	} //tAdc


	// Моделируем Action-Qualifier Submodule
	// ... ePWM1
	if ( cmp1A > t1cnt ) {
		ci1A = 1;
		ci1B = 0; 
	}
	else if ( cmp1A < t1cnt ) {
		ci1A = 0;
		ci1B = 1;
	}
	// ... ePWM2
	if ( cmp2A < t2cnt ) {
		ci2A = 1;
		ci2B = 0;
	}
	else if ( cmp2A > t2cnt ) {
		ci2A = 0;
		ci2B = 1;
	}
	// ... ePWM3
	if ( cmp3A > t3cnt ) {
		ci3A = 1;
		ci3B = 0;
	}
	else if ( cmp3A < t3cnt ) {
		ci3A = 0;
		ci3B = 1;
	}
	// ... ePWM4
	if ( cmp4A < t4cnt ) {
		ci4A = 1;
		ci4B = 0;
	}
	else if ( cmp4A > t4cnt ) {
		ci4A = 0;
		ci4B = 1;
	}
	// ... ePWM5
	if ( cmp5A > t5cnt ) {
		ci5A = 1;
		ci5B = 0;
	}
	else if ( cmp5A < t5cnt ) {
		ci5A = 0;
		ci5B = 1;
	}
	// ... ePWM6
	if ( cmp6A < t6cnt ) {
		ci6A = 1;
		ci6B = 0;
	}
	else if ( cmp6A > t6cnt ) {
		ci6A = 0;
		ci6B = 1;
	}


	// Моделируем Dead-Band Submodule
	// ... ePWM1
	if ( stateDt1 == 1 ) {
		ci1A_DT = ci1A;
		ci1B_DT = 0;
		if ( ci1A == 1 )
			cntDt1 = CntDt;
		if ( cntDt1 > 0 )
			cntDt1--;
		else
			stateDt1 = 2;
	}
	else if ( stateDt1 == 2 ) {
		ci1A_DT = 0;
		ci1B_DT = ci1B;
		if ( ci1B == 1 )
			cntDt1 = CntDt;
		if ( cntDt1 > 0 )
			cntDt1--;
		else
			stateDt1 = 1;
	}
	// ... ePWM2
	if ( stateDt2 == 1 ) {
		ci2A_DT = ci2A;
		ci2B_DT = 0;
		if ( ci2A == 1 )
			cntDt2 = CntDt;
		if ( cntDt2 > 0 )
			cntDt2--;
		else
			stateDt2 = 2;
	}
	else if ( stateDt2 == 2 ) {
		ci2A_DT = 0;
		ci2B_DT = ci2B;
		if ( ci2B == 1 )
			cntDt2 = CntDt;
		if ( cntDt2 > 0 )
			cntDt2--;
		else
			stateDt2 = 1;
	}
	// ... ePWM3
	if ( stateDt3 == 1 ) {
		ci3A_DT = ci3A;
		ci3B_DT = 0;
		if ( ci3A == 1 )
			cntDt3 = CntDt;
		if ( cntDt3 > 0 )
			cntDt3--;
		else
			stateDt3 = 2;
	}
	else if ( stateDt3 == 2 ) {
		ci3A_DT = 0;
		ci3B_DT = ci3B;
		if ( ci3B == 1 )
			cntDt3 = CntDt;
		if ( cntDt3 > 0 )
			cntDt3--;
		else
			stateDt3 = 1;
	}
	// ... ePWM4
	if ( stateDt4 == 1 ) {
		ci4A_DT = ci4A;
		ci4B_DT = 0;
		if ( ci4A == 1 )
			cntDt4 = CntDt;
		if ( cntDt4 > 0 )
			cntDt4--;
		else
			stateDt4 = 2;
	}
	else if ( stateDt4 == 2 ) {
		ci4A_DT = 0;
		ci4B_DT = ci4B;
		if ( ci4B == 1 )
			cntDt4 = CntDt;
		if ( cntDt4 > 0 )
			cntDt4--;
		else
			stateDt4 = 1;
	}
	// ... ePWM5
	if ( stateDt5 == 1 ) {
		ci5A_DT = ci5A;
		ci5B_DT = 0;
		if ( ci5A == 1 )
			cntDt5 = CntDt;
		if ( cntDt5 > 0 )
			cntDt5--;
		else
			stateDt5 = 2;
	}
	else if ( stateDt5 == 2 ) {
		ci5A_DT = 0;
		ci5B_DT = ci5B;
		if ( ci5B == 1 )
			cntDt5 = CntDt;
		if ( cntDt5 > 0 )
			cntDt5--;
		else
			stateDt5 = 1;
	}
	// ... ePWM6
	if ( stateDt6 == 1 ) {
		ci6A_DT = ci6A;
		ci6B_DT = 0;
		if ( ci6A == 1 )
			cntDt6 = CntDt;
		if ( cntDt6 > 0 )
			cntDt6--;
		else
			stateDt6 = 2;
	}
	else if ( stateDt6 == 2 ) {
		ci6A_DT = 0;
		ci6B_DT = ci6B;
		if ( ci6B == 1 )
			cntDt6 = CntDt;
		if ( cntDt6 > 0 )
			cntDt6--;
		else
			stateDt6 = 1;
	}


	// Моделируем Trip-Zone Submodule
	// ... clear flag for one-shot trip latch
	if ( EPwm1Regs.TZCLR.all == 0x0004 ) {
		EPwm1Regs.TZCLR.all = 0x0000;
		EPwm1Regs.TZFRC.all = 0x0000;
	}
	if ( EPwm2Regs.TZCLR.all == 0x0004 ) {
		EPwm2Regs.TZCLR.all = 0x0000;
		EPwm2Regs.TZFRC.all = 0x0000;
	}
	if ( EPwm3Regs.TZCLR.all == 0x0004 ) {
		EPwm3Regs.TZCLR.all = 0x0000;
		EPwm3Regs.TZFRC.all = 0x0000;
	}
	if ( EPwm4Regs.TZCLR.all == 0x0004 ) {
		EPwm4Regs.TZCLR.all = 0x0000;
		EPwm4Regs.TZFRC.all = 0x0000;
	}
	if ( EPwm5Regs.TZCLR.all == 0x0004 ) {
		EPwm5Regs.TZCLR.all = 0x0000;
		EPwm5Regs.TZFRC.all = 0x0000;
	}
	if ( EPwm6Regs.TZCLR.all == 0x0004 ) {
		EPwm6Regs.TZCLR.all = 0x0000;
		EPwm6Regs.TZFRC.all = 0x0000;
	}

	// ... forces a one-shot trip event
	if ( EPwm1Regs.TZFRC.all == 0x0004 )
		ci1A_DT = ci1B_DT = 0;
	if ( EPwm2Regs.TZFRC.all == 0x0004 )
		ci2A_DT = ci2B_DT = 0;
	if ( EPwm3Regs.TZFRC.all == 0x0004 )
		ci3A_DT = ci3B_DT = 0;
	if ( EPwm4Regs.TZFRC.all == 0x0004 )
		ci4A_DT = ci4B_DT = 0;
	if ( EPwm5Regs.TZFRC.all == 0x0004 )
		ci5A_DT = ci5B_DT = 0;
	if ( EPwm6Regs.TZFRC.all == 0x0004 )
		ci6A_DT = ci6B_DT = 0;




	// ВЫХОДЫ (begin)
	nn = 0;
	// Управление
	// ... INU1
	xD[nn++] = ci1A_DT;
	xD[nn++] = ci2A_DT;
	xD[nn++] = ci1B_DT;
	xD[nn++] = ci2B_DT;

	xD[nn++] = ci3A_DT;
	xD[nn++] = ci4A_DT;
	xD[nn++] = ci3B_DT;
	xD[nn++] = ci4B_DT;

	xD[nn++] = ci5A_DT;
	xD[nn++] = ci6A_DT;
	xD[nn++] = ci5B_DT;
	xD[nn++] = ci6B_DT;

	// Только для просмотра
	xD[nn++] = state;
	xD[nn++] = faultNo;

	xD[nn++] = mst.start;
	xD[nn++] = inuWork;
	xD[nn++] = mst.pzMode;

	xD[nn++] = psi;
	xD[nn++] = rf.psiZ;

	xD[nn++] = wm;
	xD[nn++] = rs.wmZ;
	xD[nn++] = csp.wmLimZi;

	xD[nn++] = pm*S_BAZ/P_NOM;
	xD[nn++] = rp.pmZ*S_BAZ/P_NOM;
	xD[nn++] = csp.pmLimZi*S_BAZ/P_NOM;

	xD[nn++] = id1;
	xD[nn++] = iq1;
	xD[nn++] = idZ;
	xD[nn++] = iqZ;

	xD[nn++] = me*M_BAZ/M_NOM;

	xD[nn++] = sqrt(idZ*idZ + iqZ*iqZ);
	xD[nn++] = IzLim;

	xD[nn++] = sqrt(cc.yd1*cc.yd1 + cc.yq1*cc.yq1);
	xD[nn++] = Y_LIM;
	
	xD[nn++] = theta_out;
	// ВЫХОДЫ (end)

} //void controller(SimStruct ...
