/*
schwingungen
Copyright (C) 2003 Andreas Dangel <adabolo at adabolo.de>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include <wx/wx.h>
#include <math.h>
#include "schwingungen.h"
IMPLEMENT_APP(SchwingungenApp)
enum {
ID_Quit = wxID_HIGHEST,
ID_Generator,
ID_Punktiert,
ID_Parameter,
ID_Settings_OK,
ID_Settings_Cancel,
ID_VerPlus,
ID_VerMinus,
ID_VerNix,
ID_Skala,
ID_Save
};
bool SchwingungenApp::OnInit() {
frame = new wxFrame((wxFrame *) NULL, -1, "Schwingungen");
// Menü erstellen
wxMenuBar *menuBar = new wxMenuBar;
wxMenu *menuFile = new wxMenu;
wxMenu *menuSettings = new wxMenu;
menuFile->Append(ID_Save, "Abbild speichern...");
menuFile->AppendSeparator();
menuFile->Append(ID_Quit, "&Beenden");
generator = FALSE;
menuSettings->Append(ID_Generator, "Generator", "Generator einzeichnen", TRUE);
punktiert = FALSE;
menuSettings->Append(ID_Punktiert, "Punktiert", "Punkte nicht verbinden", TRUE);
skala = FALSE;
menuSettings->Append(ID_Skala, "Skala", "Skala ein- bzw. ausschalten", TRUE);
menuSettings->AppendSeparator();
menuSettings->Append(ID_VerPlus, "Verstärkung höher", "Verstärkung erhöhen");
menuSettings->Append(ID_VerMinus, "Verstärkung niedriger", "Verstärkung verniedrigen");
menuSettings->Append(ID_VerNix, "Verstärkung aus", "Verstärkung ausschalten");
menuSettings->AppendSeparator();
menuSettings->Append(ID_Parameter, "Parameter", "Parameter einstellen");
menuBar->Append(menuFile, "&Datei");
menuBar->Append(menuSettings, "&Einstellungen");
frame->SetMenuBar(menuBar);
// data initialisieren
data.l = 5 * pow(10, -3); // 5 mH
data.c = 2 * pow(10, -6); // 2 µF
data.r = 5; // 5 Ohm
data.f = pow(10, 4) / (2 * M_PI); // ca. 1590 Hz
data.u_1_dach = 1; // 1 V
data.delta_t = 1 * pow(10, -5); // 0.00001 s = 0.01 ms
data.start_t = 0; // 0s
data.start_i = 0; // 0A
data.start_q = 0; // 0C
data.verstaerkung = 10;
frame->Show(TRUE);
SetTopWindow(frame);
Connect(frame->GetId(), wxEVT_PAINT, (wxObjectEventFunction) &SchwingungenApp::OnPaint);
Connect(ID_Quit, wxEVT_COMMAND_MENU_SELECTED, (wxObjectEventFunction) &SchwingungenApp::OnQuit);
Connect(ID_Generator, wxEVT_COMMAND_MENU_SELECTED, (wxObjectEventFunction) &SchwingungenApp::OnGenerator);
Connect(ID_Punktiert, wxEVT_COMMAND_MENU_SELECTED, (wxObjectEventFunction) &SchwingungenApp::OnPunktiert);
Connect(ID_Skala, wxEVT_COMMAND_MENU_SELECTED, (wxObjectEventFunction) &SchwingungenApp::OnSkala);
Connect(ID_Parameter, wxEVT_COMMAND_MENU_SELECTED, (wxObjectEventFunction) &SchwingungenApp::OnParameter);
Connect(ID_Settings_OK, wxEVT_COMMAND_BUTTON_CLICKED, (wxObjectEventFunction) &SchwingungenApp::OnSettingsOK);
Connect(ID_Settings_Cancel, wxEVT_COMMAND_BUTTON_CLICKED, (wxObjectEventFunction) &SchwingungenApp::OnSettingsCancel);
Connect(ID_VerPlus, wxEVT_COMMAND_MENU_SELECTED, (wxObjectEventFunction) &SchwingungenApp::OnVerPlus);
Connect(ID_VerMinus, wxEVT_COMMAND_MENU_SELECTED, (wxObjectEventFunction) &SchwingungenApp::OnVerMinus);
Connect(ID_VerNix, wxEVT_COMMAND_MENU_SELECTED, (wxObjectEventFunction) &SchwingungenApp::OnVerNix);
Connect(ID_Save, wxEVT_COMMAND_MENU_SELECTED, (wxObjectEventFunction) &SchwingungenApp::OnSave);
return true;
}
void SchwingungenApp::Draw(wxDC& dc) {
int width, height;
int x = 0;
frame->GetClientSize(&width, &height);
//wxPaintDC dc(frame);
dc.SetBackground(wxBrush(wxColour(255,255,255), wxSOLID));
dc.Clear();
dc.SetPen(wxPen(wxColour(0,0,0), 1, wxSOLID));
dc.BeginDrawing();
dc.DrawLine(0, height/2, width-1, height/2);
if (skala) {
int i;
// Teil 1: y-Achse
double y = 0;
// 10 Markierungen nach oben und 10 nach unten...
for (i = 1; i <= 10; i++) {
y = i * 50. / data.verstaerkung;
// positive Skala
dc.DrawLine(0, height/2 - y * data.verstaerkung, 5, height/2 - y * data.verstaerkung);
dc.DrawText(wxString::Format("%.2f V", y), 10, height/2 - y * data.verstaerkung - 9);
// negative Skala
dc.DrawLine(0, height/2 + y * data.verstaerkung, 5, height/2 + y * data.verstaerkung);
dc.DrawText(wxString::Format("-%.2f V", y), 10, height/2 + y * data.verstaerkung - 9);
}
// Teil 2: x-Achse
// alle 100 Pixel eine Markierung
for (i = 100; i <= width; i += 100) {
dc.DrawLine(i, height/2, i, height/2 + 5);
dc.DrawText(wxString::Format("%.2f ms", i * data.delta_t * pow(10, 3)), i - 20, height/2 + 5 + 2);
}
}
//dc.DrawText(wxString::Format("Verstärkung: %i", data.verstaerkung), 10, 10);
// Konstanten
double L = data.l;
double C = data.c;
double f = data.f;
double omega = 2 * M_PI * f;
double delta_t = data.delta_t;
int wiederholungen = width;
double U_1_dach = data.u_1_dach;
double R = data.r;
// Startwerte
double t = data.start_t;
double t_ende = t + wiederholungen * delta_t;
x = - int(t / delta_t);
t = 0.0;
double I = data.start_i;
double Q = data.start_q;
double U_1_alt = U_1_dach * sin(omega * t);
double U_C_alt = Q / C;
while (t <= t_ende) {
double U_1 = U_1_dach * sin(omega * t); // Generator
double U_C = Q / C; // Kondensator
double delta_I = (U_1/L - Q/(L*C) - R*I/L) * delta_t; // Änderung delta_I
I = I + delta_I; // neue Stromstärke
Q = Q + I * delta_t; // neue Ladung
t = t + delta_t; // neue Zeit
// Ausgabe
if (punktiert == FALSE) {
dc.DrawLine(x-1, height/2 - U_C_alt * data.verstaerkung, x, height/2 - U_C * data.verstaerkung);
} else {
dc.DrawPoint(x, height/2 - U_C * data.verstaerkung);
}
if (generator) {
dc.SetPen(wxPen(wxColour(255,0,0), 1, wxSOLID));
if (punktiert == FALSE) {
dc.DrawLine(x-1, height/2 - U_1_alt * data.verstaerkung, x, height/2 - U_1 * data.verstaerkung);
} else {
dc.DrawPoint(x, height/2 - U_1 * data.verstaerkung);
}
}
dc.SetPen(wxPen(wxColour(0,0,0), 1, wxSOLID));
x++;
U_C_alt = U_C;
U_1_alt = U_1;
}
dc.EndDrawing();
}
void SchwingungenApp::OnPaint(wxPaintEvent& event) {
wxPaintDC dc(frame);
Draw(dc);
}
void SchwingungenApp::OnQuit(wxCommandEvent& event) {
frame->Close(TRUE);
}
void SchwingungenApp::OnGenerator(wxCommandEvent& event) {
if (generator == FALSE) {
generator = TRUE;
} else {
generator = FALSE;
}
frame->Refresh();
}
void SchwingungenApp::OnPunktiert(wxCommandEvent& event) {
if (punktiert == FALSE) {
punktiert = TRUE;
} else {
punktiert = FALSE;
}
frame->Refresh();
}
void SchwingungenApp::OnParameter(wxCommandEvent& event) {
dialog = new wxDialog(frame, -1, wxString("Parameter"));
wxBoxSizer * topsizer = new wxBoxSizer(wxVERTICAL);
wxFlexGridSizer * grid = new wxFlexGridSizer(4); // 4 columns
wxBoxSizer * buttons = new wxBoxSizer(wxHORIZONTAL);
//Controls
wxTextCtrl *spule = new wxTextCtrl(dialog, -1);
spule->SetValue(wxString::Format("%f", data.l * pow(10, 3)));
wxTextCtrl *kondensator = new wxTextCtrl(dialog, -1);
kondensator->SetValue(wxString::Format("%f", data.c * pow(10, 6)));
wxTextCtrl *widerstand = new wxTextCtrl(dialog, -1);
widerstand->SetValue(wxString::Format("%f", data.r));
wxTextCtrl *frequenz = new wxTextCtrl(dialog, -1);
frequenz->SetValue(wxString::Format("%f", data.f));
wxTextCtrl *u_1_dach = new wxTextCtrl(dialog, -1);
u_1_dach->SetValue(wxString::Format("%f", data.u_1_dach));
wxTextCtrl *delta_t = new wxTextCtrl(dialog, -1);
delta_t->SetValue(wxString::Format("%f", data.delta_t * pow(10, 3)));
wxTextCtrl *verstaerker = new wxTextCtrl(dialog, -1);
verstaerker->SetValue(wxString::Format("%f", data.verstaerkung));
wxTextCtrl *start_t = new wxTextCtrl(dialog, -1);
start_t->SetValue(wxString::Format("%f", data.start_t));
wxTextCtrl *start_i = new wxTextCtrl(dialog, -1);
start_i->SetValue(wxString::Format("%f", data.start_i));
wxTextCtrl *start_q = new wxTextCtrl(dialog, -1);
start_q->SetValue(wxString::Format("%f", data.start_q));
grid->Add(new wxStaticText(dialog, -1, "Spule:"));
grid->Add(new wxStaticText(dialog, -1, " L = "));
grid->Add(spule);
grid->Add(new wxStaticText(dialog, -1, " mH"));
grid->Add(new wxStaticText(dialog, -1, "Kondensator:"));
grid->Add(new wxStaticText(dialog, -1, " C = "));
grid->Add(kondensator);
grid->Add(new wxStaticText(dialog, -1, " µF"));
grid->Add(new wxStaticText(dialog, -1, "Widerstand:"));
grid->Add(new wxStaticText(dialog, -1, " R = "));
grid->Add(widerstand);
grid->Add(new wxStaticText(dialog, -1, " Ohm"));
grid->Add(new wxStaticText(dialog, -1, "Generator-Frequenz:"));
grid->Add(new wxStaticText(dialog, -1, " f = "));
grid->Add(frequenz);
grid->Add(new wxStaticText(dialog, -1, " Hz"));
grid->Add(new wxStaticText(dialog, -1, "Generator-Maximalspannung:"));
grid->Add(new wxStaticText(dialog, -1, " U_1_dach = "));
grid->Add(u_1_dach);
grid->Add(new wxStaticText(dialog, -1, " V"));
grid->Add(new wxStaticText(dialog, -1, "Zeitschritt:"));
grid->Add(new wxStaticText(dialog, -1, " delta_t = "));
grid->Add(delta_t);
grid->Add(new wxStaticText(dialog, -1, " ms"));
grid->Add(new wxStaticText(dialog, -1, "Verstärkung:"));
grid->Add(new wxStaticText(dialog, -1, " "));
grid->Add(verstaerker);
grid->Add(new wxStaticText(dialog, -1, " x"));
grid->Add(new wxStaticText(dialog, -1, "Startwert für t:"));
grid->Add(new wxStaticText(dialog, -1, " t = "));
grid->Add(start_t);
grid->Add(new wxStaticText(dialog, -1, " s"));
grid->Add(new wxStaticText(dialog, -1, "Starwert für die Stromstärke:"));
grid->Add(new wxStaticText(dialog, -1, " I = "));
grid->Add(start_i);
grid->Add(new wxStaticText(dialog, -1, " A"));
grid->Add(new wxStaticText(dialog, -1, "Startwert für die Ladung:"));
grid->Add(new wxStaticText(dialog, -1, " Q = "));
grid->Add(start_q);
grid->Add(new wxStaticText(dialog, -1, " C"));
buttons->Add(new wxButton(dialog, ID_Settings_OK, "OK"), 1, wxRIGHT, 5);
buttons->Add(new wxButton(dialog, ID_Settings_Cancel, "Abbrechen"), 1, wxLEFT, 5);
topsizer->Add(grid, 0, wxALL, 10);
topsizer->Add(buttons, 0, wxALIGN_CENTER | wxALL, 5);
dialog->SetAutoLayout(TRUE);
dialog->SetSizer(topsizer);
topsizer->Fit(dialog);
if (dialog->ShowModal() == ID_Settings_OK) {
//Parameter speichern
if (spule->GetValue().ToDouble(&data.l)) {
data.l *= pow(10, -3);
}
if (kondensator->GetValue().ToDouble(&data.c)) {
data.c *= pow(10, -6);
}
widerstand->GetValue().ToDouble(&data.r);
frequenz->GetValue().ToDouble(&data.f);
u_1_dach->GetValue().ToDouble(&data.u_1_dach);
if (delta_t->GetValue().ToDouble(&data.delta_t)) {
data.delta_t *= pow(10, -3);
}
verstaerker->GetValue().ToDouble(&data.verstaerkung);
start_t->GetValue().ToDouble(&data.start_t);
start_i->GetValue().ToDouble(&data.start_i);
start_q->GetValue().ToDouble(&data.start_q);
}
// neu zeichnen
frame->Refresh();
}
void SchwingungenApp::OnSettingsOK(wxCommandEvent &event) {
dialog->EndModal(ID_Settings_OK);
}
void SchwingungenApp::OnSettingsCancel(wxCommandEvent &event) {
dialog->EndModal(ID_Settings_Cancel);
}
void SchwingungenApp::OnVerPlus(wxCommandEvent& event) {
data.verstaerkung += 5;
frame->Refresh();
}
void SchwingungenApp::OnVerMinus(wxCommandEvent& event) {
data.verstaerkung -= 5;
if (data.verstaerkung <= 0) {
data.verstaerkung = 1;
}
frame->Refresh();
}
void SchwingungenApp::OnVerNix(wxCommandEvent &event) {
data.verstaerkung = 1;
frame->Refresh();
}
void SchwingungenApp::OnSkala(wxCommandEvent &event) {
if (skala == FALSE) {
skala = TRUE;
} else {
skala = FALSE;
}
frame->Refresh();
}
void SchwingungenApp::OnSave(wxCommandEvent &event) {
wxString filename;
wxFileDialog * file = new wxFileDialog(frame);
file->SetWildcard("BMP Dateien (*.bmp)|*.bmp|Alle Dateien (*.*)|*.*");
file->SetDirectory(::wxGetWorkingDirectory());
if (file->ShowModal() == wxID_OK) {
filename = file->GetDirectory();
filename += "/";
filename += file->GetFilename();
} else {
return;
}
int width, height;
frame->GetClientSize(&width, &height);
wxMemoryDC mem;
wxBitmap bitmap(width, height);
mem.SelectObject(bitmap);
Draw(mem);
bitmap.SaveFile(filename, wxBITMAP_TYPE_BMP);
}
syntax highlighted by Code2HTML, v. 0.9.1