//--------------------------------------------------------------
//	FIR, IIR フィルタの波形表示，位相特性の比較のため
//		出力波形を各周波数成分ごとに表示する機能あり
//
//		FIR フィルタ：直接形 100次 HPF，直線位相
//		IIR フィルタ：縦続形   4次 HPF，非直線位相
//
//		作成者：三上直樹，2023/01/30 作成，ⓒ三上直樹 2023
//--------------------------------------------------------------

using System;
using System.Windows.Forms;
using System.Drawing;

using Mikami;
using Mikami.Filter;
using Mikami.WindowsApplication;

using static System.Math;

namespace FIR_IIR_HPF_比較_直線位相_波形
{
	public partial class Form1 : Form
	{
		private const int N_START_ = 1200;
		private const int N_USE_ = 150;
		private const int N_DATA_ = N_START_ + N_USE_ + 40000/100;
		private const int OVS_ = 4;					// オーバーサンプリング比

		private const float  X_MIN_ = 100.0f;		// 周波数の最小値
		private const float  X_MAX_ = 4300.0f;		// 周波数の最大値
		private const double F_MAX2_ = 800.0f;		// 基本波の周波数の最大値
		private const float  FS_ = 10000.0f;		// 標本化周波数 = 10 kHz

		// tFactor_: 周波数と TrackBar のツマミの位置の関係を表す係数
		private readonly double tFactor_ = Log10(X_MAX_/X_MIN_)/(X_MAX_ - X_MIN_);

		private readonly float dpiFactor_;			// 異なる DPI システム用スケール変換係数
		private readonly RadioGroup scaleV_;		// 縦倍率の情報を保持
		private readonly RadioGroup scaleH_;		// 横倍率の情報を保持
		private readonly RadioGroup filterType_;	// FIR, IIR の区別
		private readonly RadioGroup displayType_;	// 出力波形の表示法の区別
		private readonly WaveformDrawer drawer_;	// 波形描画用

		private float[] xn_ = new float[N_DATA_];		// 入力信号に対応する配列
		private float[] xn1_ = new float[N_DATA_];		// 入力信号基本波に対応する配列
		private float[] xn3_ = new float[N_DATA_];		// 入力信号３倍波に対応する配列
		private float[] xn5_ = new float[N_DATA_];		// 入力信号５倍波に対応する配列
		private float[] ynFIR_ = new float[N_DATA_];	// 出力信号に対応する配列
		private float[] ynFIR1_ = new float[N_DATA_];	// 出力信号基本波に対応する配列
		private float[] ynFIR3_ = new float[N_DATA_];	// 出力信号３倍波に対応する配列
		private float[] ynFIR5_ = new float[N_DATA_];	// 出力信号５倍波に対応する配列
		private float[] ynIIR_ = new float[N_DATA_];	// 出力信号に対応する配列
		private float[] ynIIR1_ = new float[N_DATA_];	// 出力信号基本波に対応する配列
		private float[] ynIIR3_ = new float[N_DATA_];	// 出力信号３倍波に対応する配列
		private float[] ynIIR5_ = new float[N_DATA_];	// 出力信号５倍波に対応する配列
		private float[] xm_, ymFIR_, ymIIR_;			// 補間した信号：合成波
		private float[] xm1_, xm3_, xm5_;				// 補間した入力信号：基本波と高調波
		private float[] ymFIR1_, ymFIR3_, ymFIR5_;		// 補間した出力信号：基本波と高調波
		private float[] ymIIR1_, ymIIR3_, ymIIR5_;		// 補間した出力信号：基本波と高調波

		public Form1()

		{
			InitializeComponent();

			// 異なる DPI のシステムに対応するスケール変換の係数を取得
			dpiFactor_ = CreateGraphics().DpiX/96.0f;

			trackBar1.Minimum = (int)X_MIN_;	// TrackBar の最小値を設定
			trackBar1.Maximum = (int)X_MAX_;	// TrackBar の最大値を設定

			scaleV_ = new RadioGroup(groupBox1);
			scaleH_ = new RadioGroup(groupBox2);
			filterType_ = new RadioGroup(groupBox3);
			displayType_ = new RadioGroup(groupBox4);

			numericUpDown1.Maximum = (decimal)X_MAX_;
			numericUpDown1.Minimum = (decimal)X_MIN_;
			numericUpDown1.Value = numericUpDown1.Minimum;
			numericUpDown1_ValueChanged(null, null);	// 念のため

			drawer_ = new WaveformDrawer(Font, pictureBox1.Height, OVS_, dpiFactor_);

			radioButton13_CheckedChanged(null, null);

			FormCloseConfirm.Set(this);			// 終了時に確認できるようにする
		}

		// 終了
		private void button1_Click(object sender, EventArgs e)
		{
			Close();
		}

		// 入力の正弦波の周波数を trackBar1 で設定し，正弦波を発生させる
		private void trackBar1_Scroll(object sender, EventArgs e)
		{
			int iFrq = (int)Round(X_MIN_*Pow(10, tFactor_*(trackBar1.Value - X_MIN_)));
			if (iFrq > F_MAX2_)
			{
				iFrq = (int)F_MAX2_;		// 基本波の周波数を F_MAX2_ 以下に制限するため
				trackBar1.Value = (int)(X_MIN_ + Round(Log10(iFrq/X_MIN_)/tFactor_));
			}
			numericUpDown1.Value = iFrq;	// 更新で numericUpDown1_ValueChanged() が呼ばれる
		}

		// 周波数を numericUpDown1 で設定する
		private void numericUpDown1_ValueChanged(object sender, EventArgs e)
		{
			double frq = (double)numericUpDown1.Value;
			if (frq > F_MAX2_) frq = F_MAX2_;	// 基本波の周波数を F_MAX2_ 以下に制限するため
			numericUpDown1.Value = (decimal)frq;
			trackBar1.Value = (int)(X_MIN_ + Round(Log10(frq/X_MIN_)/tFactor_));
			label4.Text = (frq < 1000) ? frq.ToString() + " Hz"
										: (frq/1000.0).ToString("#.000") + " kHz";

			SignalGeneration(frq);
			Filtering();
		}

		// numericUpDown1 の入力文字列を全選択の状態にする
		private void numericUpDown1_Click(object sender, EventArgs e)
		{
			numericUpDown1.Select(0, numericUpDown1.Value.ToString().Length);
		}

		// numericUpDown1 で Enter キー入力時の Beep 音を消す
		private void numericUpDown1_KeyDown(object sender, KeyEventArgs e)
		{
			if (e.KeyCode == Keys.Enter) e.SuppressKeyPress = true;
		}

		// 出力波形の倍率（振幅）の設定
		private void radioButton1_Click(object sender, EventArgs e)
		{
			pictureBox2.Invalidate();
		}

		// 時間軸の倍率の設定
		private void radioButton8_Click(object sender, EventArgs e)
		{
			pictureBox1.Invalidate();
			pictureBox2.Invalidate();
		}

		// FIR フィルタと IIR フィルタの切り替え
		private void radioButton13_CheckedChanged(object sender, EventArgs e)
		{
			toolStripStatusLabel1.Text = filterType_.Checked +
										 " フィルタを使用";
			pictureBox2.Invalidate();
		}

		// 出力波形を合成波にするか個々の周波数成分の波形にするかの切り替え
		private void radioButton15_CheckedChanged(object sender, EventArgs e)
		{
			if (radioButton15.Checked) label1.Text = "入力波形";
			else					   label1.Text = "入力信号の各周波数成分の波形";
			pictureBox1.Invalidate();
			if (radioButton15.Checked) label2.Text = "出力波形";
			else					   label2.Text = "出力信号の各周波数成分の波形";
			pictureBox2.Invalidate();
		}

		// 入力波形の描画指令
		private void pictureBox1_Paint(object sender, PaintEventArgs e)
		{
			string h = scaleH_.Checked;
			if (displayType_.Checked == "合成")
				drawer_.Draw(e.Graphics, xm_, N_START_, "1", h);
			else
			{
				drawer_.Draw(e.Graphics, xm1_, N_START_, "1", h);
				drawer_.Draw(e.Graphics, xm3_, N_START_, "1", h, Color.Magenta);
				drawer_.Draw(e.Graphics, xm5_, N_START_, "1", h, Color.Cyan);
			}
		}

		// 出力波形の描画指令
		private void pictureBox2_Paint(object sender, PaintEventArgs e)
		{
			string v = scaleV_.Checked;
			string h = scaleH_.Checked;
			if (filterType_.Checked == "FIR")
			{
				if (displayType_.Checked == "合成")
					drawer_.Draw(e.Graphics, ymFIR_, N_START_, v, h);
				else
				{
					drawer_.Draw(e.Graphics, ymFIR1_, N_START_, v, h);
					drawer_.Draw(e.Graphics, ymFIR3_, N_START_, v, h, Color.Magenta);
					drawer_.Draw(e.Graphics, ymFIR5_, N_START_, v, h, Color.Cyan);
				}
			}
			else
			{
				if (displayType_.Checked == "合成")
					drawer_.Draw(e.Graphics, ymIIR_, N_START_, v, h);
				else
				{
					drawer_.Draw(e.Graphics, ymIIR1_, N_START_, v, h);
					drawer_.Draw(e.Graphics, ymIIR3_, N_START_, v, h, Color.Magenta);
					drawer_.Draw(e.Graphics, ymIIR5_, N_START_, v, h, Color.Cyan);
				}
			}
		}

		// 入力正弦波の周波数の目盛描画
		private void pictureBox3_Paint(object sender, PaintEventArgs e)
		{
			DrawTickValue.Execute(e.Graphics, this, X_MAX_/X_MIN_);
		}

		// 矩形波の第５高調波まで含む信号の発生
		private void SignalGeneration(double frq)
		{
			double w1 = 2*PI*frq/FS_;	// 基本波の正規化角周波数
			double w3 = w1*3;			// ３倍波の正規化角周波数
			double w5 = w1*5;			// ５倍波の正規化角周波数

			double c3 = 1.0/3.0;		// ３倍波の振幅
			double c5 = 1.0/5.0;		// ５倍波の振幅

			for (int n=0; n<N_DATA_; n++)
			{
				int ns = n-N_START_;
				xn1_[n] = (float)Sin(w1*ns);
				xn3_[n] = (float)(c3*Sin(w3*ns));
				xn5_[n] = (float)(c5*Sin(w5*ns));
				xn_[n] = xn1_[n] + xn3_[n] + xn5_[n] ;
			}

			xm_ = Interpolator.Execute(xn_, N_DATA_);		// 帯域制限補間
			xm1_ = Interpolator.Execute(xn1_, N_DATA_);		// 帯域制限補間
			xm3_ = Interpolator.Execute(xn3_, N_DATA_);		// 帯域制限補間
			xm5_ = Interpolator.Execute(xn5_, N_DATA_);		// 帯域制限補間
			pictureBox1.Invalidate();
		}

		// フィルタ処理
		private void Filtering()
		{
			FIR_Direct fir = new FIR_Direct(FirCoefs.hk_);
			for (int n=0; n<N_DATA_; n++) ynFIR_[n] = fir.Execute(xn_[n]);
			for (int n=0; n<N_DATA_; n++) ynFIR1_[n] = fir.Execute(xn1_[n]);
			for (int n=0; n<N_DATA_; n++) ynFIR3_[n] = fir.Execute(xn3_[n]);
			for (int n=0; n<N_DATA_; n++) ynFIR5_[n] = fir.Execute(xn5_[n]);

			IIR_CascadeF iir = new IIR_CascadeF(IirCoefs.ck_, IirCoefs.g0_);
			for (int n=0; n<N_DATA_; n++) ynIIR_[n] = iir.Execute(xn_[n]);
			for (int n=0; n<N_DATA_; n++) ynIIR1_[n] = iir.Execute(xn1_[n]);
			for (int n=0; n<N_DATA_; n++) ynIIR3_[n] = iir.Execute(xn3_[n]);
			for (int n=0; n<N_DATA_; n++) ynIIR5_[n] = iir.Execute(xn5_[n]);

			ymFIR_ = Interpolator.Execute(ynFIR_, N_DATA_);		// 帯域制限補間
			ymFIR1_ = Interpolator.Execute(ynFIR1_, N_DATA_);	// 帯域制限補間
			ymFIR3_ = Interpolator.Execute(ynFIR3_, N_DATA_);	// 帯域制限補間
			ymFIR5_ = Interpolator.Execute(ynFIR5_, N_DATA_);	// 帯域制限補間

			ymIIR_ = Interpolator.Execute(ynIIR_, N_DATA_);		// 帯域制限補間
			ymIIR1_ = Interpolator.Execute(ynIIR1_, N_DATA_);	// 帯域制限補間
			ymIIR3_ = Interpolator.Execute(ynIIR3_, N_DATA_);	// 帯域制限補間
			ymIIR5_ = Interpolator.Execute(ynIIR5_, N_DATA_);	// 帯域制限補間
			pictureBox2.Invalidate();
		}
	}
}
