DSP 실험 2번째 주에는 행렬연산에 대하여 배웠습니다. 수업시간에는 그냥 포인터에 배열을 할당하여, 행렬을 만들고, transpose한 행렬을 하나 더 만들어 두 행렬을 곱하는 연산을 해보았는데요. 사실 처음에 프로그래밍 언어에 익숙해지고, 내용에 익숙해지기 위해서는 직접 만들어서 실행해 보는 것이 좋지만, 익숙해지고 나서도 직접 만들어 쓰는 것은 피곤한 일이기도 하고, (물론 완벽하게 코드를 쓰시는 분에게는 다른 이야기겠지만)프로그램의 에러 확률이 높아지는 원인이 되기도 하죠. (옛날에 자료구조를 배우고 난 직후 스택이랑 큐, 벡터 등 자료구조들을 일일히 직접 만들어 쓰던때가 생각나네요 ㅋㅋ)

서론이 길었네요 ㅎㅎ. 어쨋든 이 포스팅에는 이러한 매트릭스 연산 뿐만 아니라 선형연산에 대하여 많은 함수 및 클래스를 지원하는 라이브러리인 Eigen library에 대하여 소개를 하고자 합니다.

먼저 Eigen library를 쓰려면 Eigen library를 다운받아야 겠죠? 구글에 Eigen이라고 검색을 하거나 아래의 링크를 클릭하여 Eigen library를 받아줍니다.

Eigen Main Page

링크를 클릭하면 아래와 같은 화면이 나타날 겁니다.

캬 Eigen is a C++ template library for linear algebra: matrices, vectors, numerical solvers, and related algorithms.

말 그래도 입니다. 그냥 선형대수학에 관한 것들은 거의 다 있죵 ㅎㅎ. 흠.. latest stable release가 현재로써는 3.2.5버젼이네요 ㅋㅋ Get it에서 library를 다운로드 해줍니다. 압축포맷이 여러가지가 있는데, 다 똑같은 겁니다. 따로 압축 프로그램이 안깔려 있다면 그냥 zip파일을 다운로드 받아서 원하는 곳에 압축을 풀어줍시다!(용량이 얼마 안돼서 금방 다운로드 될겁니다!)

압축푼 폴더를 보면

캬 이것저것 많이 있네요 ㅎㅎ cmake 어쩌고 저쩌고 써져있는 것들은 cmake로 라이브러리를 따로 만들때 사용되는 것들이랍니다. 뭐 지금 단계에선 알필요 없어요. 아, 그리고 eigen library는 따로 설치 같은게 필요없습니다. 그냥 간단하게 파일 경로만 인클루드 시켜주면 되요!

어쨋든... 다운로드 받아서 압축을 풀어주셨다면 이제 Eigen library를 사용할 준비가 다 된 것입니다. ㅋㅋ 이제 Eigen을 이용한 프로그램을 만들기 위해 Visual studio를 실행하도록 하죠!

즐거운 마음으로 콘솔 어플리케이션 프로젝트를 만들어 줍니다.

프로젝트를 클릭한 후 alt+enter 또는 우클릭 properties 또는 메뉴바 Project-properties클릭을 하여 속성창을 열어줍니다!

VC++ Directories에서 Include Directories 편집창을 열어줍니다. 직접 path쓰셔도 됩니다.


저같은 경우는 Downloads에 압축을 풀었습니다. eigen library 압축푼 폴더로 경로를 설정해줍니다. Select Forder누르고 ok ok!

그럼 이제 cpp파일에 Eigen library를 이용한 간단한 프로그램을 만들어 봅시다.


#include <iostream>
#include <Eigen/Dense>
#define PI 3.14159265359
using namespace Eigen;
using namespace std;

int main()
{
	MatrixXd m1 = MatrixXd(8, 8);
	MatrixXd m2, m3;

	//8*8행렬 m1에 값을 넣어줍니다.
	double* alpha = new double[8];
	for (int i = 0; i < 8; i++) {
		if (i == 0)
			alpha[i] = 1. / (2.*sqrt(2));
		else {
			alpha[i] = 0.5;
		}
	}

	for (int i = 0; i < 8; i++) {
		for (int j = 0; j < 8; j++) {
			m1(i,j) = alpha[i] * cos((PI*(2.*j + 1)*i) / 16);
		}
	}
	delete alpha;

	m2 = m1.transpose();//m2에 m1을 transpose한 값을 넣어줍니다.
	m3 = m1*m2;//m3에 m1과 m2를 곱한 값을 넣어줍니다.
	cout <<"m1"<<endl<< m1 << endl <<"m2"<<endl<< m2 << endl <<"m3"<<endl<< m3 << endl;//출력!
	return 0;
}

코드를 실행해보면!

m1과 m2와 m3가 출력된 것을 볼 수 있습니다. m3는 identity matrix이군요!(아주 작은 값들은 0이라고 봅시다 ㅎㅎ 1,2,3의 10의 -13승이니 0이라고 봐도 무방하죠.) 그렇다면 m1행렬을 A라 했을때, 

행렬 A는 orthogonal matrix(직교행렬)라고 볼 수 있겠군요!

Eigen library에 대한 자세한 documentation은 Eigen 페이지에 가면 있습니다.

오늘 포스팅은 여기까지 하겠습니다 ㅎㅎ

DSP 실험 첫번째 주에는 sin 곡선 그리기를 배웠는데요. 실험 내용은 간단했습니다. 그냥 c / c++에서 sin함수를 써서 데이터를 파일 출력으로 데이터를 저장한 다음 excel 외부데이터 가져오기를 통해 그래프를 그리고, 이를 통해 앨리어싱을 확인하는 실험을 했습니다.

하지만 이렇게 그냥 넘어가기는 아쉬우니 Microsoft Chart Controls과 C#을 이용하여 직접 앨리어싱을 확인하는 프로그램을 한번 짜보도록 하죠.

(Microsoft Chart controls는 VB와 C#으로 이용할 수 있습니다.)

일단 windows Forms Application에서 ms chart를 사용하려면 ms chart control을 설치해 줘야 합니다.

Microsoft.NET Framework 3.5용 Microsoft Chart Controls

위 링크를 클릭하여 설치를 하시거나 구글에 microsoft chart controls를 검색하여 chart controls를 설치해 줍니다.


1. 다운로드를 살포시 눌러줍니다.


2. MSChart.exe를 체크한 뒤 다음을 누르면 설치 파일이 다운로드 됩니다. 다운로드가 완료된 후 설치를 합니다. 뭐 체크하고 그런거 없으니 그냥 다음 다음 눌러주시면 설치가 완료됩니다.

이제 MSChart가 설치되었으니 확인해볼까요?


3. Visual Studio를 실행한 뒤, Windows Forms Application 프로젝트를 만들어 줍니다.


4. 툴박스의 Data탭을 보면 Chart control이 생긴것을 볼 수 있습니다.


흠 이제 프로그래밍을 해볼까요?

먼저 프로그램 시나리오는 이렇습니다.

프로그램 시나리오

1. 샘플링을 할 원신호의 진폭과 주파수, 그리고 위상을 입력받습니다.(출력 주기는 일일이 입력하기 귀찮으니 원신호의 10주기 정도를 출력해주기로 합시다.)

2. 샘플링 주파수를 입력받습니다.

3. 사용자가 실행 버튼을 누르면 차트컨트롤1에는 원신호를, 차트컨트롤2에 샘플링 결과값을 point로 출력을 합니다.

4. 사용자가 그래프 전환 버튼을 누를 때 마다 차트컨트롤2의 chart type을 point에서 spline으로, spline에서 point로 전환해줍니다.

(뭐 이건 작은 프로그램이니 클래스 다이어그램이랑 유즈케이스 등등은 할 필요가 없을거 같군요.)


그렇다면 어떠한 변수가 필요할까요?

일단 원신호의 진폭과 주파수, 그리고 위상을 입력받을 double형 변수 amp_origin, hz_origin, phase_origin이 필요하겠군요. 또 출력주기를 저장할 double형 변수 out_period와 샘플링 주파수를 입력받을 double형 변수 hz_sample 변수가 필요할 것 같습니다. 또, 차트1에 들어갈 수열을 저장하는 Series형 변수 series1과 차트2에 들어갈 수열을 저장하는 series2변수가 필요할거 같군요. 흠... 변수는 이 정도면 된것 같습니다.

그렇다면 이젠 메쏘드를 한번 생각해보도록하죠. 일단 실행버튼을 클릭했을때 실행되는 이벤트 메쏘드와 전환 버튼을 클릭했을때 실행될 이벤트 메쏘드가 필요하겠군요. 그 다음에는 원신호의 시리즈를 만들어서 차트에 넣어주는 makeSeries가 필요하겠군요. 또 차트2의 차트타입을 바꿔줄 changeChartType 메쏘드가 필요할 것 같습니다.


필요한 변수

double amp_origin, hz_origin, phase_origin, out_period, hz_sample;

Series series1, series2;

필요한 함수

void run_Click(object sender, EventArgs e); //run 버튼을 클릭했을때 실행되는 이벤트 메쏘드

void trans_Click(object sender, EventArgs e); //trans 버튼을 클릭했을때 실행되는 이벤트 메쏘드

void makeSeries(); //차트1과 2에 넣어줄 수열을 만들어주는 메쏘드

void changeChartType(); //차트2의 차트 타입을 바꿔줄 메쏘드

흠 다 된거 같군요! 이제 프로그램을 만들어봅시다!



어플리케이션 UI




Index

Control Type

Name

Text

Tab Index 

 1

 TextBox

 tb_amp_origin

 진폭

 0

 2

 TextBox

 tb_hz_origin

 주파수

 1

 3

 TextBox

 tb_phase_origin

 위상

 2

 4

 TextBox

 tb_hz_sample

 샘플링 주파수

 3

 5

 Button

 run

 실행

 4

 6

 Button

 trans

 전환

 5

 7

 Chart

 chart1

 

 6

 8

 Chart

 chart2

 

 7

저 같은 경우는 TextBox와 Label들의 font size를 20으로 설정하였습니다.

자 그렇다면 위의 표와 그림을 참고하여 toolbox에서 드래그하여 내려놓습니다. Label도 위 그림과 같이 text를 써서 배치해 주시고요.

제 코드를 그대로 쓰실 생각이면 위 표를 보고 Properties(속성)을 설정해주세요.

자 그럼 UI도 만들었으니 코딩을 해서 기능을 넣어 볼까요?



Code

Form1.cs에 가서 코딩을 합시다.

우리가 UI를 만들던 곳은 Form1.cs[Design]입니다. Form1.cs 하위 항목의 Form1을 클릭하면 Form1.cs 파일이 열립니다.

변수 선언


using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.Windows.Forms.DataVisualization.Charting;//추가해 줍니다. 나머지는 이미 추가되어 있습니다.

namespace MyApp
{
    public partial class Form1 : Form
    {
        double amp_origin, hz_origin, phase_origin, out_period, hz_sample; //클래스에 멤버들을 선언해 줍시다.
        Series series1 = new Series();
        Series series2 = new Series();//series 변수를 선언하고 변수에 객체를 할당해 줍니다.
        double period_constant = 10.0; //몇주기를 출력할지 정해줍니다.

        public Form1()
        {
            InitializeComponent();
        }

    }
}

자 변수들을 추가해 줍시다!

이제 makeSeries 메쏘드를 만들어 볼까요?


private void makeSeries()
        {
            chart1.Series.Clear();
            chart2.Series.Clear();//chart안에 있는 Series들을 지워줍니다.

            series1.Points.Clear();
            series2.Points.Clear();//series안에 있는 값들을 지워줍니다.

            series1.ChartType = SeriesChartType.Spline;
            series2.ChartType = SeriesChartType.Point; //차트타입을 chart1은 spline으로 chart2는 point로 설정해줍니다.

            out_period = (1.0/hz_origin) * period_constant;//out_period를 원신호의 10배로 합니다.

            for(double i=0 ; i<out_period ; i+= 1.0 / (hz_origin * 10)/*원신호의 series를 만들 것이기 때문에 10배로 샘플링해줍니다.*/)
            {
                series1.Points.AddXY(i, amp_origin * Math.Cos(2.0 * Math.PI * hz_origin * i + phase_origin));
                //series1에 수열을 넣어줍니다. x값은 time(i), y값은 결과값(cos(2*PI*f*t+theta))을 입력합니다.
            }

            for (double i = 0; i < out_period; i += 1.0 / hz_sample/*원신호를 샘플링 주파수로 샘플링해줍니다.*/)
            {
                series2.Points.AddXY(i, amp_origin * Math.Cos(2.0 * Math.PI * hz_origin * i + phase_origin));
                //series2에 수열을 넣어줍니다. x값은 time(i), y값은 결과값(cos(2*PI*f*t+theta))을 입력합니다.
            }

            chart1.Series.Add(series1);
            chart2.Series.Add(series2);//chart에 각각 series를 추가해 줍니다.
        }

이제 changeChartType 메쏘드를 만들어봅시다.


private void changeChartType()
        {
            if (chart2.Series[0].ChartType == SeriesChartType.Point)
                chart2.Series[0].ChartType = SeriesChartType.Spline;
            else
                chart2.Series[0].ChartType = SeriesChartType.Point;
            //chart2의 series의 차트타입이 point면 spline으로 아니면 point로 바꿔줍니다.
        }

이제 실행버튼을 클릭했을때 실행되는 이벤트 메쏘드를 만들어봅시다.

실행버튼을 더블클릭하면 자동으로 Form1.Designer.cs에 this.run.Click += new System.EventHandler(this.run_Click); 라고 이벤트 헨들러가 만들어지고 Form1.cs에 메쏘드가 만들어집니다.

직접 코드를 쓰실분은 Form1.Designer.cs에 this.run.Click += new System.EventHandler(this.run_Click);을 쓰고, Form1.cs에 메쏘드를 직접 작성하시면 됩니다.


private void run_Click(object sender, EventArgs e)
        {
            amp_origin = Double.Parse(tb_amp_origin.Text.ToString());
            hz_origin = Double.Parse(tb_hz_origin.Text.ToString());
            phase_origin = Double.Parse(tb_phase_origin.Text.ToString());
            hz_sample = Double.Parse(tb_hz_sample.Text.ToString());//text box로 입력받은 값을 멤버변수에 입력합니다.

            makeSeries();//makeSeries를 실행합니다.
        }

이제 전환버튼을 클릭했을때 실행되는 이벤트 메쏘드를 만들어봅시다.

전환버튼을 더블클릭하면 자동으로 Form1.Designer.cs에 this.trans.Click += new System.EventHandler(this.trans_Click); 라고 이벤트 헨들러가 만들어지고 Form1.cs에 메쏘드가 만들어집니다.


private void trans_Click(object sender, EventArgs e)
        {
            changeChartType();
        }

전체소스입니다.


using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.Windows.Forms.DataVisualization.Charting;

namespace MyApp
{
    public partial class Form1 : Form
    {
        double amp_origin, hz_origin, phase_origin, out_period, hz_sample;
        Series series1 = new Series();
        Series series2 = new Series();
        double period_constant = 10.0; //몇주기를 출력할지 정해줍니다.

        private void trans_Click(object sender, EventArgs e)
        {
            changeChartType();
        }

        private void run_Click(object sender, EventArgs e)
        {
            amp_origin = Double.Parse(tb_amp_origin.Text.ToString());
            hz_origin = Double.Parse(tb_hz_origin.Text.ToString());
            phase_origin = Double.Parse(tb_phase_origin.Text.ToString());
            hz_sample = Double.Parse(tb_hz_sample.Text.ToString());//text box로 입력받은 값을 멤버변수에 입력합니다.

            makeSeries();//makeSeries를 실행합니다.
        }

        public Form1()
        {
            InitializeComponent();
        }

        private void makeSeries()
        {
            chart1.Series.Clear();
            chart2.Series.Clear();//chart안에 있는 Series들을 지워줍니다.

            series1.Points.Clear();
            series2.Points.Clear();//series안에 있는 값들을 지워줍니다.

            series1.ChartType = SeriesChartType.Spline;
            series2.ChartType = SeriesChartType.Point; //차트타입을 chart1은 spline으로 chart2는 point로 설정해줍니다.

            out_period = (1.0/hz_origin) * period_constant;//out_period를 원신호의 10배로 합니다.

            for(double i=0 ; i<out_period ; i+= 1.0 / (hz_origin * 10)/*원신호의 series를 만들 것이기 때문에 10배로 샘플링해줍니다.*/)
            {
                series1.Points.AddXY(i, amp_origin * Math.Cos(2.0 * Math.PI * hz_origin * i + phase_origin));
                //series1에 수열을 넣어줍니다. x값은 time(i), y값은 결과값(cos(2*PI*f*t+theta))을 입력합니다.
            }

            for (double i = 0; i < out_period; i += 1.0 / hz_sample/*원신호를 샘플링 주파수로 샘플링해줍니다.*/)
            {
                series2.Points.AddXY(i, amp_origin * Math.Cos(2.0 * Math.PI * hz_origin * i + phase_origin));
                //series2에 수열을 넣어줍니다. x값은 time(i), y값은 결과값(cos(2*PI*f*t+theta))을 입력합니다.
            }

            chart1.Series.Add(series1);
            chart2.Series.Add(series2);//chart에 각각 series를 추가해 줍니다.
        }

        private void changeChartType()
        {
            if (chart2.Series[0].ChartType == SeriesChartType.Point)
                chart2.Series[0].ChartType = SeriesChartType.Spline;
            else
                chart2.Series[0].ChartType = SeriesChartType.Point;
            //chart2의 series의 차트타입이 point면 spline으로 아니면 point로 바꿔줍니다.
        }
    }
}

자 이제 다 만들었으니 한번 실행을 해볼까요?

ctrl+F5를 눌러 실행을 해 봅시다.

정상적으로 실행이 되는 군요!


오버샘플링을 한번 해보겠습니다. 값을 입력합니다.


이제 실행을 누르면!

흠. 샘플링이 되긴 했는데, 잘 모르겠군요. 전환을 눌러 보겠습니다.

흠... 좀 다른거 같은 느낌도 들지만 자세히 보시면 같은 것을 볼 수 있습니다.

시작값과 끝값이 달라서 이런 현상이 일어나는데, 뭐 이런 현상을 해결하려면 chart1과 chart2의 x축을 설정해주면 됩니다.


            chart1.ChartAreas[0].AxisX.Maximum = out_period; //x축 max값 설정
            chart1.ChartAreas[0].AxisX.Minimum = 0; //x축 min값 설정
            chart1.ChartAreas[0].AxisX.IsStartedFromZero = true; //x축 0부터 시작

            chart2.ChartAreas[0].AxisX.Maximum = out_period;
            chart2.ChartAreas[0].AxisX.Minimum = 0;
            chart2.ChartAreas[0].AxisX.IsStartedFromZero = true;

아래는 수정된 전체 코드입니다.


using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.Windows.Forms.DataVisualization.Charting;

namespace MyApp
{
    public partial class Form1 : Form
    {
        double amp_origin, hz_origin, phase_origin, out_period, hz_sample;
        Series series1 = new Series();
        Series series2 = new Series();
        double period_constant = 10.0; //몇주기를 출력할지 정해줍니다.

        private void trans_Click(object sender, EventArgs e)
        {
            changeChartType();
        }

        private void run_Click(object sender, EventArgs e)
        {
            amp_origin = Double.Parse(tb_amp_origin.Text.ToString());
            hz_origin = Double.Parse(tb_hz_origin.Text.ToString());
            phase_origin = Double.Parse(tb_phase_origin.Text.ToString());
            hz_sample = Double.Parse(tb_hz_sample.Text.ToString());//text box로 입력받은 값을 멤버변수에 입력합니다.

            makeSeries();//makeSeries를 실행합니다.
        }

        public Form1()
        {
            InitializeComponent();
        }

        private void makeSeries()
        {
            chart1.Series.Clear();
            chart2.Series.Clear();//chart안에 있는 Series들을 지워줍니다.

            series1.Points.Clear();
            series2.Points.Clear();//series안에 있는 값들을 지워줍니다.

            series1.ChartType = SeriesChartType.Spline;
            series2.ChartType = SeriesChartType.Point; //차트타입을 chart1은 spline으로 chart2는 point로 설정해줍니다.

            out_period = (1.0/hz_origin) * period_constant;//out_period를 원신호의 10배로 합니다.

            for(double i=0 ; i<out_period ; i+= 1.0 / (hz_origin * 10)/*원신호의 series를 만들 것이기 때문에 10배로 샘플링해줍니다.*/)
            {
                series1.Points.AddXY(i, amp_origin * Math.Cos(2.0 * Math.PI * hz_origin * i + phase_origin));
                //series1에 수열을 넣어줍니다. x값은 time(i), y값은 결과값(cos(2*PI*f*t+theta))을 입력합니다.
            }

            for (double i = 0; i < out_period; i += 1.0 / hz_sample/*원신호를 샘플링 주파수로 샘플링해줍니다.*/)
            {
                series2.Points.AddXY(i, amp_origin * Math.Cos(2.0 * Math.PI * hz_origin * i + phase_origin));
                //series2에 수열을 넣어줍니다. x값은 time(i), y값은 결과값(cos(2*PI*f*t+theta))을 입력합니다.
            }

            chart1.ChartAreas[0].AxisX.Maximum = out_period;
            chart1.ChartAreas[0].AxisX.Minimum = 0;
            chart1.ChartAreas[0].AxisX.IsStartedFromZero = true;
            chart2.ChartAreas[0].AxisX.Maximum = out_period;
            chart2.ChartAreas[0].AxisX.Minimum = 0;
            chart2.ChartAreas[0].AxisX.IsStartedFromZero = true;

            chart1.Series.Add(series1);
            chart2.Series.Add(series2);//chart에 각각 series를 추가해 줍니다.
        }

        private void changeChartType()
        {
            if (chart2.Series[0].ChartType == SeriesChartType.Point)
                chart2.Series[0].ChartType = SeriesChartType.Spline;
            else
                chart2.Series[0].ChartType = SeriesChartType.Point;
            //chart2의 series의 차트타입이 point면 spline으로 아니면 point로 바꿔줍니다.
        }
    }
}


AxisY의 maximum과 minimum도 같은 방식으로 설정할 수 있습니다. 뭐 차트 라이브러리의 모든 메쏘드를 설명할 순 없으니 넘어가도록 하죵 ㅎㅎ

뭐 메쏘드 이름을 보는 순간 어떤 기능을 하는 메쏘드 인지, 어떻게 사용하는지 직관적으로 알 수 있을 겁니다. ㅎㅎ

수정된 프로그램을 돌려보죠! ㅋㅋ

좀 나아졌군요ㅎㅎ

이제 다운샘플링을 한번 해보겠습니다.

한눈에 보기에도 제대로 된 신호가 안나올거 같군요. 전환을 눌러보겠습니다.

이런... 역시나 앨리어싱이 일어났군요 ㅠㅠ

앨리어싱이 일어난 신호를 분석해보면 한주기가 0.025초니까 40Hz군요. 원신호가 200Hz인데 40Hz가 되버렸군요.


이러한 현상이 일어나는 이유는 다음 그림과 같습니다.

샘플링을 하면 주파수 대역에서 신호와 샘플링 주파수가 길쌈연산(컨벌루젼)이 되죠. 그래서 오른쪽 그림과 같이 되어 40Hz가 나옵니다. 240Hz로 샘플링 해도 마찬가지로 40Hz로 나와 버리겠죠.

240Hz로 샘플링 한 그림

역시나 40Hz가 나오죠?

다음은 앨리어싱에 대한 그림입니다.

출처: Sampling Theoren url: http://cnx.org/contents/f65cf38c-c974-4a34-87b0-d4dc8863c79c@8/Sampling-Theorem

마지막으로 Series에 이름을 붙여서 사용할 수도 있습니다.


using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.Windows.Forms.DataVisualization.Charting;

namespace MyApp
{
    public partial class Form1 : Form
    {
        double amp_origin, hz_origin, phase_origin, out_period, hz_sample;
        Series series1 = new Series();
        Series series2 = new Series();
        double period_constant = 10.0; //몇주기를 출력할지 정해줍니다.

        private void trans_Click(object sender, EventArgs e)
        {
            changeChartType();
        }

        private void run_Click(object sender, EventArgs e)
        {
            amp_origin = Double.Parse(tb_amp_origin.Text.ToString());
            hz_origin = Double.Parse(tb_hz_origin.Text.ToString());
            phase_origin = Double.Parse(tb_phase_origin.Text.ToString());
            hz_sample = Double.Parse(tb_hz_sample.Text.ToString());//text box로 입력받은 값을 멤버변수에 입력합니다.

            makeSeries();//makeSeries를 실행합니다.
        }

        public Form1()
        {
            InitializeComponent();
        }

        private void makeSeries()
        {
            chart1.Series.Clear();
            chart2.Series.Clear();//chart안에 있는 Series들을 지워줍니다.

            series1.Points.Clear();
            series2.Points.Clear();//series안에 있는 값들을 지워줍니다.

            series1.ChartType = SeriesChartType.Spline;
            series2.ChartType = SeriesChartType.Point; //차트타입을 chart1은 spline으로 chart2는 point로 설정해줍니다.

            out_period = (1.0/hz_origin) * period_constant;//out_period를 원신호의 10배로 합니다.

            for(double i=0 ; i<out_period ; i+= 1.0 / (hz_origin * 10)/*원신호의 series를 만들 것이기 때문에 10배로 샘플링해줍니다.*/)
            {
                series1.Points.AddXY(i, amp_origin * Math.Cos(2.0 * Math.PI * hz_origin * i + phase_origin));
                //series1에 수열을 넣어줍니다. x값은 time(i), y값은 결과값(cos(2*PI*f*t+theta))을 입력합니다.
            }

            for (double i = 0; i < out_period; i += 1.0 / hz_sample/*원신호를 샘플링 주파수로 샘플링해줍니다.*/)
            {
                series2.Points.AddXY(i, amp_origin * Math.Cos(2.0 * Math.PI * hz_origin * i + phase_origin));
                //series2에 수열을 넣어줍니다. x값은 time(i), y값은 결과값(cos(2*PI*f*t+theta))을 입력합니다.
            }

            chart1.ChartAreas[0].AxisX.Maximum = out_period;
            chart1.ChartAreas[0].AxisX.Minimum = 0;
            chart1.ChartAreas[0].AxisX.IsStartedFromZero = true;
            chart2.ChartAreas[0].AxisX.Maximum = out_period;
            chart2.ChartAreas[0].AxisX.Minimum = 0;
            chart2.ChartAreas[0].AxisX.IsStartedFromZero = true;

            series1.Name = "Original Signal";
            series2.Name = "Sampling Signal";//이름을 설정한다.

            chart1.Series.Add(series1);
            chart2.Series.Add(series2);//chart에 각각 series를 추가해 줍니다.
        }

        private void changeChartType()
        {
            if (chart2.Series["Sampling Signal"].ChartType == SeriesChartType.Point)
                chart2.Series["Sampling Signal"].ChartType = SeriesChartType.Spline;
            else
                chart2.Series["Sampling Signal"].ChartType = SeriesChartType.Point;
            //chart2의 series의 차트타입이 point면 spline으로 아니면 point로 바꿔줍니다.
        }
    }
}

그럼 실행했을때 이렇게 되죠.

이렇게 하면 나중에 한 chart에 많은 Series를 넣을때 안 헷갈릴수 있습니다. 저 같은 경우는 어차피 시리즈가 하나니 그냥 디폴트로 하겠습니다. ㅋㅋ 나중에 시간남으시면 해보세요. 맨 처음 켰을때 시리즈 이름을 바꾼생태로 보이게 하려면 굳이 코드로 할 필요 없이 차트의 프로퍼티에서 시리즈를 클릭하여 시리즈 에디터를 실행한 다음 시리즈의 이름을 바꿔주면 됩니다.


왼쪽그림은 차트를 클릭했을때 속성 화면이고 왼쪽 화면에서 시리즈를 클릭한다음 ...버튼을 누르시면 Series Collection Editor를 볼 수 있습니다. Series Collection Editor의 왼쪽을 보시면 시리즈의 인덱스 번호와 이름이 보이죠? 바꾸고자 하는 시리즈를 클릭 한 뒤 오른쪽의 Data의 Name을 바꿔주시면 됩니다. 나중에 시간이 되면 ChartArea와 Series에 대하여 좀 더 자세히 설명하는 포스팅을 올리도록 하겠습니다.

흠 오늘 포스팅은 여기까지 입니다.

아래는 위의 파일들을 올려놓은 link 입니다.

첫번째 링크는 이 프로젝트를 올려놓은 Github링크고요.

두번째 링크는 이 프로젝트를 릴리즈한 실행파일과 프로젝트 압축파일을 올려놓은 구글 드라이브 링크입니다.

코딩을 안해보고 어플리케이션을 그냥 사용만 해보실 분은 구글드라이브 링크를 클릭하셔서 exe파일만 다운받아 실행해 보시면 될 거 같습니다.

아마 실행할때 exe파일을 윈도우 디펜더가 인증받은 앱이 아니라고 경고할 수도 있어요. 그럴땐 그냥 추가사항 누른다음 실행 누르시면 됩니다.(위험한 어플 아니니까요 ㅎㅎ)

주의! .NET Framwork 3.5이상이어야함!

뭐 윈도우 업데이트를 꺼놓으시지만 않으면 거의다 .NET Framework 3.5이상일 겁니다.

혹시 모르니 링크걸어 놓을게요.

.NET Framework

DSP_Ex_1 Github

San's Google Drive_DSP_Ex

'Electronics > DSP Experiment' 카테고리의 다른 글

Eigen c++ library를 이용한 Matrix연산  (0) 2015.09.22

이미 아시는 분들도 굉장히 많으시겠지만 혹시나 도움이 될까하여 올립니다. 
아마 visual studio 2010에서는 c코드들이 잘 컴파일이 되고 실행이 되나, visual studio 2012, 2013, 2015에서는 scanf나 fopen등이 컴파일이 되지 않아 고생하시는 분들도 있을거라 생각됩니다.

일단 그 이유부터 말씀드리자면 개발자들이 함수의 오류가 발견되면, 그 함수를 deprecate해버리고 보안버젼의 새로운 함수를 추가해 버리기 때문입니다. 
(ISO에서 c++11 표준을 정하니 ms에서 visual studio 2012부터 바꾼거죠) 
예를들어 scanf의 경우 보안상의 문제로 c++ 98에서 c++11으로 넘어가는 과정에서 deprecated되버리고 scanf_s이라는 보안버젼 함수가 새로 생겨버렸습니다. 
visual studio 2010에서는 잘 컴파일이 되는데 visual studio 2012이상 버젼에서는 정상적으로 컴파일이 안되는 이유는 visual studio 2010에서는 default로 c++98을 사용하고, visual studio 2012이상 버전부터는 c++ 11을 사용하기 때문입니다. 

즉, visual studio 2012 이상 버젼부터는 scanf를 사용하면 warning이 뜨면서 컴파일이 안됩니다. 

scanf는 최근 몇년동안 보안 문제로 말이 있었죠. 뭐 이유를 알아보면 여러가지가 있지만 콘솔 io의 경우 input size를 정해주지 않으면 command를 같이 입력하는 방식으로 공격이 가능하다는데, 제가 보안쪽을 잘 몰라서 뭐라고 말을 더 못하겠군요. 뭐 이부분에 대해선 다른 블로거 님들에게 패스를 하고, 본론으로 다시 돌아가도록 하겠습니다.


이렇게 코드를 쓰고 컴파일을 하면 보통 에러가 나며 컴파일이 안됩니다.

#include <stdio.h>
int main()
{
	int inputNum;
	scanf("%d", &inputNum);
	printf("%d \n", inputNum);

	return 0;
}

이렇게 하고 start하면 아래 그림과 같은 메세지가 뜨면서 컴파일이 되지 않죠.


에러 내용을 해석해보면, scanf라는 함수가 안전하지 않으며, scanf대신 scanf_s라는 새로운 함수를 사용하는 것을 고려해 보라고 나옵니다. 또, deprecation된 함수를 사용하려면 _CRT_SECURE_NO_WARNINGS를 사용하라고 나오네요.(deprecation을 사용하지 않으려면)


그러면 지금부터 이러한 문제를 해결하는 방법을 몇가지 말씀 드리려고 합니다.

1. _CRT_SECURE_NO_WARNINGS를 사용하여 warning을 무시한다.


#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
int main()
{
	int inputNum;
	scanf("%d", &inputNum);
	printf("%d \n", inputNum);

	return 0;
}

이렇게 코드의 가장 윗부분에 _CRT_SECURE_NO_WARNINGS를 디파인 해주면 정상적으로 컴파일이 되는 것을 확인할 수 있습니다.

또는 에러 코드를 보면 C4996이라고 나오죠. 이 에러코드를 직접 무시하는 코드를 헤더 윗쪽에 추가 해서 정상적으로 컴파일을 하게 만드는 방법도 있습니다. 좀 자세히 말하자면 우리가 프로젝트를 만들때 SDL 체크를 사용하면 컴파일시 추가적으로 여러가지 보안 검사를 실시 하는데, 검사중에 경고 코드 C4996(사용되지 않는 함수 사용 경고)기능을 사용하지 않도록 설정하는 것입니다.


warning에 대한 링크 : warning-msdn

#pragma warning(disable: 4996)
#include <stdio.h>
int main()
{
	int inputNum;
	scanf("%d", &inputNum);
	printf("%d \n", inputNum);

	return 0;
}

이런식으로요 ㅎㅎ.

pragma에 대해선 나중에 #if, #ifdef등과 함께 나중에 한번 포스팅 하도록 하겠습니다.

2. 대세에따라 secure 버젼의 새로운 함수를 사용한다.

fopen의 경우 주소를 리턴하는게 보안상 문제가 있다고 판명되어 주소를 리턴하지 않고 인자로 받아 설정하는 fopen_s라는 secure버젼의 함수가 새로 만들어졌습니다.

사실 어떠한 함수가 deprecated가 되었다는 것은 sw전문가들이 문제가 있다고 생각되어 deprecated를 시키고 보다 안전한 함수를 새로 만들었다는 것이기 때문에 대세를 따라 secure버젼을 사용하는 것이 좋다고 개인적으로 생각합니다.


#include <stdio.h>

int main()
{
	FILE *data;
	fopen_s(&data, "test.txt", "w");
	fprintf(data, "%s", "Hello world!");
	fclose(data);

	return 0;
}

요런식으로 씁니다. ㅎㅎ


#include <stdio.h>

int main()
{
	char s[16];
	scanf_s("%s", &s, 16);//마지막에 input data size를 적어줍니다.(이게 scanf와 비교하여 바뀐부분이죠^^)
	printf(s);

	return 0;
}

scanf_s의 경우는 이런식으로 씁니다.

뭐 이러한 정보들은 구글링하면 바로바로 나오니 그때 그때 사용법을 검색해서 쓰기 쉽습니다.

유용한 사이트

CPlusPlus Page

MSDN Page

3. Project 생성시 SDL(Security Development Lifecycle) checks 옵션을 체크 해제한다.

이걸 체크해제하고 프로젝트를 만들면 추가적인 오류검사를 수행하지 않습니다. 예를 들어 함수의 반환형을 int로 해놓고 아무것도 반환하지 않아도 컴파일이 되고 실행이 됩니다. 뭐 컴파일이야 되겠지만은 별로 권장하고 싶진 않네요.


#include <stdio.h>

int main() {
	int inputNum;
	scanf("%d", &inputNum);
	printf("%d \n", inputNum);
}

위와 같은 코드도 컴파일이 됩니다.

어쨋든 SDL chechs 해제를 한번 해보죠.

1. 프로젝트를 만들어 줍니다.


2. 다음을 클릭합니다.


3. SDL checks의 체크를 해제해 줍니다.


4. 위의 코드를 한번 실행해 봅니다.

아주 잘 실행이 되는군요 ㅎㅎ



SDL에 대한 /sdl 링크 하나 남기겠습니다.

MSDN SDL

뭐 오늘 포스팅은 여기까지입니다. 마지막 방법은 되도록 쓰지 마세요 ㅎㅎ

'Programming > C/C++' 카테고리의 다른 글

Endian(Little endian, Big endian)  (0) 2015.10.08

+ Recent posts