본문 바로가기

뭐라도 공부해보자!!( 이론 )/Digital Signal Processing

Exercise 1 : DSP 실습 준비(여러 함수들 준비)

반응형

 이론적인 공부만 하기 보다는, 프로그래밍을 통해서 구현해봄으로써 복습과 동시에 프로그래밍 공부도 같이 하는 것이 어떨까라는 생각에서 출발하였다.

 

 지금 현재 짜고 있는 프로그램은 MATLAB과 같이 완결된 프로그램이기 보다는 DSP 실습을 위한 함수의 시험을 위한 테스트 프로젝트이다. 하나의 프로그램을 완성하는 것을 목적으로 하기에는 부가적인 부분에 시간을 뺏겨서, 당초 목적인 DSP 이론 실습과 멀어질 것 같아서 일단은 사용자의 입력 부분은 배제하였다. 다만 별개로 차후에 어느정도 이론적인 공부와 그에 병행한 실습이 이루어져 필요한 함수들이 누적된 후, 마음이 동하면 한 번 프로그램을 짜보는 것도 나쁘지 않겠다는 생각이 든다.

 

 프로그램의 목적은 사용자가 지정한 일차원 입력들을 사용자가 지정한 시스템에 적용시켜 결과값을 출력할 수 있도록 구성하였다. 기본적으로 콘솔 어플리케이션이고, 앞에서 언급하였던 것처럼 사용자의 입력부는 구현을 하지 않았다.(즉 입력과 시스템은 별도로 소스 코드에 작성해 주어야 한다!!) 그렇다고 입력을 손으로 일일이 넣는 것도 번거롭기에, 자주 사용하는 신호를 형성할 수 있는 함수들도 구현해줄 필요가 있었다.

 

  특별하게 구조가 있는 것이 아니고 사용자가 필요한 작업이 있다면 직접 main 함수에 구현을 하여 결과를 얻어야 하는 방식이다. 계속 코딩을 할 수는 없으니 편의를 함수들을 구현해 놓았고, 해당 함수들은 BasicFunction.h, BasicFunction.cpp에 정의되어 있다. 사용자가 직접 정의할 필요가 있는 함수는 별도로 UserFunction.h, UserFunction.cpp에 직접 작성해야 한다. 다만 한 가지의 규칙이 있다. 메모리 해제를 일괄적으로 하기 위해서 이를 관리해줄 객체를 정의해 놓았고, 이 객체의 관리를 받는 행렬을 생성하는 함수가 있다. 해당 객체는 생성된 행렬의 주소를 저장하고 있다가, 프로그램이 종료될 때, 일괄적으로 메모리를 해제하는 역할을 한다. 행렬 형태의 입력을 선언함에 있어서는 안정성을 위해 제공된 함수를 사용할 것을 권장한다. 

 

 다음은 행렬들을 관리하는 클래스이다. 동적 배열을 사용함에 있어서, 할당한 메모리를 해제해주는 것은 중요한 일이다. 일일이 메모리를 해제하기 보다는 하나의 관리 클래스를 만들어 관리하는 것이 실수를 줄이고, 편의성이 높아 구현하였다.

//1xN 형태의 double 행렬들을 관리하는 클래스입니다. 
//프로그램이 종료될 때, 모든 동적 배열을 해제하는 역할을 합니다.

class ArrDestroyer {
private:
	std::list<double*> toDestroy;

	int cnt = 0;
public:

	void add(double* toAdd);

	int size();

	void destroy();
};

void ArrDestroyer::add(double* toAdd) {
	toDestroy.push_back(toAdd);
	cnt++;
}

int ArrDestroyer::size() {
	return cnt;
}

void ArrDestroyer::destroy() {
	for (auto each : toDestroy) {
		if (each != NULL) {
			delete[] each;
			each = NULL;
			cnt--;
		}
	}
	std::cout << "모든 동적 배열이 삭제 되었습니다.";
}

//1xN 형태의 point 행렬들을 관리하는 클래스입니다. 
//당장 사용할 것은 아니고, 혹시 그래프를 그릴 일을 대비해서 만든 클래스입니다. 
//프로그램이 종료될 때, 모든 동적 배열을 해제하는 역할을 합니다.

struct point
{
	double x;
	double y;
};


class PointArrDestroyer {
private:
	std::list<point*> toDestroy;

	int cnt = 0;
public:

	void add(point* toAdd);

	int size();

	void destroy();
};

void PointArrDestroyer::add(point* toAdd) {
	toDestroy.push_back(toAdd);
	cnt++;
}

int PointArrDestroyer::size() {
	return cnt;
}

void PointArrDestroyer::destroy() {
	for (auto each : toDestroy) {
		if (each != NULL) {
			delete[] each;
			each = NULL;
			cnt--;
		}
	}
	std::cout << "모든 동적 배열이 삭제 되었습니다.";
}

 

 다음은 1xN 행렬을 생성하고 반환하는 함수들이다.

//사용자가 입력한 크기의 배열을 변환
double* UserArr(int size, ArrDestroyer& destroyer) {
	double* newArr = new double[size];

	destroyer.add(newArr);

	return newArr;
}

//배열의 원소가 0이고 크기가 n인 배열 반환
double* Zeros(int size, ArrDestroyer& destroyer) {
	double* newArr = new double[size];

	for (int i = 0; i < size; i++) newArr[i] = 0;

	destroyer.add(newArr);

	return newArr;
}

//사용자가 지정한 시작점과 끝점이 있을 때, 일정 간격으로 증가하는 수들을 원소로 하는 배열 반환 [start, end)
//예를 들어 시작점이 1, 끝점이 10, 간격이 1이면 1~9 까지 수를 원소로 하는 행렬을 반환한다.
double* BasicArr(double start, double end, double gap, ArrDestroyer& destroyer) {
	int arrSize = abs((end - start) / gap);

	double* newArr = new double[arrSize];

	for (int i = 0; i < arrSize; i++) newArr[i] = start + i * gap;

	destroyer.add(newArr);

	return newArr;
}

 

다음은 부가적으로 행렬을 조작하거나, 특수한 형태의 신호의 행렬을 반환하는 함수들이다.

//배열 복사(복사한 배열을 반환한다.)
double* copy(double* arr, ArrDestroyer& des) {
	int sz = ArraySize(arr);

	double* newArr = new double[sz];

	for (int i = 0; i < sz; i++) newArr[i] = arr[i];

	des.add(newArr);

	return newArr;
}

//입력의 별도 저장 없이 무기억 시스템을 지난 출력 배열을 반환
//모든 원소에 대해서 함수를 적용한다.
double* InputToOutput(double* arr, double(*fp)(double)) {
	int sz = ArraySize(arr);

	for (int i = 0; i < sz; i++) arr[i] = fp(arr[i]);

	return arr;
}

//무기억 시스템을 지난 출력 배열을 반환
//모든 원소에 대해 함수를 적용한 후, 결과 행렬을 새로운 행렬로 반환한다.
double* GetOutputArr(double* arr, double(*fp)(double), ArrDestroyer& destroyer) {
	int sz = ArraySize(arr);

	double* output = new double[sz];

	for (int i = 0; i < sz; i++) output[i] = fp(arr[i]);

	destroyer.add(output);

	return output;
}

//사인 신호 배열 반환
double* GetSinSignal(double start, double end, double gap, ArrDestroyer& destroyer) {
	double* newArr = InputToOutput(BasicArr(start, end, gap, destroyer), sin);

	return newArr;
}

//코사인 신호 배열 반환
double* GetCosSignal(double start, double end, double gap, ArrDestroyer& destroyer) {
	double* newArr = InputToOutput(BasicArr(start, end, gap, destroyer), cos);

	return newArr;
}

//지수 신호 반환
double* GetExpSignal(double start, double end, double gap, ArrDestroyer& destroyer) {
	double* newArr = InputToOutput(BasicArr(start, end, gap, destroyer), exp);

	return newArr;
}

//계단 신호 반환
double step(double x) {
	if (x >= 0) return 1;
	else return 0;
}
double* GetStepSignal(double start, double end, double gap, ArrDestroyer& destroyer) {
	double* newArr = InputToOutput(BasicArr(start, end, gap, destroyer), step);

	return newArr;
}

//램프 신호 반환
double ramp(double x) {
	if (x >= 0) return x;
	else return 0;
}
double* GetRampSignal(double start, double end, double gap, ArrDestroyer& destroyer) {
	double* newArr = InputToOutput(BasicArr(start, end, gap, destroyer), ramp);

	return newArr;
}

 

  실제 실습을 할 때는 필요한 함수들을 이용하여 다음과 같은 형식으로 코드를 작성하면 된다.

#include "BasicFunc.h"
#include "UserDefFunction.h"

int main() {
	ArrDestroyer arrDes;
	PointArrDestroyer pArrDes;
    
    //여기에 필요한 코드 구현
    
	des.destroy();
	pArrDes.destroy();
    
	return 0;
}

 

 지금 구현한 함수들 말고도, 더 필요한 함수들이 있을 것이다. 그런 함수들이 있을 때마다, 구현하고, 추가하는 방식으로 보충할 계획이다. 만약 보충한 함수들이 있다면 실습 글에서 실습에 대한 설명에 앞서서 간단하게 설명하는 것을 알려주겠다. 이 것으로 이번 글을 마친다.

 

코드 전체를 보려면 여기로

https://github.com/youngSeok0413/-

 

GitHub - youngSeok0413/-: 디지털 신호 프로세싱 실습

디지털 신호 프로세싱 실습. Contribute to youngSeok0413/- development by creating an account on GitHub.

github.com

 

반응형