//------------------------------------------------------------------------------
//	FIR フィルタと IIR フィルタのデモ
//		LPF および HPF
//
//		作成者：三上直樹，2013/12/24 作成，©三上直樹 2013
//------------------------------------------------------------------------------

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;

using System.Drawing.Drawing2D;

namespace ディジタルフィルタ_アプリ
{
	public partial class Form1 : Form
	{
		private const double FS_ = 48000;					// 標本化周波数：48 kHz
		private const double TS_ = 1/FS_;					// 標本化間隔
		private const float  TW_ = 0.005f;					// 表示する時間幅：5 ms

		private const float PP_S_ = 100e+3f;				// 1 秒 に対応するピクセル数
		private const float H0_ = 45.0f;					// 縦軸の単位長さ
		private const float DX_ = (float)(PP_S_*TS_);		// 標本化間隔に相当するピクセル数

		private const int N_DISP_ = (int)(TW_/TS_) + 1;		// 表示するデータ数
		private const int N_START_ = 2000;					// 表示を開始する点
		private float[] xn_ = new float[N_DISP_+N_START_];	// 入力信号
		private float[] yn_ = new float[N_DISP_+N_START_];	// 出力信号
		private PointF[] xnPt_ = new PointF[N_DISP_];		// 入力信号表示用
		private PointF[] ynPt_ = new PointF[N_DISP_];		// 出力信号表示用

		private float f0_;

		public Form1()
		{
			InitializeComponent();

			label1.Text = (trackBar1.Value/100.0).ToString("0.00") + " kHz";
			f0_ = trackBar1.Value*10;

			domainUpDown1.SelectedIndex = 1;
		}

		private void Form1_Paint(object sender, PaintEventArgs e)
		{
			Graphics gr = e.Graphics;
			Pen myPen = new Pen(Color.Navy, 2);

			gr.ResetTransform();

			// 入力波形の表示
			gr.TranslateTransform(50, 90);

			DrawAxis(gr);
			for (int n=0; n<N_DISP_+N_START_; n++) xn_[n] = InputSignal(f0_, n);
			
			for (int n=0; n<N_DISP_; n++)
				xnPt_[n] = new PointF(n*DX_, -H0_*xn_[n+N_START_]);

			gr.SmoothingMode = SmoothingMode.AntiAlias;
			gr.DrawCurve(myPen, xnPt_);

			// 出力波形の表示
			gr.TranslateTransform(0, 180);

			gr.SmoothingMode = SmoothingMode.AntiAlias;
			float mpy = float.Parse((string)(domainUpDown1.SelectedItem));

			DrawAxis(gr, mpy);
			gr.SetClip(new RectangleF(0, -H0_*1.5f-1, PP_S_*TW_+1, H0_*3+1));

			if (radioButton1.Checked) Fir();
			else					  Iir();

			for (int n=0; n<N_DISP_; n++)
				ynPt_[n] = new PointF(n*DX_, -H0_*mpy*yn_[n+N_START_]);
			
			gr.SmoothingMode = SmoothingMode.AntiAlias;
			gr.DrawCurve(myPen, ynPt_);
		}

		// 座標軸等の描画
		private void DrawAxis(Graphics gr, float den = 1)
		{
			Pen penGray = new Pen(Color.DimGray, 1);
			gr.SmoothingMode = SmoothingMode.Default;
			
			for (int n=0; n<=5; n++)
				gr.DrawLine(penGray, n*PP_S_*1e-3f, H0_*1.5f, n*PP_S_*1e-3f, -H0_*1.5f);
			for (int n=-3; n<=3; n++)
				gr.DrawLine(penGray, 0, n*H0_*0.5f, PP_S_*TW_, n*H0_*0.5f);

			gr.DrawString("0", this.Font, Brushes.Black, -12, -5);
			string s1 = (den > 1) ? "1/" + den.ToString() : (1/den).ToString();
			gr.DrawString(s1, this.Font, Brushes.Black, -s1.Length*5.8f-6, -H0_-5);
			string s2 = (den > 1) ? "-1/" + den.ToString() : (-1/den).ToString();
			gr.DrawString(s2, this.Font, Brushes.Black, -s2.Length*5.8f-6, H0_-5);
	
			for (int n=0; n<=5; n++)
				gr.DrawString(n.ToString(), this.Font, Brushes.Black, n*PP_S_*1e-3f-4, H0_*1.5f+5);
			gr.DrawString("時間 [ms]", this.Font, Brushes.Black, 2*PP_S_*1e-3f+25, H0_*1.5f+18);
		
		}

		// 入力信号
		private float InputSignal(double f0, int n)
		{
			const double A0 = 4/Math.PI;
			const double B3 = 1.0/3.0;
			const double B5 = 1.0/5.0;

			double w0_t = 2*Math.PI*f0*(n - N_START_)*TS_;

			return (float)(A0*(Math.Sin(w0_t) + B3*Math.Sin(3*w0_t) + B5*Math.Sin(5*w0_t)));
		}

		// 基本周波数の変更の処理
		private void trackBar1_ValueChanged(object sender, EventArgs e)
		{
			label1.Text = (trackBar1.Value/100.0).ToString("0.00") + " kHz";
			f0_ = trackBar1.Value*10;
			Invalidate();
		}

		// 倍率の切り替えに対応
		private void domainUpDown1_SelectedItemChanged(object sender, EventArgs e)
		{
			Invalidate();
		}

		// フィルタの種類の切り替えに対応
		private void radioButton1_CheckedChanged(object sender, EventArgs e)
		{
			Invalidate();
			trackBar1.Select();
		}
	}
}
