Bode.java
/*
* $Id: Bode.java,v 1.85 2008/04/10 03:34:27 koga Exp $
*
* Copyright (C) 2004 Koga Laboratory. All rights reserved.
*/
package org.mklab.tool.control;
import java.util.ArrayList;
import java.util.List;
import org.mklab.nfc.matrix.ComplexNumericalMatrix;
import org.mklab.nfc.matrix.RealNumericalMatrix;
import org.mklab.nfc.scalar.ComplexNumericalScalar;
import org.mklab.nfc.scalar.RealNumericalScalar;
import org.mklab.tool.matrix.Unwrap;
/**
* ボード線図を描画するためのデータ(ゲイン、位相)を求めるクラスです。
*
* <p>Bode response
*
* @author koga
* @version $Revision: 1.85 $
* @param <RS> type of real scalar
* @param <RM> type of real matrix
* @param <CS> type of complex scalar
* @param <CM> type of complex matrix
*/
public class Bode<RS extends RealNumericalScalar<RS,RM,CS,CM>, RM extends RealNumericalMatrix<RS,RM,CS,CM>, CS extends ComplexNumericalScalar<RS,RM,CS,CM>, CM extends ComplexNumericalMatrix<RS,RM,CS,CM>> {
/** システム */
private LinearSystem<RS,RM,CS,CM> system;
/** サンプリング周期 */
private RS samplingInterval;
/**
* 新しく生成された<code>Bode</code>オブジェクトを初期化します。
*
* @param system 線形システム
*/
public Bode(final LinearSystem<RS,RM,CS,CM> system) {
this.system = (LinearSystem<RS,RM,CS,CM>)system.clone();
this.system.setTimeDomainType(TimeDomainType.CONTINUOUS);
}
/**
* 新しく生成された<code>Bode</code>オブジェクトを初期化します。
*
* @param system 線形システム
* @param samplingInterval サンプリング周期
*/
public Bode(final LinearSystem<RS,RM,CS,CM> system, final RS samplingInterval) {
this.system = (LinearSystem<RS,RM,CS,CM>)system.clone();
this.samplingInterval = samplingInterval;
this.system.setTimeDomainType(TimeDomainType.DISCRETE);
}
/**
* システムの周波数応答(大きさ(絶対値),偏角(度),評価周波数)を返します。
*
* @param angularFrequencies 評価する角周波数(radian/s)
* @return 周波数応答(大きさ(絶対値),偏角(度),評価周波数)
*/
public List<List<RM>> getGainAndPhase(final RM angularFrequencies) {
final List<List<RM>> magnitudesPhases = new ArrayList<>();
if (this.system.isContinuous()) {
for (int inputNumber = 1; inputNumber <= this.system.getInputSize(); inputNumber++) {
final List<RM> magnitudePhase = getGainAndPhaseForContinuous(angularFrequencies, inputNumber);
magnitudesPhases.add(magnitudePhase);
}
} else {
for (int inputNumber = 1; inputNumber <= this.system.getInputSize(); inputNumber++) {
final List<RM> magnitudePhase = getGainAndPhaseForDiscrete(angularFrequencies, inputNumber);
magnitudesPhases.add(magnitudePhase);
}
}
return magnitudesPhases;
}
/**
* システムの周波数応答(大きさ(絶対値)、偏角(度)、評価周波数)を返します。
*
* @param angularFrequencies 評価する角周波数(radian/s)
* @param inputNumber 入力番号
* @return 周波数応答(大きさ(絶対値),偏角(度),評価周波数)
*/
private List<RM> getGainAndPhaseForContinuous(final RM angularFrequencies, final int inputNumber) {
final RS unit = angularFrequencies.getElement(1, 1);
final CS j = unit.toComplex().create(unit.createZero(), unit.createUnit());
final CM jw = angularFrequencies.toComplex().multiply(j);
final CM gjw = new FrequencyResponse<>(this.system).getResponse(jw, inputNumber);
final RM magnitude = gjw.absElementWise().getRealPart();
final RM phase = gjw.logElementWise().getImaginaryPart();
final RS unwarpTolerance = unit.createPI().multiply(170).divide(180);
final RM unwrapedPhase = Unwrap.unwrapRowWise(phase, unwarpTolerance);
final RS phaseDeviationTolerance = unit.createUnit().divide(10000);
final RM compensatedPhase = compensateForIntegratorAndDerivative(magnitude, unwrapedPhase, angularFrequencies, phaseDeviationTolerance);
final RS pi = unit.createPI();
final RM phaseOfDegree = compensatedPhase.multiply(pi.inverse().multiply(180));
final List<RM> gp = new ArrayList<>();
gp.add(magnitude);
gp.add(phaseOfDegree);
gp.add(angularFrequencies);
return gp;
}
/**
* 積分器と微分器のための補正をした位相を返します。
*
* @param magnitudes 大きさ
* @param phases 位相
* @param angularFrequencies 評価する角周波数
* @param phaseDeviationTolerance 許容誤差
* @return 補正後の位相
*/
private RM compensateForIntegratorAndDerivative(final RM magnitudes, final RM phases, final RM angularFrequencies,
final RS phaseDeviationTolerance) {
final RM modifiedPhases = phases.createClone();
final int size = angularFrequencies.length();
final RS pi = magnitudes.getElement(1, 1).createPI();
for (int outputNumber = 1; outputNumber <= magnitudes.getRowSize(); outputNumber++) {
final RM phase = phases.getRowVector(outputNumber);
if (phase.max().subtract(phase.min()).isGreaterThan(phaseDeviationTolerance)) {
continue;
}
final RM gain = magnitudes.getRowVector(outputNumber).log10ElementWise().multiply(20);
final RM gainDeviation = gain.getSubVector(2, size).subtract(gain.getSubVector(1, size - 1));
final RM wLog10 = angularFrequencies.log10ElementWise();
final RM wLog10Deviation = wLog10.getSubVector(2, size).subtract(wLog10.getSubVector(1, size - 1));
final RM rates = gainDeviation.divideElementWise(wLog10Deviation);
final RS degree = rates.mean().divide(20).round();
modifiedPhases.setRowVector(outputNumber, magnitudes.createOnes(1, size).multiply(degree.multiply(pi.divide(2))));
}
return modifiedPhases;
}
// /**
// * ゲイン線図と位相線図を描画します。
// *
// * @param gnuplot Gnuplot
// * @param angularFrequencies 評価する角周波数(radian/s)
// */
// public void plot(final Gnuplot gnuplot, final RM angularFrequencies) {
// new BodePlotter(gnuplot).plot(getGainAndPhase(angularFrequencies), angularFrequencies);
// }
// /**
// * ゲイン線図と位相線図を描画します。
// *
// * @param gnuplot Gnuplot
// */
// public void plot(final Gnuplot gnuplot) {
// final RM angularFrequencies;
// final RS sunit = this.system.getA().getElement(1,1).createUnit();
// if (this.system.isContinuous()) {
// angularFrequencies = LogarithmicallySpacedVector.create(sunit.create(-2), sunit.create(2));
// } else {
// final RS period = this.samplingInterval.inverse().multiply(2).multiply(this.samplingInterval.createPI());
// angularFrequencies = LogarithmicallySpacedVector.create(sunit.create(-1), period.log10());
// }
// plot(gnuplot, angularFrequencies);
// }
/**
* システムの<code>inputNumber</code>番目の入力からの周波数応答(絶対値,位相[度],評価周波数)を返します。
*
* @param angularFrequencies 伝達関数を評価する角周波数 [rad/sec]
* @param inputNumber 入力番号
* @return {magnitude, phase}
*
* <p>周波数応答(絶対値,位相[度],評価周波数)
*
* <p>(magnitude(absolute value), phase(in degrees))
*/
private List<RM> getGainAndPhaseForDiscrete(RM angularFrequencies, int inputNumber) {
final RS unit = angularFrequencies.getElement(1, 1);
final CS j = unit.toComplex().create(unit.createZero(), unit.createUnit());
final CM exp_jwT = angularFrequencies.toComplex().multiply(j.multiply(this.samplingInterval)).expElementWise();
final CM gjw = new FrequencyResponse<>(this.system).getResponse(exp_jwT, inputNumber);
final RM magnitude = gjw.absElementWise().getRealPart();
final RM phase = gjw.logElementWise().getImaginaryPart();
final RM unwrapedPhase = Unwrap.unwrapRowWise(phase);
final RS pi = unit.createPI();
final RM phaseOfDegree = unwrapedPhase.multiply(pi.inverse().multiply(180));
final List<RM>gp = new ArrayList<>();
gp.add(magnitude);
gp.add(phaseOfDegree);
gp.add(angularFrequencies);
return gp;
}
}