Выбор и обоснование переменных




Содержание

Постановка задачи……………………………………………………………………………..3

Аналитический обзор…………………………………………………………………………4

Разработка пользовательского интерфейса……………………………………..5

Диаграмма потока данных………………………………………………………………..6

Диаграмма классов……………………………………………………………………………7

Выбор и обоснование переменных…………………………………………………..8

Код программы………………………………………………………………………………….9

kursDlg…………………………………………………………………………………………………….9

MyPaint1………………………………………………………………………………………………..15

MyPaint2…………………………………………………………………………………………………17

SaveIm…………………………………………………………………………………………………….19

Файлы заголовков…………………………………………………………………………………21

Пример работы…………………………………………………………………………………24

Контрольный пример……………………………………………………………………….26

Инструкция пользователю……………………………………………………………….28

Вывод………………………………………………………………………………………………..29

 

Постановка задачи

Для сигнала вида: 𝑥(𝑡)=𝑎1∗sin(2*π*f1*t+c1)+…+𝑎10∗sin(2*π*f10*t+c10), где a[1..10] – амплитуда, измеряющаяся в метрах и принимающая любые значения, f[1..10] – реальная частота, измеряющаяся в МГц и принимающая значения [0..5), c[1..10] – фаза, измеряющаяся в радианах и принимающая значения [0..2*π], так же есть количество точек N, которые будут отрисовываться на графике, принимающее значения [100..1000]

вычислить дискретное преобразование Фурье (ДПФ), нарисовать график функции и график модуля ее ДПФ, предоставить возможность изменять масштаб графика по Х и Y, возможность изменять параметры сигнала, возможность сохранить график в выбранном формате.

 

 

Аналитический обзор

Дискретное преобразование Фурье – инструмент спектрального анализа сигналов, широко применяемый в науке и технике. В основе лежит идея – почти любую периодическую функцию можно представить суммой отдельных гармонических составляющих.

Чтобы получить график модуля дискретного преобразования Фурье, для начала нужно применить дискретизацию к начальному сигналу. Далее каждый k -ый компонент ДПФ вычисляется по формуле: , где i – мнимая единица, Xn – n-ый член дискр. сигнала, N – количество точек дискр., k – номер компонента ДПФ, n – номер компонента сигнала, Xk – k-ый компонент ДПФ.

Для более удобного расчёта выражение можно преобразовать к виду:

В результате получается комплексное число и для построения графика вычисляется его модуль:

 

Разработка пользовательского интерфейса

Рис.1 – пользовательский интерфейс

На рис.1 изображён пользовательский интерфейс, который представляет собой окно, в левой части которого расположены поля ввода и элементы управления, в правой части находятся графики (верхний отображает входной сигнал, нижний – график модуля ДПФ).
В полях ввода пользователь задаёт характеристики сигнала (амплитуда, принимающая любые значения, реальная частота, принимающая значения [0..5) МГц, фаза, принимающая значения [0..2*π] рад) и количество точек, принимающее значения [100..1000].
Элементы управления представляют собой кнопки «Построить», «Сохранить» и «Выйти», а также ползунки для управления масштабом графиков по осям X и Y.
По нажатию кнопки «Построить» программа построит графики по введённым характеристикам, по нажатию кнопки «Сохранить» программа сохранит график модуля ДПФ в выбранном формате, по нажатию кнопки «Выйти» программа завершит свою работу (аналогично с крестиком в правом верхнем углу), ползунки, подписанные как «X» и «Y» отвечают за изменение масштаба графиков соответственно по осям Х и Y.

Диаграмма потока данных

Рис.2 – диаграмма потока данных

(рисовал на сайте https://www.draw.io/, затем заскринил и вставил сюда (как делал в прошлом семестре)).

 

 

Диаграмма классов

Рис.3 – диаграмма классов

(рисовал на сайте https://www.draw.io/, затем заскринил и вставил сюда (как делал в прошлом семестре))

 

Выбор и обоснование переменных

KursDlg

Для хранения начальных параметров сигнала используются переменные f[1..10], c[1..10], a[1..10] – типа float, т.к. они могут принимать значения с плавающей точкой, N – количество точек графика – типа int, т.к. оно принимает целые значения от 100 до 1000, g необходима для того, чтобы сохранять значение, полученное из окна ввода, поэтому она имеет формат CString, err типа CString служит для записи некорректно введённых элементов в список ошибочных переменных, двумерный массив pointsb типа double служит для хранения точек входного сигнала, поэтому может содержать числа с плавающей точкой, массив fr типа double служит для хранения точек модулей ДПФ сигнала, поэтому может содержать числа с плавающей точкой, xx и xy служат для хранения текущего положения ползунков, а xxx и yyy нужны для хранения предыдущих значений ползунков, они могут принимать только целочисленные значения, поэтому используется тип int, переменная типа CString служит для хранения значения ползунка в строковом виде для вывода на экран, sliderx и slidery служат для определения максимального и минимального значений слайдеров и используют тип CSliderCtrl, textx и texty служат для записи значения в текстовое поле и используют тип CStatic.

MyPaint1 и MyPaint2

rc служит для получения координат окна и имеет тип CRect, rgn типа CRgn служит для инкапсуляции области графика, pR и pR2 служат для хранения параметров пера и поэтому имеют тип CPen, i является счетчиком массива, поэтому используется тип int, y служит для хранения координаты y входного сигнала и дальнейшего сравнения, поэтому имеет тип double.

SaveIm

image служит для хранения растрового изображения, поэтому используется тип CImage, rect типа CRect служит для получения размеров окна, bStat требуется для проверки корректности выполнения сохранения и может принимать только два значения, поэтому подойдет тип BOOL, hr служит для хранения ошибки, поэтому подойдет универсальный тип программного интерфейса с объектами в Windows HRESULT, nBPP служит для определения числа смежных цветных битов для каждого пикселя, умноженных на число цветовых плоскостей и использует тип int.

Код программы

KursDlg

#include "stdafx.h" // подключение стандартной библиотеки stdafx

#include "kurs.h" // подключение созданной библиотеки kurs

#include "kursDlg.h" // подключение созданной библиотеки kursDlg

#include "SaveIm.h"// подключение созданной библиотеки SaveIm

#include "afxdialogex.h" // подключение стандартной библиотеки CDialogEx

#include <math.h> // подключение стандартной библиотеки для выполнения математических операций

#include <vector> // подключение файла заголовков vector

void CkursDlg::OnBnClickedButtvv() // функция построения, срабатывающая при нажатии кнопки "Построить"

{

bool flag = false; // создание флага для проверки ввода хотя бы одних a[i] и f[i]

bool flag1 = false; // создание флага для проверки того, что c[i] и f[i] не могут быть одновременно равными нулю

bool flag2 = false; // создание флага для проверки отличия от нуля хотя бы одного a[i]

// эти флаги нужны, чтобы программа не пыталась построить график нуля либо график синуса нуля, я просто не знаю как это русским языком написать,

// чтобы было понятно всем, ну надеюсь Вы поняли, если что, могу на словах объяснить

CString str; // создание переменной для записи значений из полей записи

CString error; // создание переменной для записи ошибок

// далее для 31 переменной идёт считывание значений из окон переменных и перевод их из строкового типа в тип float

// я опишу только a[0], потому что со всеми переменными там всё аналогично

Ea1.GetWindowTextW(str); // считать значение из окна Ea1 (переменная a1)

a[0] = _wtof(str); // перевести значение из строкового типа в тип float

Ea2.GetWindowTextW(str); // и т.д.

a[1] = _wtof(str);

Ea3.GetWindowTextW(str);

a[2] = _wtof(str);

Ea4.GetWindowTextW(str);

a[3] = _wtof(str);

Ea5.GetWindowTextW(str);

a[4] = _wtof(str);

Ea6.GetWindowTextW(str);

a[5] = _wtof(str);

Ea7.GetWindowTextW(str);

a[6] = _wtof(str);

Ea8.GetWindowTextW(str);

a[7] = _wtof(str);

Ea9.GetWindowTextW(str);

a[8] = _wtof(str);

Ea10.GetWindowTextW(str);

a[9] = _wtof(str);

Ec1.GetWindowTextW(str);

c[0] = _wtof(str);

Ec2.GetWindowTextW(str);

c[1] = _wtof(str);

Ec3.GetWindowTextW(str);

c[2] = _wtof(str);

Ec4.GetWindowTextW(str);

c[3] = _wtof(str);

Ec5.GetWindowTextW(str);

c[4] = _wtof(str);

Ec6.GetWindowTextW(str);

c[5] = _wtof(str);

Ec7.GetWindowTextW(str);

c[6] = _wtof(str);

Ec8.GetWindowTextW(str);

c[7] = _wtof(str);

Ec9.GetWindowTextW(str);

c[8] = _wtof(str);

Ec10.GetWindowTextW(str);

c[9] = _wtof(str);

Ef1.GetWindowTextW(str);

f[0] = _wtof(str);

Ef2.GetWindowTextW(str);

f[1] = _wtof(str);

Ef3.GetWindowTextW(str);

f[2] = _wtof(str);

Ef4.GetWindowTextW(str);

f[3] = _wtof(str);

Ef5.GetWindowTextW(str);

f[4] = _wtof(str);

Ef6.GetWindowTextW(str);

f[5] = _wtof(str);

Ef7.GetWindowTextW(str);

f[6] = _wtof(str);

Ef8.GetWindowTextW(str);

f[7] = _wtof(str);

Ef9.GetWindowTextW(str);

f[8] = _wtof(str);

Ef10.GetWindowTextW(str);

f[9] = _wtof(str);

EN.GetWindowTextW(str);

N = _wtof(str);

if (!((N >= 100) && (N <= 1000))) // если N меньше 100 или больше 1000

{

error += "N "; // записать N в список ошибок

}

if (!(f[0] >= 0 && f[0] < 5)) // если f1 меньше 0 или больше либо равно 5

{

error += "f1 "; // записать f1 в список ошибок

}

if (!(f[1] >= 0 && f[1] < 5)) // аналогично f1

{

error += "f2 "; // аналогично f1

}

if (!(f[2] >= 0 && f[2] < 5)) // аналогично f1

{

error += "f3 "; // аналогично f1

}

if (!(f[3] >= 0 && f[3] < 5)) // аналогично f1

{

error += "f4 "; // аналогично f1

}

if (!(f[4] >= 0 && f[4] < 5)) // аналогично f1

{

error += "f5 "; // аналогично f1

}

if (!(f[5] >= 0 && f[5] < 5)) // аналогично f1

{

error += "f6 "; // аналогично f1

}

if (!(f[6] >= 0 && f[6] < 5)) // аналогично f1

{

error += "f7 "; // аналогично f1

}

if (!(f[7] >= 0 && f[7] < 5)) // аналогично f1

{

error += "f8 "; // аналогично f1

}

if (!(f[8] >= 0 && f[8] < 5)) // аналогично f1

{

error += "f9 "; // аналогично f1

}

if (!(f[9] >= 0 && f[9] < 5)) // аналогично f1

{

error += "f10 "; // аналогично f1

}

if (!(c[0] >= 0 && c[0] <= 2 * 3.14)) // если c1 меньше 0 или больше 2*пи

{

error += "c1 "; // записать c1 в список ошибок

}

if (!(c[1] >= 0 && c[1] <= 2 * 3.14)) // аналогично c1

{

error += "c2 "; // аналогично c1

}

if (!(c[2] >= 0 && c[2] <= 2 * 3.14)) // аналогично c1

{

error += "c3 "; // аналогично c1

}

if (!(c[3] >= 0 && c[3] <= 2 * 3.14)) // аналогично c1

{

error += "c4 "; // аналогично c1

}

if (!(c[4] >= 0 && c[4] <= 2 * 3.14)) // аналогично c1

{

error += "c5 "; // аналогично c1

}

if (!(c[5] >= 0 && c[5] <= 2 * 3.14)) // аналогично c1

{

error += "c6 "; // аналогично c1

}

if (!(c[6] >= 0 && c[6] <= 2 * 3.14)) // аналогично c1

{

error += "c7 "; // аналогично c1

}

if (!(c[7] >= 0 && c[7] <= 2 * 3.14)) // аналогично c1

{

error += "c8 "; // аналогично c1

}

if (!(c[8] >= 0 && c[8] <= 2 * 3.14)) // аналогично c1

{

error += "c9 "; // аналогично c1

}

if (!(c[9] >= 0 && c[9] <= 2 * 3.14)) // аналогично c1

{

error += "c10 "; // аналогично c1

}

for (int i = 0; i <= 9; i++) // цикл для проверки ввода a и f (см. выше комментарий к переменной flag)

{

if (a[i]!= 0) // если a не равен нулю

{

flag = true; // присвоить flag значение true

}

if (f[i]!= 0) // если f не равен нулю

{

flag = true; // присвоить flag значение true

}

}

for (int i = 0; i <= 9; i++) // цикл для проверки отличия от нуля f и c (см. выше комментарий к переменной flag1)

{

if ((f[i]!= 0) || (c[i]!= 0)) // если f не равен нулю или c не равен нулю

{

flag1 = true; // присвоить flag1 значение true

}

}

for (int i = 0; i <= 9; i++) // цикл для проверки отличия от нуля a (см.выше комментарий к переменной flag2)

{

if (a[i]!= 0) // если a не равен нулю

{

flag2 = true; // присвоить flag2 значение true

}

}

if (flag2 == false) // если flag2 имеет значение false

{

AfxMessageBox(L"хотя бы один a должен быть отличен от нуля"); // вывести сообщение об ошибке

}

else if (flag1 == false) // если flag1 имеет значение false

{

AfxMessageBox(L"c[i] и f[i] не могут быть одновременно равными нулю"); // вывести сообщение об ошибке

}

else if (error!= "") // если в списке ошибок имеются переменные (error не пустой)

{

AfxMessageBox(L"Неправильный ввод данных: " + error); // вывести сообщение об ошибке и добавить к нему список некорректно введённых переменных

}

else if (flag == false) // если flag имеет значение false

{

AfxMessageBox(L"Введите хотя бы одно a и одно f"); // вывести сообщение об ошибке

}

else // иначе

{

int N2 = N; // создание переменной, в которой хранится количество точек N

double df = 1; // создание переменной, в которой хранится частота дискретизации

df = df / 10; // определение частоты дискретизации

double y = 0; // создание переменной, в которой хранится текущее значения входного сигнала

int i = 0; // создание переменной, в которой хранится номер текущей точки входного сигнала

Ndf = (N2*df); // создание и определение переменной

 

long double fcos[10000], fsin[10000]; // создание массивов для хранения вещественных и комплексных точек

for (int i = 0; i < 10000; i++) // цикл обнуления массивов

{

fcos[i] = 0;

fsin[i] = 0;

}

for (double n = 0; n < Ndf; n += df) // цикл вычисления входного сигнала

{

for (int ii = 0; ii <= 9; ii++) // цикл вычисления значения входного сигнала

{

y = y + a[ii] * sin(2 *M_PI*f[ii] * n + c[ii]); // формула входного сигнала

}

pointsb[0][i] = n; // запись значения по х

pointsb[1][i] = y; // запись значения по у

y = 0; // обнуление формулы

i++; // увеличение счётчика

}

for (int k = 0; k < N2 - 1; k++) // / цикл вычисления модуля ДПФ

{

for (int n = 0; n < N2 - 1; n++) // цикл вычисления вещественной и комплексной частей

{

double nn = -(2 *M_PI*k*n);

nn = nn / N2;

fcos[k] = fcos[k] + pointsb[1][n] * cos(nn); // вычисление вещественной части

fsin[k] = fsin[k] + pointsb[1][n] * sin(nn); // вычисление комплексной части

}

fcos[k] = fcos[k] * fcos[k]; // вычисление квадрата вещественной части

fsin[k] = fsin[k] * fsin[k]; // вычисление квадрата комплексной части

fr[0][k] = k; // запись значение по x

fr[1][k] = sqrt(fcos[k] + fsin[k]); // вычисление и запись значения по y

}

CWnd *pP = GetDlgItem(IDC_GRAFIC1); // извлечение дескриптора диалогового окна

if (pP) // если дескриптор не равен нулю

pP->Invalidate(); // отправить сообщение рисования элементу

CWnd *pP2 = GetDlgItem(IDC_GRAFIC2); // извлечение дескриптора диалогового окна

if (pP2) // если дескриптор не равен нулю

pP2->Invalidate(); // отправить сообщение рисования элементу

}

}

 

 

void CkursDlg::OnBnClickedbuttonss() // функция сохранения, срабатывающая при нажатии кнопки "Сохранить"

{

CFileDialog fd(FALSE, _T("."), NULL,

OFN_HIDEREADONLY | OFN_FILEMUSTEXIST, _T("JPG Files(*.jpg)|*.jpg|(*.png)|*.png|(*.bmp)|*.bmp||"), this);

// создание переменной для хранения параметров окна сохранения

if (IDOK == fd.DoModal()) // если нажата кнопка ОК

{ // сохранение изображения

std::wstring strPath = fd.m_pOFN->lpstrFile; // создание переменной, хранящую указатель на буфер, в котором находится имя для инициализации поля "Имя файла" диалога

CWnd *pWn = GetDlgItem(IDC_GRAFIC2); // извлечение дескриптора диалогового окна

SaveIm(strPath.c_str(), pWn); // функция SaveIm

}

}

// создание переменных для хранения предыдущего значения слайдера

int xxx = 0;

int yyy = 0;

void CkursDlg::OnNMCustomdrawSlider1(NMHDR *pNMHDR, LRESULT *pResult) // функция слайдера оси х

{

LPNMCUSTOMDRAW pNMCD = reinterpret_cast<LPNMCUSTOMDRAW>(pNMHDR); // преобразование параметра pNMHDR к требуемому типу LPNMCUSTOMDRAW

sliderx.SetRangeMax(10); // определение максимального значения слайдера

sliderx.SetRangeMin(1); // определение минимального значения слайдера

xx = sliderx.GetPos(); // получение текущего значения слайдера

if (xxx - xx!= 0) // если текущее значение отличается от предыдущего

{

xxx = xx; // запомнить текущее значение

CString score; // создание переменной для хранения значения слайдера с строковом формате

score.Format(_T("%d"), xx); // перевести значение слайдера в строковый формат

textx.SetWindowText(score); // записать значение в текстовое поле

}

CWnd *pP = GetDlgItem(IDC_GRAFIC1); // извлечение дескриптора диалогового окна

if (pP) // если дескриптор не равен нулю

pP->Invalidate(); // отправить сообщение рисования элементу

CWnd *pP2 = GetDlgItem(IDC_GRAFIC2); // извлечение дескриптора диалогового окна

if (pP2) // если дескриптор не равен нулю

pP2->Invalidate(); // отправить сообщение рисования элементу

 

*pResult = 0;

}

void CkursDlg::OnNMCustomdrawSlider2(NMHDR *pNMHDR, LRESULT *pResult) // функция слайдера оси х

{

LPNMCUSTOMDRAW pNMCD = reinterpret_cast<LPNMCUSTOMDRAW>(pNMHDR);// преобразование параметра pNMHDR к требуемому типу LPNMCUSTOMDRAW

slidery.SetRangeMax(10); // определение максимального значения слайдера

slidery.SetRangeMin(1); // определение минимального значения слайдера

xy = slidery.GetPos(); // получение текущего значения слайдера

if (yyy - xy!= 0) // если текущее значение отличается от предыдущего

{

yyy = xy; // запомнить текущее значение

CString score; // создание переменной для хранения значения слайдера с строковом формате

score.Format(_T("%d"), xy); // перевести значение слайдера в строковый формат

texty.SetWindowText(score); // записать значение в текстовое поле

}

CWnd *pP = GetDlgItem(IDC_GRAFIC1); // извлечение дескриптора диалогового окна

if (pP) // если дескриптор не равен нулю

pP->Invalidate(); // отправить сообщение рисования элементу

CWnd *pP2 = GetDlgItem(IDC_GRAFIC2); // извлечение дескриптора диалогового окна

if (pP2) // если дескриптор не равен нулю

pP2->Invalidate(); // отправить сообщение рисования элементу

 

*pResult = 0;

}

 

 

MyPaint1

#include "stdafx.h" // подключение стандартной библиотеки stdafx

#include "MyPaint1.h" // подключение созданной библиотеки MyPaint1

#include "kursDlg.h" // подключение созданной библиотеки kursDlg

#include <vector> // подключение файла заголовков vector

#include "kurs.h" // подключение созданной библиотеки kurs

 

IMPLEMENT_DYNAMIC(CMyPaint1, CDialogEx)

CMyPaint1::CMyPaint1(CWnd* pParent)

: CDialogEx(IDD_KURS_DIALOG, pParent)

{

 

}

CMyPaint1::~CMyPaint1()

{

}

void CMyPaint1::DoDataExchange(CDataExchange* pDX)

{

CDialogEx::DoDataExchange(pDX);

}

BEGIN_MESSAGE_MAP(CMyPaint1, CDialogEx)

ON_WM_PAINT()

END_MESSAGE_MAP()

void CMyPaint1::OnPaint() // функция рисования

{

CPaintDC dc(this);

CRect rc; // создание объекта класса CRect

GetClientRect(&rc); // получение координат рабочей области окна

CRgn rgn; // создание объекта класса CRgn

rgn.CreateRectRgn(rc.left, rc.top, rc.right, rc.bottom); // определение и сохранение границ области рисования

dc.FillSolidRect(rc, RGB(255, 255, 255)); // заполнение области рисования белым цветом

CPen pR; // создание переменной для хранения параметров пера

pR.CreatePen(PS_SOLID, 2, RGB(0, 0, 0)); // определение пера толщины 2 пикселя и черного цвета

HGDIOBJ pOld = dc.SelectObject(pR); // выбор пера

// рисование координатных осей х и у

dc.MoveTo(10, rc.top + 10);

dc.LineTo(7, rc.top + 25);

dc.MoveTo(10, rc.top + 10);

dc.LineTo(13, rc.top + 25);

dc.MoveTo(10, rc.top + 10);

dc.LineTo(10, rc.bottom - 10);

dc.MoveTo(10, (rc.bottom - 10) / 2);

dc.LineTo(rc.right - 10, (rc.bottom - 10) / 2);

dc.LineTo(rc.right - 25, (rc.bottom - 10) / 2 - 3);

dc.MoveTo(rc.right - 10, (rc.bottom - 10) / 2);

dc.LineTo(rc.right - 25, (rc.bottom - 10) / 2 + 3);

CPen pR2; // создание переменной для хранения параметров второго пера

pR2.CreatePen(PS_SOLID, 2, RGB(0, 0, 225)); // определение параметров нового пера толщиной 2 и синего цвета

pOld = dc.SelectObject(pR2); // выбор второго пера

CkursDlg *pD = (CkursDlg *)AfxGetMainWnd(); // создание переменной, в которой хранится дескриптор окна

if (!pD) // если дескриптор равен нулю

return; // завершить выполнение цикла

dc.MoveTo(((pD->pointsb[0][0])*pD->xx) + 10, (rc.bottom - 10) / 2); // перемещение в начало координат

int i = 0; // создание и определение переменной для хранения номера координаты первоначального сигнала

double y; // создание переменной для хранения текущего значения первоначального сигнала

while ((i < (pD->N) - 1) && (i*pD->xx < (rc.right - 10))) // пока массив точек сигнала не закончился или пока не вышли за правую границу рисунка

{

y = (rc.bottom - 10) / 2 - pD->pointsb[1][i] * (pD->xy) * 10; // определение у координаты с учетом смещения до середины рисунка

if ((y > (rc.top + 10)) && (y < (rc.bottom - 10))) // если точка не вышла за верхнюю или нижнюю границу

{

dc.LineTo((pD->pointsb[0][i] * 10)*pD->xx + 10, ((rc.bottom - 10) / 2 - pD->pointsb[1][i] * (pD->xy) * 10)); // нарисовать линию до нее

}

else if (y > (rc.bottom - 10)) // иначе если точка ниже нижней границы

{

dc.LineTo((pD->pointsb[0][i] * 10)*pD->xx + 10, (rc.bottom - 10)); // нарисовать линию до низа рисунка - 10

}

else // иначе

{

dc.LineTo((pD->pointsb[0][i] * 10)*pD->xx + 10, (rc.top + 10)); // нарисовать линию до верхней границы + 10

}

i++; // увеличение счётчика

}

dc.SelectObject(pOld);

}

 

 

MyPaint2

#include "stdafx.h" // подключение стандартной библиотеки stdafx

#include "MyPaint2.h" // подключение созданной библиотеки MyPaint2

#include "kursDlg.h" // подключение созданной библиотеки kursDlg

#include <vector> // подключение файла заголовков vector

#include "kurs.h" // подключение созданной библиотеки kurs

 

IMPLEMENT_DYNAMIC(CMyPaint2, CDialogEx)

CMyPaint2::CMyPaint2(CWnd* pParent)

: CDialogEx(IDD_KURS_DIALOG, pParent)

{

 

}

CMyPaint2::~CMyPaint2()

{

}

void CMyPaint2::DoDataExchange(CDataExchange* pDX)

{

CDialogEx::DoDataExchange(pDX);

}

BEGIN_MESSAGE_MAP(CMyPaint2, CDialogEx)

ON_WM_PAINT()

END_MESSAGE_MAP()

void CMyPaint2::OnPaint() // функция рисования

{

CPaintDC dc(this);

CRect rc; // создание объекта класса CRect

GetClientRect(&rc); // получение координат рабочей области окна

CRgn rgn; // создание объекта класса CRgn

rgn.CreateRectRgn(rc.left, rc.top, rc.right, rc.bottom); // определение и сохранение границ области рисования

dc.FillSolidRect(rc, RGB(255, 255, 255)); // заполнение области рисования белым цветом

CPen pR; // создание переменной для хранения параметров пера

pR.CreatePen(PS_SOLID, 2, RGB(0, 0, 0)); // определение пера толщины 2 пикселя и черного цвета

HGDIOBJ pOld = dc.SelectObject(pR); // выбор пера

// рисование координатных осей х и у

dc.MoveTo(10, rc.top + 10);

dc.LineTo(7, rc.top + 25);

dc.MoveTo(10, rc.top + 10);

dc.LineTo(13, rc.top + 25);

dc.MoveTo(10, rc.top + 10);

dc.LineTo(10, rc.bottom - 10);

dc.LineTo(rc.right - 10, rc.bottom - 10);

dc.LineTo(rc.right - 25, rc.bottom - 13);

dc.MoveTo(rc.right - 10, rc.bottom - 10);

dc.LineTo(rc.right - 25, rc.bottom - 7);

CPen pR2; // создание переменной для хранения параметров второго пера

pR2.CreatePen(PS_SOLID, 2, RGB(0, 0, 225)); // определение параметров нового пера толщиной 2 и синего цвета

pOld = dc.SelectObject(pR2); // выбор второго пера

CkursDlg *pD = (CkursDlg *)AfxGetMainWnd(); // создание переменной, в которой хранится дескриптор окна

if (!pD) // если дескриптор равен нулю

return; // завершить выполнение цикла

dc.MoveTo(10, rc.bottom - 10); // перемемещение в начало координат

int i = 0; // создание и определение переменной для хранения номера координаты первоначального сигнала

double y; // создание переменной для хранения текущего значения первоначального сигнала

while ((i < (pD->N) - 1) && ((pD->fr[0][i])*pD->xx + 10 < (rc.right - 10))) // пока массив точек сигнала не закончился или пока не вышли за правую границу рисунка

{

y = (rc.bottom - 10 + (-pD->fr[1][i])*(pD->xy)); // определение у координаты с учетом смещения до низа рисунка

if ((y > (rc.top + 10)) && (y < (rc.bottom - 10))) // если точка не вышла за верхнюю или нижнюю границу

{

// нарисовать линию от этой точки до оси х (столбик)

dc.MoveTo((pD->fr[0][i])*pD->xx + 10, (rc.bottom - 10 + (-pD->fr[1][i])*(pD->xy)));

dc.LineTo((pD->fr[0][i])*pD->xx + 10, rc.bottom - 10);

}

else if (y > (rc.bottom - 10)) // иначе если точка ниже нижней границы

{

// нарисовать столбик от низа до оси x

dc.MoveTo((pD->fr[0][i])*pD->xx + 10, (rc.bottom - 10));

dc.LineTo((pD->fr[0][i])*pD->xx + 10, rc.bottom - 10);

}

else // иначе

{

// // нарисовать столб от рисунка - 10 до оси х

dc.MoveTo((pD->fr[0][i])*pD->xx + 10, (rc.top + 10));

dc.LineTo((pD->fr[0][i])*pD->xx + 10, rc.bottom - 10);

}

i++; // увеличение счётчика

}

dc.SelectObject(pOld);

}

SaveIm

#include "stdafx.h" // подключение стандартной библиотеки stdafx

#include <string> // подключение стандартной библиотеки для работы со строками

#include "SaveIm.h" // подключении созданной библиотеки SaveIm

#include <initguid.h> // подключение стандартной библиотеки для контроля GUID инициализации

// определение форматов

DEFINE_GUID(ImageFormatBMP, 0xb96b3cab, 0x0728, 0x11d3, 0x9d, 0x7b, 0x00, 0x00, 0xf8, 0x1e, 0xf3, 0x2e);

DEFINE_GUID(ImageFormatEMF, 0xb96b3cac, 0x0728, 0x11d3, 0x9d, 0x7b, 0x00, 0x00, 0xf8, 0x1e, 0xf3, 0x2e);

DEFINE_GUID(ImageFormatWMF, 0xb96b3cad, 0x0728, 0x11d3, 0x9d, 0x7b, 0x00, 0x00, 0xf8, 0x1e, 0xf3, 0x2e);

DEFINE_GUID(ImageFormatJPEG, 0xb96b3cae, 0x0728, 0x11d3, 0x9d, 0x7b, 0x00, 0x00, 0xf8, 0x1e, 0xf3, 0x2e);

DEFINE_GUID(ImageFormatPNG, 0xb96b3caf, 0x0728, 0x11d3, 0x9d, 0x7b, 0x00, 0x00, 0xf8, 0x1e, 0xf3, 0x2e);

DEFINE_GUID(ImageFormatGIF, 0xb96b3cb0, 0x0728, 0x11d3, 0x9d, 0x7b, 0x00, 0x00, 0xf8, 0x1e, 0xf3, 0x2e);

DEFINE_GUID(ImageFormatTIFF, 0xb96b3cb1, 0x0728, 0x11d3, 0x9d, 0x7b, 0x00, 0x00, 0xf8, 0x1e, 0xf3, 0x2e);

bool SaveIm(LPCTSTR path, CWnd *pWnd) // функция сохранения рисунка

{

CImage image; // создание переменной типа CImage

CRect rect; // создание переменной типа CRect

BOOL bStat;// создание переменной типа BOOL

ASSERT(pWnd); // если pWnd равен нулю, то вывести сообщение об ошибке

if (pWnd == NULL) // если pWnd равен нулю, то

return FALSE; // завершить функцию

CWindowDC winDC(pWnd); // создание объекта, который обращается к целой части экрана

pWnd->GetWindowRect(&rect); // получить размеры рамки ограничивающей прямоугольник определяемого окна

int nBPP = winDC.GetDeviceCaps(BITSPIXEL) * winDC.GetDeviceCaps(PLANES); // создание переменной, равной числу смежных цветных битов для каждого пикселя умноженных на число цветовых плоскостей

if (nBPP < 24) nBPP = 24; // если переменная меньше 24, сделать ее равной 24

bStat = image.Create(rect.Width(), rect.Height(), nBPP); // создание изображения

ASSERT(bStat); // если изображение не удалось создать, вывести сообщение об ошибке

if (!bStat) // если bStat равен нулю

return FALSE; // завершить цикл

CImageDC imageDC(image); // создание переменной типа CImageDC содержащую перобразованную переменную image

::BitBlt(imageDC, 0, 0, rect.Width(), rect.Height(), winDC, 0, 0, SRCCOPY); // функция BitBlt выполняет передачу битовых блоков данных о цвете, соответствующих прямоугольнику пикселей из заданного исходного контекста устройства в целевой контекст устройства

HRESULT hr; // создание переменной для хранения результата сохранения

std::wstring strFull = path; // создание строковой переменной, содержащей имя файла

if (std::string::npos!= strFull.find(_T(".png"))) // если имя файла содержит ".png"

hr = image.Save(path, ImageFormatPNG); // сохранить в формате png

else if (std::string::npos!= strFull.find(_T(".jpg"))) // если имя файла содержит ".jpg"

hr = image.Save(path, ImageFormatJPEG); // сохранить в формате jpg

else if (std::string::npos!= strFull.find(_T(".gif"))) // если имя файла содержит ".gif"

hr = image.Save(path, ImageFormatGIF); // сохранить в формате gif

else if (std::string::npos!= strFull.find(_T(".bmp"))) // если имя файла содержит ".bmp"

hr = image.Save(path, ImageFormatBMP); // сохранить в формате bmp

else // иначе

{

strFull += _T(".bmp"); // добавить в строку ".bmp"

hr = image.Save(path, ImageFormatBMP); // сохранить в формате ".bmp"

}

 

if (FAILED(hr)) // если сохранить не удалось

{

TRACE(" Couldn't Save File: %s, %x ", (LPCTSTR)path, hr); // вывести сообщение об ошибке

return FALSE; // завершить цикл

}

return false; // вернуть false

}

 

 


 

Файлы заголовков

Kurs.h

// kurs.h: главный файл заголовка для приложения PROJECT_NAME

//

 

#pragma once

 

#ifndef __AFXWIN_H__

#error "включить stdafx.h до включения этого файла в PCH"

#endif

 

#include "resource.h" // основные символы

 

 

// CkursApp:

// О реализации данного класса см. kurs.cpp

//

 

class CkursApp: public CWinApp

{

public:

CkursApp();

 

// Переопределение

public:

virtual BOOL InitInstance();

 

// Реализация

 

DECLARE_MESSAGE_MAP()

};

 

extern CkursApp theApp;

KursDlg.h

// kursDlg.h: файл заголовка

//

 

#pragma once

#include "MyPaint1.h"

#include "MyPaint2.h"

#include "afxwin.h"

#include <math.h>

#include <vector>

#include "afxcmn.h"

#define _USE_MATH_DEFINES

 

 

// диалоговое окно CkursDlg

class CkursDlg: public CDialogEx

{

// Создание

public:

CkursDlg(CWnd* pParent = nullptr); // стандартный конструктор

 

// Данные диалогового окна

#ifdef AFX_DESIGN_TIME

enum { IDD = IDD_KURS_DIALOG };

#endif

 

protected:

virtual void DoDataExchange(CDataExchange* pDX); // поддержка DDX/DDV

 

 

// Реализация

protected:

HICON m_hIcon;

 

// Созданные функции схемы сообщений

virtual BOOL OnInitDialog();

afx_msg void OnSysCommand(UINT nID, LPARAM lParam);

afx_msg void OnPaint();

afx_msg HCURSOR OnQueryDragIcon();

DECLARE_MESSAGE_MAP()

public:

afx_msg void OnEnChangeEN();

CMyPaint1 m_painter1;

CMyPaint2 m_painter2;

afx_msg void OnBnClickedButtvv();

CEdit EN;

CEdit Ea1;

CEdit Ea2;

CEdit Ea3;

CEdit Ea4;

CEdit Ea5;

CEdit Ea6;

CEdit Ea7;

CEdit Ea8;

CEdit Ea9;

CEdit Ea10;

CEdit Ec1;

CEdit Ec2;

CEdit Ec3;

CEdit Ec4;

CEdit Ec5;

CEdit Ec6;

CEdit Ec7;

CEdit Ec8;

CEdit Ec9;

CEdit Ec10;

CEdit Ef1;

CEdit Ef2;

CEdit Ef3;

CEdit Ef4;

CEdit Ef5;

CEdit Ef6;

CEdit Ef7;

CEdit Ef8;

CEdit Ef9;

CEdit Ef10;

float a[10];

float f[10];

float c[10];

float N;

float Ndf;

double pointsb[2][10000];

double fr[2][10000];

int xx;

int xy;

afx_msg void OnBnClickedbuttonss();

afx_msg void OnNMCustomdrawSlider1(NMHDR *pNMHDR, LRESULT *pResult);

CSliderCtrl sliderx;

CSliderCtrl slidery;

afx_msg void OnNMCustomdrawSlider2(NMHDR *pNMHDR, LRESULT *pResult);

CStatic textx;

CStatic texty;

};

MyPaint1.h

#pragma once

// диалоговое окно CMyPaint1

class CMyPaint1: public CDialogEx

{

DECLARE_DYNAMIC(CMyPaint1)

 

public:

CMyPaint1(CWnd* pParent = NULL); // стандартный конструктор

virtual ~CMyPaint1();

 

// Данные диалогового окна

#ifdef AFX_DESIGN_TIME

enum { IDD = IDD_KURS_DIALOG };

#endif

 

protected:

virtual void DoDataExchange(CDataExchange* pDX); // поддержка DDX/DDV

 

DECLARE_MESSAGE_MAP()

public:

afx_msg void OnPaint();

};

MyPaint2.h

#pragma once

// диалоговое окно CMyPaint2

class CMyPaint2: public CDialogEx

{

DECLARE_DYNAMIC(CMyPaint2)

 

public:

CMyPaint2(CWnd* pParent = NULL); // стандартный конструктор

virtual ~CMyPaint2();

 

// Данные диалогового окна

#ifdef AFX_DESIGN_TIME

enum { IDD = IDD_KURS_DIALOG };

#endif

 

protected:

virtual void DoDataExchange(CDataExchange* pDX); // поддержка DDX/DDV

 

DECLARE_MESSAGE_MAP()

public:

afx_msg void OnPaint();

};

SaveIm.h

#pragma once

bool SaveIm(LPCTSTR path, CWnd *pWnd);

 

 

Пример работы

После того, как пользователь открыл программу kurs.exe, а затем ввёл данные входного сигнала и нажал на кнопку “Построить”, программа построила график входного сигнала и модуля его ДПФ (рис.4):

Рис.4 – простейший пример работы программы

Затем, когда пользователь изменил масштаб по x и y при помощи слайдеров, графики растянулись на указанные значения (рис.5):

Рис.5 – растягивание графиков

Когда пользователь нажал на кнопку “Сохранить”, появилось новое окно сохранения (рис.6):

Рис.6 – окно сохранения

После указания пользователем пути сохранения, имени файла и его типа, программа выполнила функцию сохранения (рис.7, 8):

Рис.7 – программа сохранила изображение графика Рис.8 – свойства сохранённого файла

В конце, когда пользователь нажал кнопку “Выйти”, программа завершила свою работу.

 

Контрольный пример

Для примера я взял значения a1 = 1, a6 = 2, c1 = 0.5, c6 = 0.6, f1 = 1, f6 = 4, количество точек N = 40, частота дискретизации 0.1 МГц:

Рис.9 – контрольный пример работы программы

 

Контрольный пример был составлен при помощи программы MathCad Prime 2.0 и встроенной функции CFFT

Данные входного сигнала (рис.10):

Рис.10 – данные входного сигнала в MathCad

Входной сигнал (рис.11):

Рис.11 – график входного сигнала в MathCad

 

График модуля ДПФ (рис.12):

Рис.12 – график модуля ДПФ в MathCad

 

Основываясь на том, что графики входных сигналов и графики модулей ДПФ совпадают, можно сделать вывод о правильности работы программы.

 

 

Инструкция пользователю

При запуске программы kurs.exe открывается окно, в левой части которого находятся поля ввода и элементы управления программой, а в правой – графики входного сигнала (верхний) и модуля его ДПФ (нижний).
В полях ввода пользователю необходимо задать данные входного сигнала: a[1..10] – амплитуда [м] - принимает любые значения, f[1..10] – реальная частота [МГц] - принимает значения от 0 включая до 5 не включая, c[1..10] – фаза [рад] – принимает значения от 0 включая до 2*π включая, N – количество точек - принимает значения от 100 включая до 1000 включая.

После того, как пользователь введёт начальные данные и нажмёт на кнопку “Построить”, программа построит графики.
Иначе, если пользователь ввёл данные некорректно, программа выдаст сообщение об ошибке.

Пользователь может масштабировать графики по x и y при помощи слайдеров, которые подписаны x и y соответственно.

После нажатия кнопки “Сохранить” откроется новое окно, в котором пользователь может указать путь сохранения, имя файла и его тип, а затем сохранить изображение графика.
При вводе недопустимых символов в поле ввода имени файла программа выдаст сообщение об ошибке.
После нажатия кнопки “Выйти” программа завершит свою работу.

 

Вывод

В ходе выполнения курсовой работы были приобретены навыки работы в среде разработки оконных приложений MFC, был изучен метод дискретного преобразования Фурье – его история, значение и т.д.

Результатом работы является оконное приложение kurs.exe, имеющее размер 296 кб, которое способно вычислять ДПФ для сигнала заданного вида, затем рисовать графики входного сигнала и модуля его ДПФ, масштабировать эти графики, а так же сохранять изображение графика с заданным именем и в заданном формате.

Правильность выполнения работы была проверена при помощи эталона – программы MathCad Prime 2.0 и встроенной функции CFFT. Контрольный пример в MathCad и в программе совпадает, следовательно, можно сделать вывод о том, что работа была выполнена корректно.

 



Поделиться:




Поиск по сайту

©2015-2024 poisk-ru.ru
Все права принадлежать их авторам. Данный сайт не претендует на авторства, а предоставляет бесплатное использование.
Дата создания страницы: 2019-12-19 Нарушение авторских прав и Нарушение персональных данных


Поиск по сайту: