TransportDelay.java

/*
 * Created on 2007/04/16
 * Copyright (C) 2007 Koga Laboratory. All rights reserved.
 *
 */
package org.mklab.tool.control.system.continuous;

import java.util.ArrayDeque;
import java.util.Deque;

import org.mklab.nfc.matrix.ComplexNumericalMatrix;
import org.mklab.nfc.matrix.RealNumericalMatrix;
import org.mklab.nfc.ode.EquationSolver;
import org.mklab.nfc.scalar.ComplexNumericalScalar;
import org.mklab.nfc.scalar.RealNumericalScalar;
import org.mklab.tool.control.system.parameter.Parameter;
import org.mklab.tool.control.system.parameter.ParameterUpdator;
import org.mklab.tool.control.system.parameter.SIunit;
import org.mklab.tool.control.system.parameter.StringExternalizable;


/**
 * 入力を遅延させるシステム(無駄時間システム)を表わすクラスです。
 * 
 * @author koga
 * @version $Revision: 1.18 $, 2007/04/16
 * @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 TransportDelay<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>> extends BaseContinuousStaticSystem<RS,RM,CS,CM> implements ParameterUpdator, StringExternalizable {

  /** 無駄時間 */
  @Parameter(name = "timeDelay", unit = SIunit.s, description = "TransportDelay.1", internationalization = true)
  private RS timeDelay =this.sunit.create(1);

  /** 初期出力 */
  @Parameter(name = "initialOutput", description = "TransportDelay.3", update = true, internationalization = true)
  private RM initialOutput = this.sunit.createZeroGrid(1, 1);

  /** 初期バッファサイズ */
  @Parameter(name = "initialBufferSize", description = "TransportDelay.5", internationalization = true)
  private int initialBufferSize = 1024;

  /** 時刻のキュー */
  private Deque<RS> timeQue;

  /** 入力のキュー */
  private Deque<RM> inputQue;

  /**
   * 新しく生成された<code>TransportDelay</code>オブジェクトを初期化します。
   * @param sunit unit of scalar
   */
  public TransportDelay(RS sunit) {
    this(sunit.create(1), sunit);
  }

  /**
   * 新しく生成された<code>TransportDelay</code>オブジェクトを初期化します。
   * 
   * @param timeDelay 無駄時間
   * @param sunit unit of scalar
   */
  public TransportDelay(final RS timeDelay, RS sunit) {
    super(-1, -1, sunit);
    setAutoSize(true);
    setHasDirectFeedthrough(true);
    this.timeDelay = timeDelay;
    initialize();
  }

  /**
   * 新しく生成された<code>TransportDelay</code>オブジェクトを初期化します。
   * 
   * @param timeDelay 無駄時間
   * @param initialOutput 初期出力
   * @param sunit unit of scalar
   */
  public TransportDelay(final RS timeDelay, final RM initialOutput, RS sunit) {
    super(initialOutput.getRowSize(), initialOutput.getRowSize(), sunit);
    setHasDirectFeedthrough(true);
    this.timeDelay = timeDelay;
    this.initialOutput = initialOutput.createClone();
    initialize();
  }

  /**
   * @see org.mklab.tool.control.system.continuous.BaseContinuousStaticSystem#initialize()
   */
  @Override
  public void initialize() {
    this.timeQue = new ArrayDeque<>(this.initialBufferSize);
    this.inputQue = new ArrayDeque<>(this.initialBufferSize);
  }

  /**
   * {@inheritDoc}
   */
  @Override
  public RM outputEquation(final RS t, final RM u) {
    final int dataSize = this.timeQue.size();

    if (EquationSolver.isTrial() == false) {
      if (dataSize == 0 || this.timeQue.getLast() != t) {
        this.timeQue.add(t);
        this.inputQue.add(u);
      }
    }

    if (t.isLessThan(this.timeDelay) || dataSize == 0) {
      return this.initialOutput.createClone();
    }

    final RS delayPoint = t.subtract(this.timeDelay);

    if (dataSize == 1) {
      final RS t2 = this.timeQue.getLast();
      final RS t1 = this.sunit.create(0);
      final RM u2 = this.inputQue.getLast();
      final RM u1 = this.initialOutput;

      return u2.subtract(u2.subtract(u1).multiply((delayPoint.subtract(t1)).divide((t2.subtract(t1)))));
    }

    if (this.timeQue.getLast().isLessThan(delayPoint)) {
      final RS t2 = this.timeQue.pollLast();
      final RS t1 = this.timeQue.pollLast();

      final RM u2 = this.inputQue.pollLast();
      final RM u1 = this.inputQue.pollLast();

      this.timeQue.add(t1);
      this.timeQue.add(t2);

      this.inputQue.add(u1);
      this.inputQue.add(u2);

      return u1.add(u2.subtract(u1).multiply((delayPoint.subtract(t1)).divide((t2.subtract(t1)))));
    }

    RS t1 = this.timeQue.pollFirst();
    RM u1 = this.inputQue.pollFirst();

    for (int i = 0; i < dataSize; i++) {
      RS t2 = this.timeQue.pollFirst();
      final RM u2 = this.inputQue.pollFirst();

      if (t2.isLessThan(delayPoint)) {
        t1 = t2;
        u1 = u2;
        continue;
      }

      this.timeQue.addFirst(t2);
      this.timeQue.addFirst(t1);
      this.inputQue.addFirst(u2);
      this.inputQue.addFirst(u1);

      return u1.add(u2.subtract(u1).multiply((delayPoint.subtract(t1)).divide((t2.subtract(t1)))));
    }

    assert false : "never reached"; //$NON-NLS-1$

    return this.initialOutput;
  }

  /**
   * 無駄時間を設定します。
   * 
   * @param timeDelay 無駄時間
   */
  public void setTimeDelay(final RS timeDelay) {
    this.timeDelay = timeDelay;
  }

  /**
   * 無駄時間を返します。
   * 
   * @return 無駄時間
   */
  public RS getTimeDelay() {
    return this.timeDelay;
  }

  /**
   * 初期出力を設定します。
   * 
   * @param initialOutput 初期出力
   */
  public void setInitialOutput(final RM initialOutput) {
    this.initialOutput = initialOutput.createClone();
    final int size = initialOutput.getRowSize();
    if (getInputSize() != size) {
      super.setInputSize(size);
    }
    if (getOutputSize() != size) {
      super.setOutputSize(size);
    }
  }

  /**
   * @see org.mklab.tool.control.system.SystemOperator#setInputSize(int)
   */
  @Override
  public void setInputSize(final int inputSize) {
    if (inputSize != -1) {
      super.setOutputSize(inputSize);
      if (this.initialOutput == null || this.initialOutput.length() != inputSize) {
        setInitialOutput(this.sunit.createZeroGrid(inputSize, 1));
      }
    }
    super.setInputSize(inputSize);
  }

  /**
   * @see org.mklab.tool.control.system.SystemOperator#setOutputSize(int)
   */
  @Override
  public void setOutputSize(final int outputSize) {
    if (outputSize != -1) {
      super.setInputSize(outputSize);
      if (this.initialOutput == null || this.initialOutput.length() != outputSize) {
        setInitialOutput(this.sunit.createZeroGrid(outputSize, 1));
      }
    }
    super.setOutputSize(outputSize);
  }

  /**
   * 初期出力を返します。
   * 
   * @return 初期出力
   */
  public RM getInitialOutput() {
    return this.initialOutput.createClone();
  }

  /**
   * 初期バッファサイズを設定します。
   * 
   * @param initialBufferSize 初期バッファサイズ
   */
  public void setInitialBufferSize(final int initialBufferSize) {
    this.initialBufferSize = initialBufferSize;
  }

  /**
   * 初期バッファサイズを返します。
   * 
   * @return 初期バッファサイズ
   */
  public int getInitialBufferSize() {
    return this.initialBufferSize;
  }

  /**
   * @see org.mklab.tool.control.system.parameter.ParameterUpdator#updateWith(java.lang.String)
   */
  public boolean updateWith(String parameter) {
    if (parameter.equals("initialOutput")) { //$NON-NLS-1$
      final int size = this.initialOutput.getRowSize();
      setInputSize(size);
      setOutputSize(size);
      return true;
    }
    return false;
  }

  /**
   * @see org.mklab.tool.control.system.parameter.StringExternalizable#getString(java.lang.String)
   */
  public String getString(String key) {
    return Messages.getString(key);
  }

  /**
   * @see org.mklab.tool.control.system.SystemOperator#equals(java.lang.Object)
   */
  @Override
  public boolean equals(Object o) {
    if (this == o) {
      return true;
    }
    if (!super.equals(o)) {
      return false;
    }
    if (o == null) {
      return false;
    }
    if (o.getClass() != getClass()) {
      return false;
    }

    final TransportDelay<RS,RM,CS,CM> castedObj = (TransportDelay<RS,RM,CS,CM>)o;
    return ((this.timeDelay == castedObj.timeDelay) && (this.initialOutput == null ? castedObj.initialOutput == null : this.initialOutput.equals(castedObj.initialOutput))
        && (this.initialBufferSize == castedObj.initialBufferSize) && (this.timeQue == null ? castedObj.timeQue == null : this.timeQue.equals(castedObj.timeQue)) && (this.inputQue == null
          ? castedObj.inputQue == null : this.inputQue.equals(castedObj.inputQue)));
  }

  /**
   * @see org.mklab.tool.control.system.SystemOperator#hashCode()
   */
  @Override
  public int hashCode() {
    int hashCode = super.hashCode();
    hashCode = 31 * hashCode + (this.timeDelay.hashCode() ^ (this.timeDelay.hashCode() >>> 32));
    hashCode = 31 * hashCode + (this.initialOutput == null ? 0 : this.initialOutput.hashCode());
    hashCode = 31 * hashCode + this.initialBufferSize;
    hashCode = 31 * hashCode + (this.timeQue == null ? 0 : this.timeQue.hashCode());
    hashCode = 31 * hashCode + (this.inputQue == null ? 0 : this.inputQue.hashCode());
    return hashCode;
  }
}