@Test
  public final void testCone() {
    try {
      PhysicalParameters parameters = new PhysicalParameters(25., TemperatureType.C);
      double z0 = parameters.calcZ0(BaseRadius);
      double waveNumber = parameters.calcWaveNumber(BaseFrequency);

      TerminationCalculator term = new IdealOpenEndCalculator();
      StateVector sv = term.calcStateVector(null, waveNumber, parameters);

      TransferMatrix tm =
          Tube.calcConeMatrix(waveNumber, BaseLength, BaseRadius, 0.75 * BaseRadius, parameters);
      assertEquals("Determinant incorrect", 1.0, tm.determinant().getReal(), 0.0001);
      assertEquals("Determinant.imag incorrect", 0.0, tm.determinant().getImaginary(), 0.0001);
      Complex zLoad = tm.multiply(sv).Impedance().divide(z0);

      assertEquals("Re(Z) incorrect", 0.03871, zLoad.getReal(), 0.00001);
      assertEquals("Im(Z) incorrect", -0.46038, zLoad.getImaginary(), 0.00001);

      BoreSectionCalculator boreCalc = new DefaultBoreSectionCalculator();
      BoreSection bore = new BoreSection(BaseLength, BaseRadius, 0.75 * BaseRadius);
      TransferMatrix tm2 = boreCalc.calcTransferMatrix(bore, waveNumber, parameters);
      assertEquals("Determinant 2 incorrect", 1.0, tm2.determinant().getReal(), 0.0001);
      assertEquals("Determinant.imag 2 incorrect", 0.0, tm2.determinant().getImaginary(), 0.0001);
      Complex zLoad2 = tm2.multiply(sv).Impedance().divide(z0);

      assertEquals("Re(Z2) incorrect", 0.03871, zLoad2.getReal(), 0.00001);
      assertEquals("Im(Z2) incorrect", -0.46038, zLoad2.getImaginary(), 0.00001);
    } catch (Exception e) {
      fail(e.getMessage());
    }
  }
  /*
   * (non-Javadoc)
   *
   * @see com.wwidesigner.geometry.HoleCalculator#calcTransferMatrix(double,
   * com.wwidesigner.util.PhysicalParameters)
   */
  @Override
  public TransferMatrix calcTransferMatrix(
      Hole hole, double waveNumber, PhysicalParameters parameters) {
    mParams = parameters;
    updateGeometry(hole);
    TransferMatrix matrix = new TransferMatrix();
    double omega = waveNumber * mParams.getSpeedOfSound();
    double freq = omega / (2. * Math.PI);
    double z0 = mParams.getRho() * mParams.getSpeedOfSound() / (Math.PI * mRB * mRB); // Wave
    // impedance of
    // the main
    // bore.
    double rb_on_rh = mRB / mRH;
    double rb_on_rh_2 = rb_on_rh * rb_on_rh;

    matrix.setPP(Complex.ONE);
    matrix.setUU(Complex.ONE);
    if (hole.isOpenHole()) {
      // Sign as per Gordon's implementation
      matrix.setPU(Complex.I.multiply(z0 * rb_on_rh_2 * waveNumber * mOHLB));
      matrix.setUP(
          new Complex(0., -1.)
              .multiply(waveNumber * calcHLE(freq))
              .add(calcXi(freq))
              .multiply(z0 * rb_on_rh_2)
              .reciprocal());

      // Change sign to match Antoine's configuration
      // matrix.setPU(new Complex(0., -1.).multiply(z0 * rb_on_rh_2 *
      // waveNumber
      // * mOHLB));
      // matrix.setUP(new Complex(0., 1.)
      // .multiply(waveNumber * calcHLE(freq)).add(calcXi(freq))
      // .multiply(z0 * rb_on_rh_2).reciprocal());
    } else {
      // Sign as per Gordon's implementation
      matrix.setPU(Complex.I.multiply(z0 * rb_on_rh_2 * waveNumber * mCHLB));
      matrix.setUP(new Complex(0., -1.).multiply(Math.tan(waveNumber * mLH) / (z0 * rb_on_rh_2)));

      // Change sign to match Antoine's configuration
      // matrix.setPU(new Complex(0., -1.).multiply(z0 * rb_on_rh_2
      // * waveNumber * mCHLB));
      // matrix.setUP(new Complex(0., 1.).multiply(Math.tan(waveNumber
      // * mLH)
      // / (z0 * rb_on_rh_2)));
    }

    return matrix;
  }
  @Test
  public final void testImpedance() {
    try {
      PhysicalParameters parameters = new PhysicalParameters(25., TemperatureType.C);

      double z0 = parameters.calcZ0(BaseRadius);

      Complex zLoad = Tube.calcZload(BaseFrequency, BaseRadius, parameters).divide(z0);
      assertEquals("Re(Z) incorrect", 0.00101768, zLoad.getReal(), 1.0e-6);
      assertEquals("Im(Z) incorrect", 0.039, zLoad.getImaginary(), 0.0001);

    } catch (Exception e) {
      fail(e.getMessage());
    }
  }
  // Specific resistance along the bore, when the hole is open.
  protected double calcXi(double freq) {
    double omega = 2.0 * Math.PI * freq;
    double k = omega / mParams.getSpeedOfSound(); // Wavenumber.

    double d_v = Math.sqrt(2.0 * mParams.getEta() / (mParams.getRho() * omega));
    // Viscous boundary layer thickness.

    double alpha =
        (Math.sqrt(2 * mParams.getEta() * omega / mParams.getRho())
                + (mParams.getGamma() - 1)
                    * Math.sqrt(
                        2. * mParams.getKappa() * omega / (mParams.getRho() * mParams.getC_p())))
            / (2. * mRH * mParams.getSpeedOfSound());

    double result =
        0.25 * (k * mRH) * (k * mRH) + alpha * mLH + 0.25 * k * d_v * Math.log(2. * mRH / mRC);

    return result;
  }
  // Effective acoustic length of the hole when it is open.
  protected double calcHLE(double freq) {
    // See Keefe 1990

    double k = 2.0 * Math.PI * freq / mParams.getSpeedOfSound(); // Wavenumber.
    double k_inv = 1.0 / k;

    double tan_k_l = Math.tan(k * mLH);

    double rh_on_rb = mRH / mRB;

    double result =
        (k_inv * tan_k_l + mRH * (1.40 - 0.58 * rh_on_rb * rh_on_rb))
            / (1.0 - 0.61 * k * mRH * tan_k_l);
    // From eq. (5) in Keefe (1990):

    return result;
  }