diff --git a/qucs/qucsactivefilter/.gitignore b/qucs/qucsactivefilter/.gitignore new file mode 100644 index 0000000000..9d1b6d2a6d --- /dev/null +++ b/qucs/qucsactivefilter/.gitignore @@ -0,0 +1,19 @@ +*.o +*.cpp~ +*.h~ +qucsactivefilter +*.pro.user +Makefile +moc_*.cpp +*.sch +*.sch~ +*.dat +*.dpl +qrc_*.cpp +moc_*.cxx +qrc_*.cxx +CMakeFiles +CMakeCache.txt +*.cmake +*.depends + diff --git a/qucs/qucsactivefilter/AFR.svg b/qucs/qucsactivefilter/AFR.svg new file mode 100644 index 0000000000..04a1f40d48 --- /dev/null +++ b/qucs/qucsactivefilter/AFR.svg @@ -0,0 +1,401 @@ + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + Rp + + + Fc + + + + Fs + + + + As + K (dB) + F (Hz) + 0 + + + + + + Ap + + + + + + + + + + + + + + + diff --git a/qucs/qucsactivefilter/CMakeLists.txt b/qucs/qucsactivefilter/CMakeLists.txt new file mode 100644 index 0000000000..bc7e34db3e --- /dev/null +++ b/qucs/qucsactivefilter/CMakeLists.txt @@ -0,0 +1,88 @@ +PROJECT(qucsactivefilter CXX C) +CMAKE_MINIMUM_REQUIRED(VERSION 2.6) +cmake_policy(VERSION 2.6) + +SET(CMAKE_BUILD_TYPE Debug) + +ADD_DEFINITIONS( -DHAVE_CONFIG_H ) + +# define variables +SET(BINARYDIR "${CMAKE_INSTALL_PREFIX}/bin/") +SET(BITMAPDIR "${CMAKE_INSTALL_PREFIX}/share/qucs/bitmaps/") +SET(DOCDIR "${CMAKE_INSTALL_PREFIX}/share/qucs/docs/") +SET(LANGUAGEDIR "${CMAKE_INSTALL_PREFIX}/share/qucs/lang/") +SET(LIBRARYDIR "${CMAKE_INSTALL_PREFIX}/share/qucs/library/") +SET(OCTAVEDIR "${CMAKE_INSTALL_PREFIX}/share/qucs/octave/") + +# configure the header config.h +#CONFIGURE_FILE ( +# "${PROJECT_SOURCE_DIR}/../config.h.cmake" +# "${PROJECT_BINARY_DIR}/config.h" +#) + +INCLUDE_DIRECTORIES("${PROJECT_BINARY_DIR}") + +FIND_PACKAGE( Qt4 REQUIRED ) +SET( QT_USE_QTGUI TRUE ) +SET( QT_USE_QTSVG TRUE ) + +INCLUDE( ${QT_USE_FILE} ) + +ADD_DEFINITIONS(${QT_DEFINITIONS}) + +SET(QUCS-ACTIVE-FILTER_SRCS +filter.cpp +mfbfilter.cpp +main.cpp +qf_poly.cpp +sallenkey.cpp +schcauer.cpp +transferfuncdialog.cpp +qucsactivefilter.cpp +) + +SET(QUCS-ACTIVE-FILTER_MOC_HDRS +transferfuncdialog.h +qucsactivefilter.h +) + +QT4_WRAP_CPP(QUCS-ACTIVE-FILTER_MOC_SRCS ${QUCS-ACTIVE-FILTER_MOC_HDRS}) + +SET(RESOURCES qucsactivefilter.qrc) +QT4_ADD_RESOURCES(RESOURCES_SRCS ${RESOURCES}) + + +IF(APPLE) + # set information on Info.plist file + SET(MACOSX_BUNDLE_INFO_STRING "${PROJECT_NAME} ${PROJECT_VERSION}") + SET(MACOSX_BUNDLE_BUNDLE_VERSION "${PROJECT_NAME} ${PROJECT_VERSION}") + SET(MACOSX_BUNDLE_LONG_VERSION_STRING "${PROJECT_NAME} ${PROJECT_VERSION}") + SET(MACOSX_BUNDLE_SHORT_VERSION_STRING "${PROJECT_VERSION}") + SET(MACOSX_BUNDLE_COPYRIGHT "${PROJECT_COPYRIGHT_YEAR} ${PROJECT_VENDOR}") + SET(MACOSX_BUNDLE_GUI_IDENTIFIER "${PROJECT_DOMAIN_SECOND}.${PROJECT_DOMAIN_FIRST}") + SET(MACOSX_BUNDLE_BUNDLE_NAME "${PROJECT_NAME}") + SET(MACOSX_BUNDLE_COPYRIGHT "(C) 2013 Qucs team" ) + SET(MACOSX_BUNDLE_ICON_FILE qucsactivefilter.icns) + + # set where in the bundle to put the icns file + SET_SOURCE_FILES_PROPERTIES(${CMAKE_CURRENT_SOURCE_DIR}/../qucs/bitmaps/qucsactivefilter.icns PROPERTIES MACOSX_PACKAGE_LOCATION Resources) + # include the icns file in the target + SET(QUCS-ACTIVE-FILTER_SRCS ${QUCS-ACTIVE-FILTER_SRCS} ${CMAKE_CURRENT_SOURCE_DIR}/../qucs/bitmaps/qucsactivefilter.icns) + +ENDIF(APPLE) + +ADD_EXECUTABLE(qucsactivefilter MACOSX_BUNDLE WIN32 + ${QUCS-ACTIVE-FILTER_SRCS} + ${QUCS-ACTIVE-FILTER_MOC_SRCS} + ${RESOURCES_SRCS} ) + +TARGET_LINK_LIBRARIES(qucsactivefilter ${QT_LIBRARIES}) + +INSTALL(TARGETS qucsactivefilter + BUNDLE DESTINATION bin COMPONENT Runtime + RUNTIME DESTINATION bin COMPONENT Runtime + ) + +# man pages +INSTALL( FILES qucsactivefilter.1 DESTINATION share/man/man1 ) + diff --git a/qucs/qucsactivefilter/QFilterSintez.pro b/qucs/qucsactivefilter/QFilterSintez.pro new file mode 100644 index 0000000000..b69cf10613 --- /dev/null +++ b/qucs/qucsactivefilter/QFilterSintez.pro @@ -0,0 +1,36 @@ +#------------------------------------------------- +# +# Project created by QtCreator 2014-04-10T18:36:21 +# +#------------------------------------------------- + +QT += core gui svg + +greaterThan(QT_MAJOR_VERSION, 4): QT += widgets + +TARGET = QFilterSintez +TEMPLATE = app + + +SOURCES += main.cpp\ + filter.cpp \ + sallenkey.cpp \ + mfbfilter.cpp \ + qf_poly.cpp \ + schcauer.cpp \ + transferfuncdialog.cpp \ + qucsactivefilter.cpp + +HEADERS += \ + filter.h \ + sallenkey.h \ + mfbfilter.h \ + qf_poly.h \ + qf_matrix.h \ + schcauer.h \ + transferfuncdialog.h \ + bessel.h \ + qucsactivefilter.h + +RESOURCES += \ + qucsactivefilter.qrc diff --git a/qucs/qucsactivefilter/README b/qucs/qucsactivefilter/README new file mode 100644 index 0000000000..938f3f668f --- /dev/null +++ b/qucs/qucsactivefilter/README @@ -0,0 +1,2 @@ +QFilterSintez is tool for sintezing active and passive analog filters. + diff --git a/qucs/qucsactivefilter/bessel-poles.m b/qucs/qucsactivefilter/bessel-poles.m new file mode 100755 index 0000000000..b8b13654d0 --- /dev/null +++ b/qucs/qucsactivefilter/bessel-poles.m @@ -0,0 +1,57 @@ +#!/usr/bin/octave -qf + +printf("Generating bessel.h...\n"); + +maxorder=20; + +fid=fopen("bessel.h","w"); +fprintf(fid,"#ifndef BESSEL_H\n"); +fprintf(fid,"#define BESSEL_H\n"); +fprintf(fid,"//Bessel coeffs table\n //Generated automatically! DO NOT EDIT!!!\n\n\n"); +fprintf(fid,"\n\ndouble BesselPoles[%d][%d]={\n\n",maxorder,2*maxorder); + +for n=1:maxorder; + +fprintf(fid,"/* %d th order */ {\n",n); +b=1; +for k=1:n+1; +b(k)=factorial(2*n-(k-1))/((2^(n-(k-1)))*factorial(k-1)*factorial(n-(k-1))); +endfor; +b=fliplr(b); +#disp(b); +#printf("Bessel polynomial %dth order poles:",n); +poles=roots(b); +Np=length(poles); + +for i=1:Np; +fprintf(fid," %f, ",real(poles(i))); +#fprintf(fid,"\t%f,\n",imag(poles(i))); +if (i==maxorder) +fprintf(fid," %f \n",imag(poles(i))); +else +fprintf(fid," %f, ",imag(poles(i))); +endif; +endfor; + +for i=Np+1:maxorder; +#fprintf(fid," 0.0,\n"); +if (i==maxorder) +fprintf(fid," 0.0 \n"); +else +fprintf(fid," 0.0, "); +endif; +endfor; + +if (n==maxorder) +fprintf(fid,"\t}\n"); +else +fprintf(fid,"\t},\n"); +endif; + +endfor; + +fprintf(fid,"\n};\n"); +fprintf(fid,"#endif\n"); +fclose(fid); + +printf("bessel.h successfully generated!\n"); diff --git a/qucs/qucsactivefilter/bessel.h b/qucs/qucsactivefilter/bessel.h new file mode 100644 index 0000000000..387d342b44 --- /dev/null +++ b/qucs/qucsactivefilter/bessel.h @@ -0,0 +1,73 @@ +#ifndef BESSEL_H +#define BESSEL_H +//Bessel coeffs table + //Generated automatically! DO NOT EDIT!!! + + + + +double BesselPoles[20][40]={ + +/* 1 th order */ { + -1.000000, 0.000000, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0 + }, +/* 2 th order */ { + -1.500000, 0.866025, -1.500000, -0.866025, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0 + }, +/* 3 th order */ { + -1.838907, 1.754381, -1.838907, -1.754381, -2.322185, 0.000000, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0 + }, +/* 4 th order */ { + -2.103789, 2.657418, -2.103789, -2.657418, -2.896211, 0.867234, -2.896211, -0.867234, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0 + }, +/* 5 th order */ { + -2.324674, 3.571023, -2.324674, -3.571023, -3.646739, 0.000000, -3.351956, 1.742661, -3.351956, -1.742661, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0 + }, +/* 6 th order */ { + -2.515932, 4.492673, -2.515932, -4.492673, -3.735708, 2.626272, -3.735708, -2.626272, -4.248359, 0.867510, -4.248359, -0.867510, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0 + }, +/* 7 th order */ { + -2.685677, 5.420694, -2.685677, -5.420694, -4.070139, 3.517174, -4.070139, -3.517174, -4.971787, 0.000000, -4.758291, 1.739286, -4.758291, -1.739286, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0 + }, +/* 8 th order */ { + -2.838984, 6.353911, -2.838984, -6.353911, -4.368289, 4.414443, -4.368289, -4.414443, -5.204841, 2.616175, -5.204841, -2.616175, -5.587886, 0.867614, -5.587886, -0.867614, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0 + }, +/* 9 th order */ { + -2.979261, 7.291464, -2.979261, -7.291464, -4.638440, 5.317272, -4.638440, -5.317272, -5.604422, 3.498157, -5.604422, -3.498157, -6.297019, 0.000000, -6.129368, 1.737848, -6.129368, -1.737848, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0 + }, +/* 10 th order */ { + -3.108916, 8.232699, -3.108916, -8.232699, -4.886220, 6.224985, -4.886220, -6.224985, -5.967528, 4.384947, -5.967528, -4.384947, -6.615291, 2.611568, -6.615291, -2.611568, -6.922045, 0.867665, -6.922045, -0.867665, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0 + }, +/* 11 th order */ { + -3.229722, 9.177112, -3.229722, -9.177112, -5.115648, 7.137021, -5.115648, -7.137021, -6.301337, 5.276192, -6.301337, -5.276192, -7.057892, 3.489015, -7.057892, -3.489015, -7.622340, 0.000000, -7.484230, 1.737103, -7.484230, -1.737103, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0 + }, +/* 12 th order */ { + -3.343023, 10.124297, -3.343023, -10.124297, -5.329709, 8.052907, -5.329709, -8.052907, -6.611004, 6.171535, -6.611004, -6.171535, -7.465571, 4.370170, -7.465571, -4.370170, -7.997271, 2.609067, -7.997271, -2.609067, -8.253422, 0.867694, -8.253422, -0.867694, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0 + }, +/* 13 th order */ { + -3.449867, 11.073929, -3.449867, -11.073929, -5.530681, 8.972248, -5.530681, -8.972248, -6.900373, 7.070644, -6.900373, -7.070644, -7.844380, 5.254903, -7.844380, -5.254903, -8.470592, 3.483868, -8.470592, -3.483868, -8.947710, 0.000000, -8.830252, 1.736666, -8.830252, -1.736666, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0 + }, +/* 14 th order */ { + -3.551087, 12.025738, -3.551087, -12.025738, -5.720352, 9.894708, -5.720352, -9.894708, -7.172396, 7.973217, -7.172396, -7.973217, -8.198847, 6.143041, -8.198847, -6.143041, -8.911001, 4.361604, -8.911001, -4.361604, -9.363146, 2.607553, -9.363146, -2.607553, -9.583171, 0.867711, -9.583171, -0.867711, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0 + }, +/* 15 th order */ { + -3.647357, 12.979501, -3.647357, -12.979501, -5.900152, 10.819999, -5.900152, -10.819999, -7.429397, 8.878983, -7.429397, -8.878983, -8.532459, 7.034394, -8.532459, -7.034394, -9.323599, 5.242259, -9.323599, -5.242259, -9.859567, 3.480671, -9.859567, -3.480671, -10.273109, 0.000000, -10.170914, 1.736389, -10.170914, -1.736389, 0.0, 0.0, 0.0, 0.0, 0.0 + }, +/* 16 th order */ { + -3.739232, 13.935028, -3.739232, -13.935028, -6.071241, 11.747875, -6.071241, -11.747875, -7.673241, 9.787697, -7.673241, -9.787697, -8.847968, 7.928773, -8.847968, -7.928773, -9.712326, 6.125761, -9.712326, -6.125761, -10.325121, 4.356163, -10.325121, -4.356163, -10.911887, 0.867721, -10.911887, -0.867721, -10.718985, 2.606568, -10.718985, -2.606568, 0.0, 0.0, 0.0, 0.0 + }, +/* 17 th order */ { + -3.827174, 14.892159, -3.827174, -14.892159, -6.234581, 12.678120, -6.234581, -12.678120, -7.905450, 10.699145, -7.905450, -10.699145, -9.147588, 8.825998, -9.147588, -8.825998, -10.080296, 7.012010, -10.080296, -7.012010, -10.764132, 5.234075, -10.764132, -5.234075, -11.233439, 3.478542, -11.233439, -3.478542, -11.598530, 0.000000, -11.508076, 1.736203, -11.508076, -1.736203, 0.0, 0.0, 0.0 + }, +/* 18 th order */ { + -3.911572, 15.850754, -3.911572, -15.850754, -6.390973, 13.610547, -6.390973, -13.610547, -8.127284, 11.613132, -8.127284, -11.613132, -9.433133, 9.725901, -9.433133, -9.725901, -10.430010, 7.900893, -10.430010, -7.900893, -11.180044, 6.114391, -11.180044, -6.114391, -11.718943, 4.352488, -11.718943, -4.352488, -12.068139, 2.605877, -12.068139, -2.605877, -12.239902, 0.867741, -12.239902, -0.867741, 0.0, 0.0 + }, +/* 19 th order */ { + -3.992759, 16.810692, -3.992759, -16.810692, -6.541095, 14.544991, -6.541095, -14.544991, -8.339801, 12.529484, -8.339801, -12.529484, -9.706101, 10.628321, -9.706101, -10.628321, -10.763544, 8.792290, -10.763544, -8.792290, -11.575589, 6.997092, -11.575589, -6.997092, -12.179243, 5.228415, -12.179243, -5.228415, -12.597062, 3.477100, -12.597062, -3.477100, -12.923980, 0.000000, -12.842816, 1.736037, -12.842816, -1.736037, 0.0 + }, +/* 20 th order */ { + -4.071019, 17.771869, -4.071019, -17.771869, -6.685527, 15.481306, -6.685527, -15.481306, -8.543895, 13.448046, -8.543895, -13.448046, -9.967765, 11.533112, -9.967765, -11.533112, -11.082571, 9.686112, -11.082571, -9.686112, -11.953103, 7.881991, -11.953103, -7.881991, -12.617294, 6.106632, -12.617294, -6.106632, -13.098755, 4.349634, -13.098755, -4.349634, -13.567377, 0.867486, -13.567377, -0.867486, -13.412693, 2.605660, -13.412693, -2.605660 + } + +}; +#endif diff --git a/qucs/qucsactivefilter/cauer.svg b/qucs/qucsactivefilter/cauer.svg new file mode 100644 index 0000000000..48308459c9 --- /dev/null +++ b/qucs/qucsactivefilter/cauer.svg @@ -0,0 +1,1595 @@ + + + + + + image/svg+xml + + + + + + Qt Svg Document + Generated with Qt + + + + + + + + + + + + + + + + + + + + + + + R1 + + + + + + + + + + + + + + + + + + OP3 + + + + + + + + + + + + + + + + + + OP2 + + + + + + + + + + + C1 + + + + + + + + + + + + + + + + + + OP1 + + + + + + + + + + + + + + + + + + + C2 + + + + + + + + + + + R5 + + + + + + + + + + + R4 + + + + + + + + + + + R2 + + + + + + + + + + + R3 + + + + + + + + + + + + + + + + + + + + + + + + + + + V1 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + output + + + + + + + + + + + + + + + + + + input + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/qucs/qucsactivefilter/filter.cpp b/qucs/qucsactivefilter/filter.cpp new file mode 100644 index 0000000000..6472011c19 --- /dev/null +++ b/qucs/qucsactivefilter/filter.cpp @@ -0,0 +1,536 @@ +#include "filter.h" +#include "qf_poly.h" +#include "bessel.h" + +Filter::Filter(Filter::FilterFunc ffunc_, Filter::FType type_, FilterParam par) +{ + ffunc = ffunc_; + ftype = type_; + Fc = par.Fc; + Fs = par.Fs; + Rp = par.Rp; + As = par.As; + Ap = par.Ap; + Kv = par.Kv; + if (ffunc==Filter::Bessel) { + order = par.order; + } +} + +Filter::~Filter() +{ + +} + +void Filter::createSchematic(QString &s) +{ + switch (ftype) { + case Filter::HighPass : createHighPassSchematic(s); + break; + case Filter::LowPass : createLowPassSchematic(s); + break; + default: break; + } + + /*QFile sch("filter.sch"); + sch.open(QFile::WriteOnly); + QTextStream out(&sch); + out< zero; + foreach(zero,Zeros) { + lst< pole; + foreach(pole,Poles) { + lst<\n").arg(Nopamp+1).arg(270+dx); + s += QString("\n").arg(170+dx); + s += QString("\n").arg(220+dx); + s += QString("\n").arg(Nr+2).arg(220+dx).arg(stage.R2,0,'f',3); + s += QString("\n").arg(Nr+1).arg(170+dx).arg(stage.R1,0,'f',3); + s += QString("\n").arg(Nr+3).arg(310+dx).arg(stage.R3,0,'f',3); + s += QString("\n").arg(Nc+1).arg(100+dx).arg(C1,0,'f',3).arg(suf); +} + + +void Filter::createFirstOrderComponentsLPF(QString &s,RC_elements stage,int dx) +{ + QString suf; + float C1 = autoscaleCapacitor(stage.C1,suf); + s += QString("\n").arg(Nopamp+1).arg(270+dx); + s += QString("\n").arg(170+dx); + s += QString("\n").arg(220+dx); + s += QString("\n").arg(Nr+2).arg(220+dx).arg(stage.R2,0,'f',3); + s += QString("\n").arg(Nr+1).arg(100+dx).arg(stage.R1,0,'f',3); + s += QString("\n").arg(Nr+3).arg(310+dx).arg(stage.R3,0,'f',3); + s += QString("\n").arg(Nc+1).arg(170+dx).arg(C1,0,'f',3).arg(suf); +} + +void Filter::createFirstOrderWires(QString &s, int dx, int y) +{ + s += QString("<%1 190 %2 190 \"\" 0 0 0 \"\">\n").arg(dx-20).arg(70+dx); + s += QString("<%1 190 %2 %3 \"\" 0 0 0 \"\">\n").arg(dx-20).arg(dx-20).arg(y); + s += QString("<%1 %2 %3 %4 \"\" 0 0 0 \"\">\n").arg(dx-20).arg(y).arg(dx-50).arg(y); + + s += QString("<%1 190 %2 190 \"\" 0 0 0 \"\">\n").arg(130+dx).arg(170+dx); + s += QString("<%1 160 %2 160 \"out\" %3 130 39 \"\">\n").arg(310+dx).arg(360+dx).arg(380+dx); + s += QString("<%1 260 %2 260 \"\" 0 0 0 \"\">\n").arg(340+dx).arg(360+dx); + s += QString("<%1 160 %2 260 \"\" 0 0 0 \"\">\n").arg(360+dx).arg(360+dx); + s += QString("<%1 190 %2 210 \"\" 0 0 0 \"\">\n").arg(170+dx).arg(170+dx); + s += QString("<%1 140 %2 190 \"\" 0 0 0 \"\">\n").arg(170+dx).arg(170+dx); + s += QString("<%1 140 %2 140 \"\" 0 0 0 \"\">\n").arg(170+dx).arg(240+dx); + s += QString("<%1 180 %2 260 \"\" 0 0 0 \"\">\n").arg(220+dx).arg(220+dx); + s += QString("<%1 180 %2 180 \"\" 0 0 0 \"\">\n").arg(220+dx).arg(240+dx); + s += QString("<%1 260 %2 260 \"\" 0 0 0 \"\">\n").arg(220+dx).arg(280+dx); + s += QString("<%1 260 %2 310 \"\" 0 0 0 \"\">\n").arg(220+dx).arg(220+dx); +} + + +float Filter::autoscaleCapacitor(float C, QString &suffix) +{ + float C1 = C*1e-6; + + if (C1>=1e-7) { + suffix = "uF"; + C1 *= 1e6; + } + + if ((C1<1e-7)&&(C1>=1e-8)) { + suffix = "nF"; + C1 *= 1e9; + } + + if (C1<1e-8) { + suffix = "pF"; + C1 *= 1e12; + } + return C1; +} + +bool Filter::checkRCL() +{ + RC_elements sec; + foreach (sec,Sections) { + if (std::isnan(sec.R1)|| + std::isnan(sec.R2)|| + std::isnan(sec.R3)|| + std::isnan(sec.R4)|| + std::isnan(sec.C1)|| + std::isnan(sec.C2)) { + return false; + } + } + return true; +} + +void Filter::calcChebyshev() +{ + float kf = std::max(Fs/Fc,Fc/Fs); + float eps=sqrt(pow(10,0.1*Rp)-1); + + float N1 = acosh(sqrt((pow(10,0.1*As)-1)/(eps*eps)))/acosh(kf); + int N = ceil(N1); + + float a = sinh((asinh(1/eps))/N); + float b = cosh((asinh(1/eps))/N); + + Poles.clear(); + Zeros.clear(); + + for (int k=1;k<=N;k++) { + float re = -1*a*sin(M_PI*(2*k-1)/(2*N)); + float im = b*cos(M_PI*(2*k-1)/(2*N)); + std::complex pol(re,im); + Poles.append(pol); + } + + order = Poles.count(); +} + +void Filter::calcButterworth() +{ + float kf = std::min(Fc/Fs,Fs/Fc); + + float C1=(pow(10,(0.1*Ap))-1)/(pow(10,(0.1*As))-1); + float J2=log10(C1)/(2*log10(kf)); + int N2 = round(J2+1); + + Poles.clear(); + Zeros.clear(); + + for (int k=1;k<=N2;k++) { + float re =-1*sin(M_PI*(2*k-1)/(2*N2)); + float im =cos(M_PI*(2*k-1)/(2*N2)); + std::complex pol(re,im); + Poles.append(pol); + } + + order = Poles.count(); +} + +void Filter::calcInvChebyshev() // Chebyshev Type-II filter +{ + Poles.clear(); + Zeros.clear(); + + float kf = std::max(Fs/Fc,Fc/Fs); + + order = ceil(acosh(sqrt(pow(10.0,0.1*As)-1.0))/acosh(kf)); + + float eps = 1.0/(sqrt(pow(10.0,0.1*As)-1.0)); + float a = sinh((asinh(1.0/eps))/(order)); + float b = cosh((asinh(1.0/eps))/(order)); + + for (int k=1;k<=order;k++) { + float im = 1.0/(cos(((2*k-1)*M_PI)/(2*order))); + Zeros.append(std::complex(0,im)); + } + + for (int k=1;k<=order;k++) { + float re = -1*a*sin(M_PI*(2*k-1)/(2*order)); + float im = b*cos(M_PI*(2*k-1)/(2*order)); + std::complex invpol(re,im); // inverse pole + std::complex pol; + pol = std::complex(1.0,0) / invpol; // pole + Poles.append(pol); + } + +} + +void Filter::cauerOrderEstim() // from Digital Filter Design Handbook page 102 +{ + float k = std::min(Fc/Fs,Fs/Fc); + float kk = sqrt(sqrt(1.0-k*k)); + float u = 0.5*(1.0-kk)/(1.0+kk); + float q = 150.0*pow(u,13) + 2.0*pow(u,9) + 2.0*pow(u,5) + u; + float dd = (pow(10.0,As/10.0)-1.0)/(pow(10.0,Rp/10.0)-1.0); + order = ceil(log10(16.0*dd)/log10(1.0/q)); +} + +void Filter::calcCauer() // from Digital Filter Designer's handbook p.103 +{ + float P0; + //float H0; + float mu; + float aa[50],bb[50],cc[50]; + + cauerOrderEstim(); + float k = Fc/Fs; + float kk = sqrt(sqrt(1.0-k*k)); + float u = 0.5*(1.0-kk)/(1.0+kk); + float q = 150.0*pow(u,13) + 2.0*pow(u,9) + 2.0*pow(u,5) + u; + float numer = pow(10.0,Rp/20.0)+1.0; + float vv = log(numer/(pow(10.0,Rp/20.0)-1.0))/(2.0*order); + float sum = 0.0; + for (int m=0;m<5;m++) { + float term = pow(-1.0,m); + term = term*pow(q,m*(m+1)); + term = term*sinh((2*m+1)*vv); + sum = sum +term; + } + numer = 2.0*sum*sqrt(sqrt(q)); + + sum=0.0; + for (int m=1;m<5;m++) { + float term = pow(-1.0,m); + term = term*pow(q,m*m); + term = term*cosh(2.0*m*vv); + sum += term; + } + float denom = 1.0+2.0*sum; + P0 = fabs(numer/denom); + float ww = 1.0+k*P0*P0; + ww = sqrt(ww*(1.0+P0*P0/k)); + int r = (order-(order%2))/2; + //float numSecs = r; + + for (int i=1;i<=r;i++) { + if ((order%2)!=0) { + mu = i; + } else { + mu = i-0.5; + } + sum = 0.0; + for(int m=0;m<5;m++) { + float term = pow(-1.0,m)*pow(q,m*(m+1)); + term = term*sin((2*m+1)*M_PI*mu/order); + sum += term; + } + numer = 2.0*sum*sqrt(sqrt(q)); + + sum = 0.0; + for(int m=1;m<5;m++) { + float term = pow(-1.0,m)*pow(q,m*m); + term = term*cos(2.0*m*M_PI*mu/order); + sum += term; + } + denom = 1.0+2.0*sum; + float xx = numer/denom; + float yy = 1.0 - k*xx*xx; + yy = sqrt(yy*(1.0-(xx*xx/k))); + aa[i-1] = 1.0/(xx*xx); + denom = 1.0 + pow(P0*xx,2); + bb[i-1] = 2.0*P0*yy/denom; + denom = pow(denom,2); + numer = pow(P0*yy,2)+pow(xx*ww,2); + cc[i-1] = numer/denom; + } + + if (order%2!=0) { + cc[order-1] = P0; // first order section + bb[order-1] = 0; + } + + Zeros.clear(); + Poles.clear(); + for (int i=0;i(0,im)); + float re = -0.5*bb[i]; + im = 0.5*sqrt(-1.0*bb[i]*bb[i]+4*cc[i]); + Poles.append(std::complex(re,im)); + } + + if (order%2!=0) { + Poles.append(std::complex(-cc[order-1],0.0)); + } + + for (int i=r-1;i>=0;i--) { + float im = sqrt(aa[i]); + Zeros.append(std::complex(0,-im)); + float re = -0.5*bb[i]; + im = 0.5*sqrt(-1.0*bb[i]*bb[i]+4*cc[i]); + Poles.append(std::complex(re,-im)); + } +} + +void Filter::calcBessel() +{ + Poles.clear(); + Zeros.clear(); + + if (order<=0) return; + + for (int i=0;i(BesselPoles[order-1][2*i],BesselPoles[order-1][2*i+1])); + } + + reformPolesZeros(); +} + +void Filter::calcUserTrFunc() +{ + if ((!vec_A.isEmpty())&&(!vec_B.isEmpty())) { + long double *a = vec_A.data(); + long double *b = vec_B.data(); + + int a_order = vec_A.count() - 1; + int b_order = vec_B.count() - 1; + + order = std::max(a_order,b_order); + + qf_poly Numenator(b_order,b); + qf_poly Denomenator(a_order,a); + + Numenator.to_roots(); + Denomenator.to_roots(); + + Numenator.disp_c(); + Denomenator.disp_c(); + + Numenator.roots_to_complex(Zeros); + Denomenator.roots_to_complex(Poles); + + reformPolesZeros(); + } + +} + +void Filter::reformPolesZeros() +{ + int Np = Poles.count(); + int Nz = Zeros.count(); + + for (int i=0;i tmp; + tmp = Poles[i]; + Poles[i]=Poles[Np-1-i]; + Poles[Np-1-i]=tmp; + } + } + + for (int i=0;i tmp; + tmp = Poles[i]; + Poles[i]=Poles[Nz-1-i]; + Poles[Nz-1-i]=tmp; + } + } +} + +void Filter::set_TrFunc(QVector a, QVector b) +{ + vec_A = a; + vec_B = b; +} diff --git a/qucs/qucsactivefilter/filter.h b/qucs/qucsactivefilter/filter.h new file mode 100644 index 0000000000..92ac0ad9d9 --- /dev/null +++ b/qucs/qucsactivefilter/filter.h @@ -0,0 +1,91 @@ +#ifndef FILTER_H +#define FILTER_H + +#include +#include +#include + +struct RC_elements { + int N; + float R1; + float R2; + float R3; + float R4; + float R5; + float C1; + float C2; +}; + +struct FilterParam { + float Ap; + float As; + float Fc; + float Fs; + float Rp; + float Kv; + int order; +}; + +class Filter +{ + +public: + enum FType {HighPass, LowPass, BandPass, BandStop, NoFilter}; + enum FilterFunc {Butterworth, Chebyshev, Cauer, Bessel, InvChebyshev, NoFunc, User}; + +private: + void cauerOrderEstim(); + void reformPolesZeros(); + +protected: + QVector< std::complex > Poles; + QVector< std::complex > Zeros; + QVector vec_B; // Transfer function numenator + QVector vec_A; // and denominator + QVector Sections; + + Filter::FType ftype; + Filter::FilterFunc ffunc; + int order; + float Fc,Kv,Fs,Ap,As,Rp; + int Nr,Nc,Nopamp; // total number of R,C, opamp + + int Nr1,Nc1,Nop1; // number of R,C, opamp per stage + + void calcButterworth(); + void calcChebyshev(); + void calcInvChebyshev(); + void calcCauer(); + void calcBessel(); + void calcUserTrFunc(); + bool checkRCL(); // Checks RCL values. Are one of them NaN or not? + + void createFirstOrderComponentsHPF(QString &s,RC_elements stage, int dx); + void createFirstOrderComponentsLPF(QString &s,RC_elements stage, int dx); + void createFirstOrderWires(QString &s, int dx, int y); + float autoscaleCapacitor(float C, QString &suffix); + virtual void calcHighPass(); + virtual void calcLowPass(); + virtual void createHighPassSchematic(QString &s); + virtual void createLowPassSchematic(QString &s); + +public: + + + Filter(Filter::FilterFunc ffunc_, Filter::FType type_, FilterParam par); + virtual ~Filter(); + + void calcFirstOrder(); + + void createPartList(QStringList &lst); + void createPolesZerosList(QStringList &lst); + + virtual void createSchematic(QString &s); + + virtual bool calcFilter(); + + void set_TrFunc(QVector a, QVector b); + +}; + +#endif // FILTER_H diff --git a/qucs/qucsactivefilter/high-pass.svg b/qucs/qucsactivefilter/high-pass.svg new file mode 100644 index 0000000000..342637b272 --- /dev/null +++ b/qucs/qucsactivefilter/high-pass.svg @@ -0,0 +1,393 @@ + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + Rp + + + Fc + + + + Fs + + + As + K (dB) + F (Hz) + 0 + + + + + + Ap + + + + + + + + + + + + + + + + diff --git a/qucs/qucsactivefilter/main.cpp b/qucs/qucsactivefilter/main.cpp new file mode 100644 index 0000000000..3de11badd7 --- /dev/null +++ b/qucs/qucsactivefilter/main.cpp @@ -0,0 +1,11 @@ +#include +#include "qucsactivefilter.h" + +int main(int argc, char *argv[]) +{ + QApplication a(argc, argv); + QucsActiveFilter w; + w.show(); + + return a.exec(); +} diff --git a/qucs/qucsactivefilter/mfb-highpass.svg b/qucs/qucsactivefilter/mfb-highpass.svg new file mode 100644 index 0000000000..a7926fe0be --- /dev/null +++ b/qucs/qucsactivefilter/mfb-highpass.svg @@ -0,0 +1,1050 @@ + + + + + + image/svg+xml + + + + + + Qt Svg Document + Generated with Qt + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + OP1 + + + + + + + + + + + + + + + + + + + + + + + + + + + V1 + + + + + + + + + + + R2 + + + + + + + + + + + R1 + + + + + + + + + + + C3 + + + + + + + + + + + C2 + + + + + + + + + + + C1 + + + + + + + + + + + + + + + + output + + + + + + + + + + + + + + input + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/qucs/qucsactivefilter/mfb-lowpass.svg b/qucs/qucsactivefilter/mfb-lowpass.svg new file mode 100644 index 0000000000..9f67fa0b1b --- /dev/null +++ b/qucs/qucsactivefilter/mfb-lowpass.svg @@ -0,0 +1,1053 @@ + + + + + + image/svg+xml + + + + + + Qt Svg Document + Generated with Qt + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + R3 + + + + + + + + + + + R2 + + + + + + + + + + + R1 + + + + + + + + + + + C2 + + + + + + + + + + + + + + + + + + OP1 + + + + + + + + + + + C1 + + + + + + + + + + + + + + + + + + + + + + + + + + + V1 + + + + + + + + + + + + + + + + + + output + + + + + + + + + + + + + + input + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/qucs/qucsactivefilter/mfbfilter.cpp b/qucs/qucsactivefilter/mfbfilter.cpp new file mode 100644 index 0000000000..2d4ea9262f --- /dev/null +++ b/qucs/qucsactivefilter/mfbfilter.cpp @@ -0,0 +1,238 @@ +#include "mfbfilter.h" + +MFBfilter::MFBfilter(Filter::FilterFunc ffunc_, Filter::FType type_, FilterParam par) + : Filter(ffunc_,type_,par) +{ + switch (ftype) { + case Filter::LowPass : + Nr1=3; + Nc1=2; + break; + case Filter::HighPass : + Nr1=2; + Nc1=3; + break; + default: + break; + } + + //Nr1 = 3; + //Nc1 = 2; + Nop1 = 1; +} + +void MFBfilter::createLowPassSchematic(QString &s) +{ + //int const N_R=4; // number of resisitors in 2-order Sallen-Key stage + //int const N_C=2; // number of capacitors in 2-order Sallen-Key stage + RC_elements stage; + int dx = 0; + int N2ord = order/2; // number of 2-nd order stages + int N1stOrd = order%2; // number of 1-st order stages + + s += "\n"; + s += "\n"; + s += QString("<.AC AC1 1 300 440 0 61 0 0 \"lin\" 1 \"1 Hz\" 1 \"%1 kHz\" 1 \"501\" 1 \"no\" 0>\n").arg((10.0*Fc)/1000.0); + s += "<.DC DC1 1 60 440 0 61 0 0 \"26.85\" 0 \"0.001\" 0 \"1 pA\" 0 \"1 uV\" 0 \"no\" 0 \"150\" 0 \"no\" 0 \"none\" 0 \"CroutLU\" 0>\n"; + s += "\n"; + s += QString("\n").arg(70+dx); + s += QString("\n").arg(70+dx); + for (int i=1; i<=N2ord; i++) { + stage = Sections.at(i-1); + qDebug()<\n").arg(200+dx); + s += QString("\n").arg(360+dx); + s += QString("\n").arg(1+(i-1)*Nop1).arg(390+dx); + s += QString("\n").arg(2+(i-1)*Nc1).arg(200+dx).arg(C2,0,'f',3).arg(suffix2); + s += QString("\n").arg(1+(i-1)*Nc1).arg(320+dx).arg(C1,0,'f',3).arg(suffix1); + s += QString("\n").arg(1+(i-1)*Nr1).arg(200+dx).arg(stage.R1,0,'f',3); + s += QString("\n").arg(2+(i-1)*Nr1).arg(150+dx).arg(stage.R2,0,'f',3); + s += QString("\n").arg(3+(i-1)*Nr1).arg(250+dx).arg(stage.R3,0,'f',3); + dx += 510; + } + + if (N1stOrd!=0) { + createFirstOrderComponentsLPF(s,Sections.last(),dx+10); + } + + s += "\n"; + s += "\n"; + dx = 0; + s += "<70 250 120 250 \"in\" 120 220 22 \"\">\n"; + s += "<70 250 70 300 \"\" 0 0 0 \"\">\n"; + for (int i=1; i<=N2ord; i++) { + if (i!=1) { + s += QString("<%1 250 %2 270 \"\" 0 0 0 \"\">\n").arg(120+dx).arg(120+dx); + s += QString("<%1 270 %2 270 \"\" 0 0 0 \"\">\n").arg(dx-40).arg(120+dx); + } + s += QString("<%1 250 %2 250 \"\" 0 0 0 \"\">\n").arg(180+dx).arg(200+dx); + s += QString("<%1 250 %2 250 \"\" 0 0 0 \"\">\n").arg(200+dx).arg(220+dx); + s += QString("<%1 210 %2 250 \"\" 0 0 0 \"\">\n").arg(200+dx).arg(200+dx); + s += QString("<%1 250 %2 320 \"\" 0 0 0 \"\">\n").arg(200+dx).arg(200+dx); + s += QString("<%1 290 %2 350 \"\" 0 0 0 \"\">\n").arg(360+dx).arg(360+dx); + s += QString("<%1 130 %2 150 \"\" 0 0 0 \"\">\n").arg(200+dx).arg(200+dx); + s += QString("<%1 130 %2 130 \"\" 0 0 0 \"\">\n").arg(200+dx).arg(320+dx); + s += QString("<%1 130 %2 150 \"\" 0 0 0 \"\">\n").arg(320+dx).arg(320+dx); + s += QString("<%1 250 %2 250 \"\" 0 0 0 \"\">\n").arg(280+dx).arg(320+dx); + s += QString("<%1 250 %2 250 \"\" 0 0 0 \"\">\n").arg(320+dx).arg(360+dx); + s += QString("<%1 210 %2 250 \"\" 0 0 0 \"\">\n").arg(320+dx).arg(320+dx); + s += QString("<%1 130 %2 130 \"\" 0 0 0 \"\">\n").arg(320+dx).arg(470+dx); + s += QString("<%1 270 %2 270 \"\" 0 0 0 \"\">\n").arg(430+dx).arg(470+dx); + if ((2*i)==order) { + s += QString("<%1 130 %2 270 \"out\" %3 170 70 \"\">\n").arg(470+dx).arg(470+dx).arg(500+dx); + } else { + s += QString("<%1 130 %2 270 \"\" 0 0 0 \"\">\n").arg(470+dx).arg(470+dx); + } + dx += 510; + } + + if (N1stOrd!=0) { + createFirstOrderWires(s,dx+10,270); + } + + s += "\n"; +} + +void MFBfilter::createHighPassSchematic(QString &s) +{ + RC_elements stage; + int dx = 0; + int N2ord = order/2; // number of 2-nd order stages + int N1stOrd = order%2; // number of 1-st order stages + + s += "\n"; + s += "\n"; + s += QString("<.AC AC1 1 300 440 0 61 0 0 \"lin\" 1 \"1 Hz\" 1 \"%1 kHz\" 1 \"501\" 1 \"no\" 0>\n").arg((10.0*Fc)/1000.0); + s += "<.DC DC1 1 60 440 0 61 0 0 \"26.85\" 0 \"0.001\" 0 \"1 pA\" 0 \"1 uV\" 0 \"no\" 0 \"150\" 0 \"no\" 0 \"none\" 0 \"CroutLU\" 0>\n"; + s += "\n"; + s += QString("\n").arg(70+dx); + s += QString("\n").arg(70+dx); + for (int i=1; i<=N2ord; i++) { + stage = Sections.at(i-1); + qDebug()<\n").arg(200+dx); + s += QString("\n").arg(360+dx); + s += QString("\n").arg(1+(i-1)*Nop1).arg(390+dx); + s += QString("\n").arg(1+(i-1)*Nr1).arg(200+dx).arg(stage.R1,0,'f',3); + s += QString("\n").arg(2+(i-1)*Nr1).arg(320+dx).arg(stage.R2,0,'f',3); + s += QString("\n").arg(1+(i-1)*Nc1).arg(200+dx).arg(C1,0,'f',3).arg(suffix1); + s += QString("\n").arg(2+(i-1)*Nc1).arg(150+dx).arg(C2,0,'f',3).arg(suffix2); + s += QString("\n").arg(3+(i-1)*Nc1).arg(250+dx).arg(C1,0,'f',3).arg(suffix1); + dx += 510; + } + + if (N1stOrd!=0) { + createFirstOrderComponentsHPF(s,Sections.last(),dx+10); + } + + s += "\n"; + s += "\n"; + dx = 0; + s += "<70 250 120 250 \"in\" 120 220 22 \"\">\n"; + s += "<70 250 70 300 \"\" 0 0 0 \"\">\n"; + for (int i=1; i<=N2ord; i++) { + if (i!=1) { + s += QString("<%1 250 %2 270 \"\" 0 0 0 \"\">\n").arg(120+dx).arg(120+dx); + s += QString("<%1 270 %2 270 \"\" 0 0 0 \"\">\n").arg(dx-40).arg(120+dx); + } + s += QString("<%1 250 %2 250 \"\" 0 0 0 \"\">\n").arg(180+dx).arg(200+dx); + s += QString("<%1 250 %2 250 \"\" 0 0 0 \"\">\n").arg(200+dx).arg(220+dx); + s += QString("<%1 210 %2 250 \"\" 0 0 0 \"\">\n").arg(200+dx).arg(200+dx); + s += QString("<%1 250 %2 320 \"\" 0 0 0 \"\">\n").arg(200+dx).arg(200+dx); + s += QString("<%1 290 %2 350 \"\" 0 0 0 \"\">\n").arg(360+dx).arg(360+dx); + s += QString("<%1 130 %2 150 \"\" 0 0 0 \"\">\n").arg(200+dx).arg(200+dx); + s += QString("<%1 130 %2 130 \"\" 0 0 0 \"\">\n").arg(200+dx).arg(320+dx); + s += QString("<%1 130 %2 150 \"\" 0 0 0 \"\">\n").arg(320+dx).arg(320+dx); + s += QString("<%1 250 %2 250 \"\" 0 0 0 \"\">\n").arg(280+dx).arg(320+dx); + s += QString("<%1 250 %2 250 \"\" 0 0 0 \"\">\n").arg(320+dx).arg(360+dx); + s += QString("<%1 210 %2 250 \"\" 0 0 0 \"\">\n").arg(320+dx).arg(320+dx); + s += QString("<%1 130 %2 130 \"\" 0 0 0 \"\">\n").arg(320+dx).arg(470+dx); + s += QString("<%1 270 %2 270 \"\" 0 0 0 \"\">\n").arg(430+dx).arg(470+dx); + if ((2*i)==order) { + s += QString("<%1 130 %2 270 \"out\" %3 170 70 \"\">\n").arg(470+dx).arg(470+dx).arg(500+dx); + } else { + s += QString("<%1 130 %2 270 \"\" 0 0 0 \"\">\n").arg(470+dx).arg(470+dx); + } + dx += 510; + } + + if (N1stOrd!=0) { + createFirstOrderWires(s,dx+10,270); + } + + s += "\n"; +} + +void MFBfilter::calcHighPass() +{ + float R1,R2,C1,C2; + float Wc = 2*M_PI*Fc; + + for (int k=1; k <= order/2; k++) { + float re = Poles.at(k-1).real(); + float im = Poles.at(k-1).imag(); + float B = -2.0*re; + float C = re*re + im*im; + + qDebug()<calcFirstOrder(); + +} + +void MFBfilter::calcLowPass() +{ + float R1,R2,R3,C1,C2; + float Wc = 2*M_PI*Fc; + + for (int k=1; k <= order/2; k++) { + float re = Poles.at(k-1).real(); + float im = Poles.at(k-1).imag(); + float B = -2.0*re; + float C = re*re + im*im; + + qDebug()<calcFirstOrder(); +} diff --git a/qucs/qucsactivefilter/mfbfilter.h b/qucs/qucsactivefilter/mfbfilter.h new file mode 100644 index 0000000000..1130398117 --- /dev/null +++ b/qucs/qucsactivefilter/mfbfilter.h @@ -0,0 +1,21 @@ +#ifndef MFBFILTER_H +#define MFBFILTER_H + +#include +#include +#include "filter.h" + +class MFBfilter : public Filter +{ +protected: + void calcHighPass(); + void calcLowPass(); + void createHighPassSchematic(QString &s); + void createLowPassSchematic(QString &s); + +public: + MFBfilter(Filter::FilterFunc ffunc_, Filter::FType type_, FilterParam par); + +}; + +#endif // MFBFILTER_H diff --git a/qucs/qucsactivefilter/qf_matrix.h b/qucs/qucsactivefilter/qf_matrix.h new file mode 100644 index 0000000000..0b4a3f87a4 --- /dev/null +++ b/qucs/qucsactivefilter/qf_matrix.h @@ -0,0 +1,50 @@ +/*************************************************************************** + qf_matrix.h + ---------------- + begin : Mon Jan 02 2006 + copyright : (C) 2006 by Stefan Jahn + email : stefan@lkcc.org + ***************************************************************************/ + +/*************************************************************************** + * * + * 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. * + * * + ***************************************************************************/ + +#ifndef _QF_MATRIX_H +#define _QF_MATRIX_H + +class qf_matrix +{ + public: + // constructor + qf_matrix (unsigned int d) { + data = (qf_double_t *) calloc (d * d, sizeof (qf_double_t)); + n = d; + } + + // destructor + ~qf_matrix () { + free (data); + } + + // accessor operators + qf_double_t operator () (int r, int c) const { + return data[r * n + c]; + } + qf_double_t & operator () (int r, int c) { + return data[r * n + c]; + } + + // size of matrix + unsigned int n; + + private: + qf_double_t * data; +}; + +#endif // _QF_MATRIX_H diff --git a/qucs/qucsactivefilter/qf_poly.cpp b/qucs/qucsactivefilter/qf_poly.cpp new file mode 100644 index 0000000000..80705da079 --- /dev/null +++ b/qucs/qucsactivefilter/qf_poly.cpp @@ -0,0 +1,1580 @@ +/*************************************************************************** + qf_poly.cpp + ---------------- + begin : Mon Jan 02 2006 + copyright : (C) 2006 by Vincent Habchi, F5RCS + email : 10.50@free.fr + ***************************************************************************/ + +/*************************************************************************** + * * + * 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. * + * * + ***************************************************************************/ + +// Class for polynoms with real coefficients (R[X]) +// Basic operations are covered +// It includes also an algorithm to find all the roots of a real +// polynom. + +#include +#include +#include +#include + +#undef _QF_POLY_DEBUG + +#include "qf_poly.h" + +// A polynom is essentially a structure with an order (max. index) +// and a table storing coefficients +qf_poly::qf_poly () : + rep (NONE), d (0), krts (0), p (NULL), rts (NULL) { +} + +// Creates default polynoms +qf_poly::qf_poly (unsigned o) : + rep (NONE), d (o), krts (0), p (NULL), rts (NULL) { +} + +// This function creates irreductible real polynoms +// That is either constants, monoms, or binoms +qf_poly::qf_poly (qf_double_t a, qf_double_t b, qf_double_t c, unsigned deg) { + +#ifdef _QF_POLY_DEBUG + std::cout << "qf_poly (ax^2+bx+c), a = " << a << ", b = " << b + << ", c = " << c << ", d = " << deg << "\n"; +#endif + + // Pathological cases + if ((deg == 2) && (a == 0)) { + deg = 1; + a = b; + b = c; + } + if ((deg == 1) && (a == 0)) { + deg = 0; + a = b; + } + + // Proceed with normal cases + switch (deg) { + case 0: + // Constant + d = 0; + p = new qf_double_t[1]; + p[0] = a; + rts = NULL; // no root (or an infinite #of them) + krts = a; + rep = BOTH; + break; + case 1: + // (aX + b) + d = 1; + p = new qf_double_t[2]; + p[0] = b; + p[1] = a; + rts = new qf_double_t[2]; + rts[0] = ROUND_ROOT (-b / a); + rts[1] = 0; + krts = a; + rep = BOTH; + break; + default: + // Polynom of d 2 (aX^2 + bX + c) + if (deg > 2) + std::cout << "Warning: qf_poly called with deg > 2.\n"; + d = 2; + p = new qf_double_t[3]; + p[0] = c; + p[1] = b; + p[2] = a; + rts = new qf_double_t[4]; + krts = a; + qf_double_t dlt = (b * b - 4 * a * c); + if (dlt == 0) { + // Double root (should not occur) + rts[0] = rts[2] = ROUND_ROOT (-b / (2 * a)); + rts[1] = rts[3] = 0; + } else if (dlt > 0) { + // Two real roots (should not occur) + rts[1] = rts[3] = 0; + rts[0] = ROUND_ROOT ((-b + sqrt (dlt)) / (2 * a)); + rts[2] = ROUND_ROOT (-(b + sqrt (dlt)) / (2 * a)); + } else { + // Two conjugate complex root (normal case) + rts[0] = rts[2] = ROUND_ROOT (-b / (2 * a)); + rts[1] = ROUND_ROOT (sqrt (-dlt) / (2 * a)); + rts[3] = -rts[1]; + } + rep = BOTH; + break; + } + +#ifdef _QF_POLY_DEBUG + std::cout << "qf_poly ax^2+bx+c: "; + this->disp ("prod"); +#endif +} + +// Creates a polynom and instantiates it out of a constant table +qf_poly::qf_poly (int o, const qf_double_t coef[]) : + rep (COEFF), d (o), krts (0), rts (NULL) { + + p = new qf_double_t[o + 1]; + for (int i = o; i >= 0; i--) p[i] = coef[o - i]; + +#ifdef _QF_POLY_DEBUG + std::cout << "qf_poly coeffs: "; + this->disp ("P"); +#endif + return; +} + +// Creates a polynom out of its roots and a constant factor +// The roots are complex numbers +// If a root is complex, then its conjugate is also a root +// since the coefficients are real. +qf_poly::qf_poly (int o, qf_double_t k, const qf_double_t r[]) : + rep (ROOTS), d (o), p (NULL) { + + rts = new qf_double_t[2 * o]; + for (int i = 0; i < 2 * o; i++) + rts[i] = ROUND_ROOT (r[i]); + krts = k; + +#ifdef _QF_POLY_DEBUG + std::cout << "qf_poly (roots): "; + this->disp ("P"); +#endif + return; +} + +// Copy constructor +qf_poly::qf_poly (const qf_poly & P) : + rep (P.rep), d (P.d), krts (0), p (NULL), rts (NULL) { + + if (rep & COEFF) { + p = new qf_double_t[d + 1]; + memcpy (p, P.p, sizeof (qf_double_t) * (d + 1)); + } + if (rep & ROOTS) { + rts = new qf_double_t[2 * d]; + memcpy (rts, P.rts, sizeof (qf_double_t) * 2 * d); + krts = P.krts; + } +} + +// Assignment operator +// Identical to previous +qf_poly & qf_poly::operator = (const qf_poly & P) { + if (&P == this) // Self copy, nothing to do + return (*this); + + d = P.d; + rep = P.rep; + + if (p != NULL) delete[] p; + if (rts != NULL) delete[] rts; + p = rts = NULL; + krts = 0; + + if (rep & COEFF) { + p = new qf_double_t[d + 1]; + memcpy (p, P.p, sizeof (qf_double_t) * (d + 1)); + } + if (rep & ROOTS) { + rts = new qf_double_t[2 * d]; + memcpy (rts, P.rts, sizeof (qf_double_t) * (2 * d)); + krts = P.krts; + } + return (*this); +} + +// Garbage bin +qf_poly::~qf_poly () { + if (p != NULL) delete[] p; + if (rts != NULL) delete[] rts; +} + +// Basic functions. + +// Access to the element of nth order +// [] overload +qf_double_t & qf_poly::operator [] (int i) { + if (rep == NONE) { + std::cout << "qf_poly::[] used on a NONE polynom.\n"; + exit (-1); + } + if (rep & COEFF) + return p[i]; + return rts[i]; +} + +// Returns d (order) of polynom +unsigned qf_poly::deg () { + return d; +} + +qf_double_t qf_poly::k () { + if (rep == NONE) { + std::cout << "qf_poly::k () used on a NONE polynom.\n"; + exit (-1); + } + if (rep & ROOTS) + return krts; + return p[d]; +} + +// Simplifies a polynom +// This function looks for the highest non-zero term and sets +// d accordingly, so that we do not perform useless operations on 0s +// Note that the unused 0s are not freed. We cannot do that at that +// time without copying, which is a ** overhead +// Useful after additions +void qf_poly::spl () { + int i = d; + + if (rep == NONE) { + std::cout << "qf_poly::spl () used on a NONE polynom.\n"; + exit (-1); + } + + if (d == 0) + return; + + if (rep == ROOTS) + return; + + // We scan from highest to lowest order + while (i > 0) { + if (p[i] == 0) + i--; + else + break; + } + d = i; + + return; +} + +// Arithmetical operations + +// Negates (Unary minus : P -> -P) +qf_poly qf_poly::operator - (void) { + if (rep == NONE) { + std::cout << "qf_poly::unary - used on a NONE polynom.\n"; + exit (-1); + } + + qf_poly R (d); + + if (rep & COEFF) { + R.p = new qf_double_t[d + 1]; + for (unsigned i = 0; i <= d; i++) + R.p[i] = -p[i]; + } + if (rep & ROOTS) { + R.rts = new qf_double_t[2 * d]; + memcpy (R.rts, rts, sizeof (qf_double_t) * 2 * d); + R.krts = -krts; + } + + R.rep = rep; + return R; +} + +// Addition +qf_poly operator + (qf_poly P, qf_poly Q) { + if ((Q.rep == NONE) || (P.rep == NONE)) { + std::cout << "qf_poly::+ used on a NONE polynom.\n"; + exit (-1); + } + + if (Q.d >= P.d) { + qf_poly R (Q); + return R += P; + } + else { + qf_poly R (P); + return R += Q; + } +} + +// Self-Addition +qf_poly qf_poly::operator += (qf_poly P) { + if ((rep == NONE) || (P.rep == NONE)) { + std::cout << "qf_poly::+= used on a NONE polynom.\n"; + exit (-1); + } + + // We cannot add two polynoms if one of them is under the ROOTS form + if (rep == ROOTS) + to_coeff (); + + // We add coefficients, not roots! + if (P.rep == ROOTS) + P.to_coeff (); + + if (d >= P.d) { + for (unsigned i = 0; i <= P.d; i++) + p[i] += P.p[i]; + } + else { + qf_double_t * pp = new qf_double_t[P.d]; + memcpy (pp, P.p, sizeof (qf_double_t) * P.d); + for (unsigned i = 0; i <= d; i++) + pp[i] += p[i]; + delete[] p; + p = pp; + } + + if (rep & ROOTS) { + rep = COEFF; // We must recompute roots if needed + delete[] rts; + rts = NULL; + krts = 0; + } + spl (); // Simplifies + return (*this); +} + +// Substraction +qf_poly operator - (qf_poly P, qf_poly Q) { + if ((P.rep == NONE) || (Q.rep == NONE)) { + std::cout << "qf_poly::- used on a NONE polynom.\n"; + exit (-1); + } + + if (P.d >= Q.d) { + qf_poly R (P); + return R -= Q; + } + else { + qf_poly R (Q); + return R -= P; + } +} + +// Self-Substraction +qf_poly qf_poly::operator -= (qf_poly P) { + if ((rep == NONE) || (P.rep == NONE)) { + std::cout << "qf_poly::-= used on a NONE polynom.\n"; + exit (-1); + } + + if (rep == ROOTS) + to_coeff (); + + if (P.rep == ROOTS) + P.to_coeff (); + + if (d >= P.d) { + for (unsigned i = 0; i <= P.d; i++) + p[i] -= P.p[i]; + } + else { + qf_double_t * pp = new qf_double_t[P.d + 1]; + memcpy (pp, P.p, sizeof (qf_double_t) * (P.d + 1)); + for (unsigned i = 0; i <= P.d; i++) + if (i <= d) + pp[i] = p[i] - pp[i]; + else + pp[i] = -pp[i]; + delete[] p; + p = pp; + } + + if (rep & ROOTS) { + rep = COEFF; // We must recompute roots if needed + delete[] rts; + rts = NULL; + krts = 0; + } + spl (); // Simplifies + return (*this); +} + +// Multiplication of two polynoms +qf_poly operator * (qf_poly P, qf_poly Q) { + if ((P.rep == NONE) || (Q.rep == NONE)) { + std::cout << "qf_poly::* used on a NONE polynom.\n"; + exit (-1); + } + + qf_poly R (P); + R *= Q; + return R; +} + +// Multiplication with a scalar +qf_poly operator * (qf_poly P, const qf_double_t m) { + if (P.rep == NONE) { + std::cout << "qf_poly::* (scalar) used on a NONE polynom.\n"; + exit (-1); + } + + qf_poly R (P); + R *= m; + return R; +} + +// Self-Multiply +qf_poly qf_poly::operator *= (qf_poly P) { + if ((rep == NONE) || (P.rep == NONE)) { + std::cout << "qf_poly::*= () used on a NONE polynom.\n"; + exit (-1); + } + + // Just a constant to multiply + if (P.d < 1) { + if (P.rep & COEFF) + return ((*this) *= P.p[0]); + else + return ((*this) *= P.krts); + } + + // Resizes the coefficient list + if (rep & COEFF) { + if (!(P.rep & COEFF)) P.to_coeff (); + qf_double_t * q = new qf_double_t[d + P.d + 1]; + memset (q, 0, sizeof (qf_double_t) * (d + P.d + 1)); + for (unsigned i = 0; i <= d; i++) + for (unsigned j = 0; j <= P.d; j++) + q[i + j] += p[i] * P.p[j]; + delete[] p; + p = q; + } + + // The roots are the concatenation of the roots of both polynoms + if (rep & ROOTS) { + if (!(P.rep & ROOTS)) P.to_roots (); + qf_double_t * rtsp = new qf_double_t[2 * (d + P.d)]; + memcpy (rtsp, rts, sizeof (qf_double_t) * 2 * d); + memcpy (&rtsp[2 * d], P.rts, sizeof (qf_double_t) * 2 * P.d); + delete[] rts; + rts = rtsp; + krts *= P.krts; + } + + d += P.d; + return (*this); +} + +// Self-Scalar-Multiply +qf_poly qf_poly::operator *= (const qf_double_t m) { + if (rep == NONE) { + std::cout << "qf_poly::*= (scalar) used on a NONE polynom.\n"; + exit (-1); + } + + if (m == 0) { + krts = d = 0; + delete[] rts; + delete[] p; + rts = p = NULL; + rep = COEFF; + return (*this); + } + + if (m == 1) + return (*this); + + if (rep & COEFF) + for (unsigned i = 0; i <= d; i++) + p[i] *= m; + + if (rep & ROOTS) + krts *= m; + + return (*this); +} + +// Test +bool qf_poly::operator == (qf_poly P) { + if (rep == NONE) + return false; + + // Two polynoms can be equal only if their degree is the same + if (d != P.d) + return false; + + // We can't compare two polynoms using the roots, because they can + // be stored in different order, and therefore the comparison would + // be cumbersome. It is shorter to translate the polynoms in COEFF + // form, then make a comparison of each coefficient + + if (rep == ROOTS) + to_coeff (); + + if (P.rep == ROOTS) + P.to_coeff (); + + for (unsigned i = 0; i <= d; i++) + if (p[i] != P.p[i]) + return false; + + return true; +} + +bool qf_poly::operator != (qf_poly P) { + return !((*this) == P); +} + +bool qf_poly::is_null (void) { + if (rep == NONE) { + std::cout << "Warning qf_poly::is_null() on a NONE polynom.\n"; + return true; + } + + if (d == 0) + return true; + + if (d > 1) + return false; + + if (rep & ROOTS) + return (krts == 0); + else + return (p[0] == 0); +} + +// Basic division by x^n == left shift n places +qf_poly qf_poly::operator << (unsigned n) { + if (rep == NONE) { + std::cout << "qf_poly::<< used on a NONE polynom.\n"; + exit (-1); + } + + if (n == 0) + return (*this); + + if (d < n) + return qf_poly (0, 0, 0, 0); // 0 + + else if (d == n) + return qf_poly (p[d], 0, 0, 0); // Q(x) = P(n) + + qf_poly R; + + if (rep & COEFF) { + for (unsigned i = 0; i < n; i++) + if (p[i] != 0) { + std::cout << "Warning: << by " << n << " asked for but only " + << i << " possible.\n"; + n = i; + } + + // Okay, proceed + R.p = new qf_double_t[d - n + 1]; + memcpy (R.p, &(p[n]), sizeof (qf_double_t) * (d - n + 1)); + R.d = d - n; + } + + if (rep & ROOTS) { + int nbz = n; + R.rts = new qf_double_t[2 * d]; + R.krts = krts; + + // Eliminates n zero roots + for (unsigned i = 0, j = 0; i < 2 * d; i += 2) { + if ((rts[i] == 0) && (rts[i + 1] == 0) && (nbz != 0)) + nbz--; + + else { + R.rts[j] = rts[i]; + R.rts[j + 1] = rts[i + 1]; + j += 2; + } + } + + R.d = d - n; + + // We did not found a sufficient number of zeros + if (nbz != 0) { + std::cout << "Warning: << by " << n << " asked for but only " + << n - nbz << " possible.\n"; + R.d += nbz; + } + } + + R.rep = rep; + return R; +} + +// Multiplies by x^n +qf_poly qf_poly::operator >> (unsigned n) { + if (rep == NONE) { + std::cout << "qf_poly::>> used on a NONE polynom.\n"; + exit (-1); + } + + if (n == 0) + return (*this); + + qf_poly R (d + n); + + if (rep & COEFF) { + R.p = new qf_double_t[d + n + 1]; + memset (R.p, 0, sizeof (qf_double_t) * n); + memcpy (&(R.p[n]), p, sizeof (qf_double_t) * (d + 1)); + } + + if (rep & ROOTS) { + R.rts = new qf_double_t[2 * (d + n)]; + memset (R.rts, 0, sizeof (qf_double_t) * 2 * n); // n times root = 0 + memcpy (&(R.rts[2 * n]), rts, sizeof (qf_double_t) * 2 * d); + R.krts = krts; + } + + R.rep = rep; + return R; +} + +// Creates the odd part of a polynom +qf_poly qf_poly::odd () { + qpr orep = rep; + + if (rep == NONE) { + std::cout << "qf_poly::odd () used on a NONE polynom.\n"; + exit (-1); + } + + if (rep == ROOTS) + to_coeff (); + + qf_poly P (*this); + + int i = d; + + if ((i % 2) == 1) + i--; + + for (; i >= 0; i -= 2) + P.p[i] = 0; + + P.spl (); + + if ((orep == ROOTS) || (orep == BOTH)) + P.to_roots (); + + return P; +} + +// Creates the even part of a polynom +qf_poly qf_poly::even () { + qpr orep = rep; + + if (rep == NONE) { + std::cout << "qf_poly::even () used on a NONE polynom.\n"; + exit (-1); + } + + + if (rep == ROOTS) + to_coeff (); + + qf_poly P (*this); + + int i = d; + + if (i == 0) + return P; + + if ((i % 2) == 0) + i--; + + for (; i >= 1; i -= 2) + P.p[i] = 0; + + P.spl (); + + if ((orep == ROOTS) || (orep == BOTH)) + P.to_roots (); + + return P; +} + +// computes P(-X) +qf_poly qf_poly::mnx () { + if (rep == NONE) { + std::cout << "qf_poly::mnx () used on a NONE polynom.\n"; + exit (-1); + } + + qf_poly P (d); + + if ((rep == COEFF) || (rep == BOTH)) { + P.p = new qf_double_t[d + 1]; + for (unsigned i = 0; i <= d; i++) + P.p[i] = ((i % 2) == 0 ? p[i] : -p[i]); + } + + if ((rep == ROOTS) || (rep == BOTH)) { + P.rts = new qf_double_t[2 * d]; + + for (unsigned i = 0; i < 2 * d; i++) + P.rts[i] = -rts[i]; + + P.krts = ((d % 2) == 0 ? krts : -krts); + } + + P.rep = rep; + return P; +} + +// "Half square" : P(X) * P(-X) +qf_poly qf_poly::hsq () { + if (rep == NONE) { + std::cout << "qf_poly::hsq () used on a NONE polynom.\n"; + exit (-1); + } + + qf_poly P (*this); + + P *= mnx (); + + return P; +} + +// Q(X) <- P(X^2) +qf_poly qf_poly::sqr () { + if (rep == NONE) { + std::cout << "qf_poly::sqr () used on a NONE polynom.\n"; + exit (-1); + } + + if (rep == ROOTS) + to_coeff (); + + qf_poly P (even ()); + + // Contains odd order terms + if ((*this) != P) { + std::cout << "Error! qf_poly::sqr () used on a non-square polynom.\n"; + exit (-1); + } + + qf_poly Q (d / 2); + + Q.p = new qf_double_t[d / 2 + 1]; + + for (unsigned i = 0; i <= d / 2; i++) + Q.p[i] = p[2 * i]; + + Q.rep = COEFF; + + if ((rep == BOTH) || (rep == ROOTS)) + Q.to_roots (); + + return Q; // Q(X) = P(X^2) +} + +// Eliminates a prime factor +void qf_poly::div (qf_double_t r, qf_double_t i) { + if (rep == NONE) { + std::cout << "qf_poly::div () used on a NONE polynom.\n"; + exit (-1); + } + + i = fabs (i); // Imaginary part must be > 0 + + // First examins pathological cases + + if (d == 0) { + std::cout << "Warning: Div () called on a constant polynom.\n"; + return; + } + + if ((d == 1) && (i != 0)) { + std::cout << "Div () real/complex error.\n"; + return; + } + + if (d == 1) { + if (((rep == ROOTS) || (rep == BOTH)) + && (fabs (rts[0] - r) < ROOT_TOL) + && (fabs (rts[1]) < ROOT_TOL)) { + d = 0; + delete[] rts; + rts = NULL; + delete[] p; + p = new qf_double_t[1]; + p[0] = krts; + rep = BOTH; + return; + } + + if ((rep == COEFF) && (fabs (p[0] / p[1] + r) < ROOT_TOL)) { + qf_double_t temp = p[1]; + d = 0; + delete[] p; + p = new qf_double_t[1]; + p[0] = temp; + delete[] rts; + krts = temp; + rep = BOTH; + return; + } + + std::cout << "Warning: Div () error. Specified factor not found.\n"; + return; + } + + // Proceed to general case. + // If i = 0, we divide by (X - r) + // If i != 0, we divide by (X^2 - 2rX + r^2+i^2), that is to say + // by (X - (r + iI))(X - (r - iI)) where I^2 = -1 + if (rep == COEFF) + to_roots (); + + qf_double_t * rtsp = new qf_double_t[2 * d]; + + bool found = false; + + for (unsigned k = 0, j = 0; k < 2 * d; k += 2) { +#ifdef _QF_POLY_DEBUG + std::cout << "Div: " << fabs (rts[k] - r) << " " + << fabs (rts[k + 1] - i) << "\n"; +#endif + + if ((fabs (rts[k] - r) > ROOT_TOL) || (fabs (rts[k + 1] - i) > ROOT_TOL)) { + rtsp[j] = rts[k]; + rtsp[j + 1] = rts[k + 1]; + j += 2; + } + + else { + if (i != 0) + // If complex root, eliminates also next root (conjugate) + k += 2; + + found = true; + } + } + + if (!found) { + delete[] rtsp; + std::cout << "Div () : factor not found! \n"; + return; + } + + delete[] rts; + rts = rtsp; + + if (i == 0) + d--; + else + d -= 2; + + rep = ROOTS; +} + +// Simplifies polynoms : eliminates common roots +void smpf (qf_poly & N, qf_poly & D) { + unsigned dN = N.d; + unsigned dD = D.d; + unsigned i, j; + + std::cout << "dN: " << dN << " dD : " << dD << '\n'; + + bool * Ln = new bool[dN]; + bool * Ld = new bool[dD]; + + // Init + for (i = 0; i < dN; i++) + Ln[i] = true; + + for (i = 0; i < dD; i++) + Ld[i] = true; + + if (N.rep == COEFF) + N.to_roots (); + + if (D.rep == COEFF) + D.to_roots (); + + // Seek for common roots + + unsigned ndN = dN; + unsigned ndD = dD; + + for (i = 0; i < 2 * dN; i += 2) { + for (j = 0; j < 2 * dD; j += 2) { + std::cout << "N.rts[" << i << "] = " << N.rts[i] << ", "; + std::cout << "D.rts[" << j << "] = " << D.rts[j] << "\n"; + std::cout << "N.rts[" << i + 1 << "] = " << N.rts[i + 1] << ", "; + std::cout << "D.rts[" << j + 1 << "] = " << D.rts[j + 1] << "\n"; + if ((Ld[j / 2]) && + (fabs (N.rts[i] - D.rts[j]) < ROOT_TOL) && + (fabs (N.rts[i + 1] - D.rts[j + 1]) < ROOT_TOL)) { + Ln[i / 2] = false; // Eliminates both roots + Ld[j / 2] = false; + ndN--; + ndD--; + std::cout << "Common root: (" << D.rts[j] + << ", " << D.rts[j + 1] << "i)\n"; + break; // Direct to next root + } + } + } + + if (ndN != dN) { // We have simplified sth + qf_double_t * nrN = new qf_double_t[2 * ndN]; + qf_double_t * nrD = new qf_double_t[2 * ndD]; + + for (i = 0, j = 0; i < 2 * dN; i += 2) { + if (Ln[i / 2]) { // Non common root + nrN[j] = N.rts[i]; + nrN[j + 1] = N.rts[i + 1]; + j += 2; + } + } + + delete[] N.rts; + N.rts = nrN; + N.d = ndN; + N.rep = ROOTS; + + for (i = 0, j = 0; i < 2 * D.d; i += 2) { + if (Ld[i / 2]) { // Non common root + nrD[j] = D.rts[i]; + nrD[j + 1] = D.rts[i + 1]; + j += 2; + } + } + + delete[] D.rts; + D.rts = nrD; + D.d = ndD; + D.rep = ROOTS; + + N.to_coeff (); + D.to_coeff (); + std::cout << "ndN: " << ndN << " ndD : " << ndD << '\n'; + } + delete[] Ln; + delete[] Ld; +} + +// Hurwitzes a polynom. That is to say, eliminate its roots whose real part +// is positive +void qf_poly::hurw () { + if (rep == NONE) { + std::cout << "qf_poly::hurw () used on a NONE polynom.\n"; + return; + } + + if (rep == COEFF) + to_roots (); + + qf_double_t * rtsp = new qf_double_t[2 * d]; + unsigned j = 0; + + for (unsigned i = 0; i < 2 * d; i += 2) { + if (rts[i] > 0) + if (rts[i + 1] == 0) // Real positive root + continue; + + else { + i += 2; // Skips conjugate + continue; + } + + else { + rtsp[j] = rts[i]; + rtsp[j + 1] = rts[i + 1]; + j += 2; + } + } + + delete[] rts; + rts = rtsp; + d = j / 2; + + if (krts < 0) + krts = -krts; + + rep = ROOTS; +} + +// Evaluates a polynom. Computes P(a) for real a +qf_double_t qf_poly::eval (qf_double_t a) { + if (rep == NONE) { + std::cout << "qf_poly::eval () used on a NONE polynom.\n"; + return 0; + } + + if ((rep == COEFF) || (rep == BOTH)) { + + if (d == 0) + return p[0]; // Constant + + qf_double_t r = p[d]; + + for (int i = d - 1; i >= 0; i--) + r = r * a + p[i]; + + return r; + } + // ROOTS form : P (a) = k prod (a - r[i]) + if (d == 0) + return krts; + + qf_double_t r = krts; + + for (unsigned i = 0; i < 2 * d; i += 2) { + if (rts[i + 1] == 0) // Real root + r *= (a - rts[i]); + + else { + qf_double_t m = rts[i] * rts[i] + rts[i + 1] * rts[i + 1]; + qf_double_t n = -2 * rts[i]; + + r *= (a * a + n * a + m); + i += 2; // Skips conjugate (following root) + } + } + + return r; +} + +// Evaluates a polynom P(X^2) for X^2 = c (c can be negative) +qf_double_t qf_poly::evalX2 (qf_double_t c) { + return (sqr ()).eval (c); +} + +#ifdef _QF_POLY_DEBUG +// Pretty prints a polynom +void qf_poly::disp (const char *name) { + + if (rep == NONE) { + std::cout << name << "(x) is not initalized.\n"; + return; + } + + if ((rep == COEFF) || (rep == BOTH)) { + std::cout << name << "(x) = "; + disp_c (); + } + + if ((rep == ROOTS) || (rep == BOTH)) { + std::cout << name << "(x) = "; + disp_r (); + } +} +#else +void qf_poly::disp (const char *) { } +#endif // _QF_POLY_DEBUG + +void qf_poly::disp_c (void) { + if (d == 0) { + std::cout << p[0] << '\n'; + return; + } + + if (p[d] < 0) + std::cout << "-"; + + if (fabs (p[d]) != 1) + std::cout << fabs (p[d]); + + if (d == 1) { + std::cout << " x "; + } + + else { + std::cout << " x^" << d << ' '; + + for (unsigned i = d - 1; i > 1; i--) { + if (p[i] == 0) // Null monome + continue; + + if (p[i] > 0) + std::cout << "+ "; + else + std::cout << "- "; + + if (fabs (p[i]) != 1) + std::cout << fabs (p[i]); + + std::cout << " x^" << i << ' '; + } + + if (p[1] != 0) { + if (p[1] > 0) + std::cout << "+ "; + else + std::cout << "- "; + + if (fabs (p[1]) != 1) + std::cout << fabs (p[1]); + + std::cout << " x "; + } + } + + if (p[0] != 0) { + if (p[0] > 0) + std::cout << "+ "; + else + std::cout << "- "; + + std::cout << fabs (p[0]); + + } + std::cout << '\n'; +} + +void qf_poly::disp_r (void) { + if (krts == -1) + std::cout << "- "; + + else if (krts != 1) + std::cout << krts << ' '; + + for (unsigned i = 0; i < 2 * d; i += 2) { + if (rts[i + 1] == 0) { // Real root + std::cout << "(X"; + + if (rts[i] == 0) + std::cout << ") "; + + else if (rts[i] < 0) + std::cout << " + " << fabs (rts[i]) << ") "; + + else + std::cout << " - " << rts[i] << ") "; + } + else { // Complex conjugate root + std::cout << "(X^2 "; + + qf_double_t m = rts[i] * rts[i] + rts[i + 1] * rts[i + 1]; + qf_double_t n = 2 * rts[i]; + + if (n > 0) + std::cout << "- " << n << "X "; + + if (n < 0) + std::cout << "+ " << fabs (n) << "X "; + + std::cout << "+ " << m << ") "; + + i += 2; // Skips next root (conjugate of present one) + } + } + std::cout << '\n'; +} + +/* This function calculates P(X) = sum (a[i] * X^i) (sum form) out of + the roots (product form) P(X) = k * prod (X - r[i]) */ +void qf_poly::to_coeff (void) { + if (rep == NONE) { + std::cout << "qf_poly::to_coeff () used on a NONE polynom.\n"; + exit (-1); + } + + if ((rep == COEFF) || (rep == BOTH)) + return; + + if (p != NULL) + delete[] p; + + rep = BOTH; + + p = new qf_double_t[1]; + p[0] = krts; + + if ((rts == NULL) || (d == 0)) + return; + + if (d == 1) { + p = new qf_double_t[2]; + p[0] = -krts * rts[0]; + p[1] = krts; + return; + } + + unsigned r = 0; + + do { + if (rts[2 * r + 1] == 0) { // Real root + qf_double_t *q = new qf_double_t[r + 2]; + + q[0] = 0; + memcpy (&(q[1]), p, sizeof (qf_double_t) * (r + 1)); // Q(X) = XP(X) + + for (unsigned j = 0; j < r + 1; j++) + q[j] -= rts[2 * r] * p[j]; // Q(X) -= r[i]P(X) + + delete[] p; + p = q; + r++; + } + + else { // Complex conjugate root + qf_double_t m, n; + qf_double_t *q = new qf_double_t[r + 3]; + qf_double_t *s = new qf_double_t[r + 2]; + + m = rts[2 * r] * rts[2 * r] + rts[2 * r + 1] * rts[2 * r + 1]; + n = -2 * rts[2 * r]; + + q[0] = q[1] = s[0] = 0; + + memcpy (&(q[2]), p, sizeof (qf_double_t) * (r + 1)); // Q(X) = X^2P(X) + memcpy (&(s[1]), p, sizeof (qf_double_t) * (r + 1)); // S(X) = XP(X) + + for (unsigned j = 0; j < r + 1; j++) // Q(X) = (X^2 + nX + m) P(X) + q[j] += n * s[j] + m * p[j]; + + q[r + 1] += n * s[r + 1]; + + delete[] p; + delete[] s; + p = q; + r += 2; + } + } + while (r < d); + + (*this).disp ("qf_poly::to_coeff: "); +} + +/* The function finds the complex roots of the polynom given by: + p(x) = a_{n-1} * x^{n-1} + ... a_{2} * x^{2} + a_{1} * x + a_{0} + The results are stored in the vector rst, real part followed by + imaginary part for each complex root. It return zero on success + and non-zero otherwise. */ +void qf_poly::to_roots (void) { + if (rep == NONE) { + std::cout << "qf_poly::to_roots () used on a NONE polynom.\n"; + exit (-1); + } + + int status; + + if ((rep == ROOTS) || (rep == BOTH)) + return; // Nothing to do + + if (d == 0) + // cannot solve for only one term + return; + + qf_matrix m (d); + + if (rts != NULL) + delete[] rts; + + rts = new qf_double_t[2 * d]; + + krts = p[d]; + + qf_scm (m); + qf_bcm (m); + status = qf_qrc (m, rts); + + // root solving qr method failed to converge + for (unsigned i = 0; i < 2 * d; i++) { + if (fabs (rts[i]) <= ROOT_PREC) + rts[i] = 0; + else + rts[i] = ROUND_ROOT (rts[i]); +// std::cout << "root(" << i/2 << ") = " << rts [i] << +// " + " << rts [i+1] << " i\n" ; + } + + rep = BOTH; +} + +// Private functions used by qf_poly::solve + +// Set companion matrix. +void qf_poly::qf_scm (qf_matrix & m) { + unsigned int i, j; + for (i = 0; i < m.n; i++) + for (j = 0; j < m.n; j++) + m (i, j) = 0; + + for (i = 1; i < m.n; i++) + m (i, i - 1) = 1; + + for (i = 0; i < m.n; i++) + m (i, m.n - 1) = -p[i] / p[m.n]; +} + +// Balance companion matrix +void qf_poly::qf_bcm (qf_matrix & m) { + int not_converged = 1; + qf_double_t row_norm = 0; + qf_double_t col_norm = 0; + + while (not_converged) { + unsigned int i, j; + qf_double_t g, f, s; + + not_converged = 0; + + for (i = 0; i < m.n; i++) { + /* column norm, excluding the diagonal */ + if (i != m.n - 1) { + col_norm = fabs (m (i + 1, i)); + } + + else { + col_norm = 0; + + for (j = 0; j < m.n - 1; j++) { + col_norm += fabs (m (j, m.n - 1)); + } + } + + /* row norm, excluding the diagonal */ + if (i == 0) { + row_norm = fabs (m (0, m.n - 1)); + } + + else if (i == m.n - 1) { + row_norm = fabs (m (i, i - 1)); + } + + else { + row_norm = fabs (m (i, i - 1)) + fabs (m (i, m.n - 1)); + } + + if ((col_norm == 0) || (row_norm == 0)) { + continue; + } + + g = row_norm / RADIX; + f = 1; + s = col_norm + row_norm; + + while (col_norm < g) { + f *= RADIX; + col_norm *= RADIX2; + } + + g = row_norm * RADIX; + + while (col_norm > g) { + f /= RADIX; + col_norm /= RADIX2; + } + + if ((row_norm + col_norm) < 0.95 * s * f) { + not_converged = 1; + g = 1 / f; + if (i == 0) { + m (0, m.n - 1) *= g; + } + + else { + m (i, i - 1) *= g; + m (i, m.n - 1) *= g; + } + + if (i == m.n - 1) { + for (j = 0; j < m.n; j++) { + m (j, i) *= f; + } + } + else { + m (i + 1, i) *= f; + } + } + } + } +} + +// Root solver using QR method. +int qf_poly::qf_qrc (qf_matrix & h, qf_double_t * zroot) { + qf_double_t t = 0; + unsigned int iterations, e, i, j, k, m; + qf_double_t w, x, y, s, z; + qf_double_t p = 0, q = 0, r = 0; + int notlast; + unsigned int n = d; + +next_root: + if (n == 0) + return 0; + iterations = 0; + +next_iteration: + for (e = n; e >= 2; e--) { + qf_double_t a1 = fabs (h (e - 1, e - 2)); + qf_double_t a2 = fabs (h (e - 2, e - 2)); + qf_double_t a3 = fabs (h (e - 1, e - 1)); + + if (a1 <= EPSILON * (a2 + a3)) + break; + } + + x = h (n - 1, n - 1); + + if (e == n) { + SET_COMPLEX_PACKED (zroot, n - 1, x + t, 0); /* one real root */ + n--; + goto next_root; + } + + y = h (n - 2, n - 2); + w = h (n - 2, n - 1) * h (n - 1, n - 2); + + if (e == n - 1) { + p = (y - x) / 2; + q = p * p + w; + y = sqrt (fabs (q)); + x += t; + + if (q > 0) { /* two real roots */ + if (p < 0) + y = -y; + + y += p; + SET_COMPLEX_PACKED (zroot, n - 1, x - w / y, 0); + SET_COMPLEX_PACKED (zroot, n - 2, x + y, 0); + } + + else { + SET_COMPLEX_PACKED (zroot, n - 1, x + p, -y); + SET_COMPLEX_PACKED (zroot, n - 2, x + p, y); + } + + n -= 2; + goto next_root; + } + + /* No more roots found yet, do another iteration */ + if (iterations == MAX_ITERATIONS) { /* increased from 30 to 60 */ + /* too many iterations - give up! */ + return -1; + } + + if (iterations % 10 == 0 && iterations > 0) { + /* use an exceptional shift */ + t += x; + + for (i = 0; i < n; i++) { + h (i, i) -= x; + } + + s = fabs (h (n - 1, n - 2)) + fabs (h (n - 2, n - 3)); + y = 0.75 * s; + x = y; + w = -0.4375 * s * s; + } + + iterations++; + + for (m = n - 2; m >= e; m--) { + qf_double_t a1, a2, a3; + + z = h (m - 1, m - 1); + r = x - z; + s = y - z; + p = h (m - 1, m) + (r * s - w) / h (m, m - 1); + q = h (m, m) - z - r - s; + r = h (m + 1, m); + s = fabs (p) + fabs (q) + fabs (r); + p /= s; + q /= s; + r /= s; + + if (m == e) + break; + + a1 = fabs (h (m - 1, m - 2)); + a2 = fabs (h (m - 2, m - 2)); + a3 = fabs (h (m, m)); + + if (a1 * (fabs (q) + fabs (r)) <= EPSILON * fabs (p) * (a2 + a3)) + break; + } + + for (i = m + 2; i <= n; i++) { + h (i - 1, i - 3) = 0; + } + for (i = m + 3; i <= n; i++) { + h (i - 1, i - 4) = 0; + } + + /* double QR step */ + for (k = m; k <= n - 1; k++) { + notlast = (k != n - 1); + + if (k != m) { + p = h (k - 1, k - 2); + q = h (k, k - 2); + r = notlast ? h (k + 1, k - 2) : 0; + x = fabs (p) + fabs (q) + fabs (r); + + if (x == 0) + continue; /* FIXME????? */ + + p /= x; + q /= x; + r /= x; + } + + s = sqrt (p * p + q * q + r * r); + + if (p < 0) + s = -s; + + if (k != m) { + h (k - 1, k - 2) = -s * x; + } else if (e != m) { + h (k - 1, k - 2) *= -1; + } + + p += s; + x = p / s; + y = q / s; + z = r / s; + q /= p; + r /= p; + + /* do row modifications */ + for (j = k; j <= n; j++) { + p = h (k - 1, j - 1) + q * h (k, j - 1); + + if (notlast) { + p += r * h (k + 1, j - 1); + h (k + 1, j - 1) -= p * z; + } + + h (k, j - 1) -= p * y; + h (k - 1, j - 1) -= p * x; + } + j = (k + 3 < n) ? (k + 3) : n; + + /* do column modifications */ + for (i = e; i <= j; i++) { + p = x * h (i - 1, k - 1) + y * h (i - 1, k); + + if (notlast) { + p += z * h (i - 1, k + 1); + h (i - 1, k + 1) -= p * r; + } + + h (i - 1, k) -= p * q; + h (i - 1, k - 1) -= p; + } + } + + goto next_iteration; +} + + +void qf_poly::roots_to_complex(QVector< std::complex > &roots) +{ + roots.clear(); + for (unsigned int i=0;i(rts[2*i],rts[2*i+1])); + } +} diff --git a/qucs/qucsactivefilter/qf_poly.h b/qucs/qucsactivefilter/qf_poly.h new file mode 100644 index 0000000000..06e61146dd --- /dev/null +++ b/qucs/qucsactivefilter/qf_poly.h @@ -0,0 +1,142 @@ +/*************************************************************************** + qf_poly.h + ---------------- + begin : Mon Jan 02 2006 + copyright : (C) 2006 by Vincent Habchi, F5RCS + email : 10.50@free.fr + ***************************************************************************/ + +/*************************************************************************** + * * + * 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. * + * * + ***************************************************************************/ +#include +#include + +#ifndef _QF_POLY_H +#define _QF_POLY_H + +/* Headers for R[X] arithmetic */ + +#define qf_double_t long double + +#include "qf_matrix.h" + +// A polynom can be described either by a product of monoms equal to +// (x - r[i]) where r[i] is the ith root and a constant factor, or by +// the classical series of coefficient a[0]...a[n], or both. + +enum qf_poly_rep +{ + NONE = 0, // Not initialized + ROOTS = 1, // P(X) = k * prod (x - r[i]) + COEFF = 2, // P(X) = sum (a[i] * x^i) + BOTH = 3 // Both have been computed +}; + +typedef enum qf_poly_rep qpr; + +class qf_poly +{ + private: + qpr rep; // Type of representation + unsigned d; // Current degree + qf_double_t krts; // Constant k + qf_double_t * p; // Table of coefficients + qf_double_t * rts; // Table of complex roots + + // Functions used by solve + void qf_bcm (qf_matrix &); + int qf_qrc (qf_matrix &, qf_double_t *); + void qf_scm (qf_matrix &); + + public: + qf_poly (); + qf_poly (unsigned); // id with d + qf_poly (qf_double_t, qf_double_t, qf_double_t, unsigned); // Up to d=2 + qf_poly (int, const qf_double_t[]); // Id, with inst. + qf_poly (int, qf_double_t, const qf_double_t[]); + qf_poly (const qf_poly &); // Copy + ~qf_poly (); + + // access operators + qf_poly & operator = (const qf_poly &); // P = Q + qf_double_t & operator [] (int i); // Access to element + + // arithmetic operators + qf_poly operator - (void); // Unary - + + friend qf_poly operator + (qf_poly, qf_poly); + friend qf_poly operator - (qf_poly, qf_poly); + friend qf_poly operator * (qf_poly, qf_poly); + friend qf_poly operator * (qf_poly, const qf_double_t); + + qf_poly operator += (qf_poly); + qf_poly operator -= (qf_poly); + qf_poly operator *= (qf_poly); // P(X) = P(X)*Q(X) + qf_poly operator *= (const qf_double_t); + + qf_poly operator << (unsigned); // Basic div by X^n + qf_poly operator >> (unsigned); // Multiply by X^n + + bool operator == (qf_poly); // Test + bool operator != (qf_poly); // Test + bool is_null (void); + + unsigned deg (void); // Degree of poly + void spl (void); // Simplify + qf_poly odd (void); // Odd part + qf_poly even (void); // Even part + qf_poly mnx (void); // P(X) -> P(-X) + qf_poly hsq (void); // P(X)*P(-X) + qf_poly sqr (void); // Q(X) = P(X^2) + qf_double_t eval (qf_double_t); // P(X = a) + qf_double_t evalX2 (qf_double_t); // P(X^2 = a) + + void to_roots (void); // Solves + qf_double_t k (void); // Return krts factor + void to_coeff (void); // Calculate normal form + void div (qf_double_t, qf_double_t); // Simple division + void hurw (void); // "Hurwitzes" polynom + + void disp (const char *); // Prints P(X) + void disp_c (void); + void disp_r (void); + + void roots_to_complex(QVector< std::complex > &roots); + + friend void smpf (qf_poly &, qf_poly &); // Simplify +}; + +// For solve, we need some gibber + +// Save complex value elements +#define SET_COMPLEX_PACKED(zp,n,r,i) \ + *((zp)+2*(n)+0)=(r); *((zp)+2*(n)+1)=(i); + +// Some constants + +// IEEE long precision 2^{-52} +// # define EPSILON 2.2204460492503131e-16 +// IEEE double long precision 2^{-63} +#define EPSILON 1.0842021724855044e-19 +#define ROOT_PREC 1e-9 +#define ROOT_TOL 1e-7 + +inline qf_double_t ROUND_ROOT (qf_double_t k) { + if (k > 0) + return floor (k / ROOT_PREC) * ROOT_PREC; + else + return ceil (k / ROOT_PREC) * ROOT_PREC; +} + +#define RADIX 2 +#define RADIX2 (RADIX*RADIX) +#define MAX_ITERATIONS 60 + +#endif // _QF_POLY_H + diff --git a/qucs/qucsactivefilter/qucsactivefilter.cpp b/qucs/qucsactivefilter/qucsactivefilter.cpp new file mode 100644 index 0000000000..beed449f9f --- /dev/null +++ b/qucs/qucsactivefilter/qucsactivefilter.cpp @@ -0,0 +1,429 @@ +#include "qucsactivefilter.h" +#include "sallenkey.h" +#include "mfbfilter.h" +#include "schcauer.h" +#include "transferfuncdialog.h" +#include + + +QucsActiveFilter::QucsActiveFilter(QWidget *parent) + : QMainWindow(parent) +{ + Nfil = 4; + Fc = 1000; + ftyp = Filter::NoFilter; + + QTextCodec::setCodecForTr(QTextCodec::codecForName("UTF-8")); + + //lblInputData = new QLabel(tr("Входные данные")); + lblA1 = new QLabel(tr("Passband attenuation, Ap (dB)")); + lblA2 = new QLabel(tr("Stopband attenuation, As (dB)")); + lblF1 = new QLabel(tr("Cuttof frequency, Fc (Hz)")); + lblF2 = new QLabel(tr("Stopband frequency, Fs (Hz)")); + lblRpl1 = new QLabel(tr("Passband ripple Rp(dB)")); + //lblRpl2 = new QLabel(tr("Stopband ripple (dB)")); + lblKv = new QLabel(tr("Passband gain, Kv (dB)")); + + + edtA1 = new QLineEdit("3"); + QDoubleValidator *val1 = new QDoubleValidator(0,100000,3); + edtA1->setValidator(val1); + edtA2 = new QLineEdit("20"); + edtA2->setValidator(val1); + edtF1 = new QLineEdit("1000"); + edtF1->setValidator(val1); + edtF2 = new QLineEdit("1200"); + edtF2->setValidator(val1); + edtPassbRpl = new QLineEdit("3"); + edtPassbRpl->setValidator(val1); + //edtStopbRpl = new QLineEdit("3"); + //edtStopbRpl->setValidator(val1); + edtKv = new QLineEdit("0"); + edtKv->setValidator(val1); + QIntValidator *val2 = new QIntValidator(2,20); + lblOrder = new QLabel(tr("Filter order")); + edtOrder = new QLineEdit("5"); + edtOrder->setValidator(val2); + + lblTyp = new QLabel(tr("Approximation type:")); + cbxFilterFunc = new QComboBox; + QStringList lst2; + lst2<addItems(lst2); + connect(cbxFilterFunc,SIGNAL(currentIndexChanged(int)),this,SLOT(slotSwitchParameters())); + + btnDefineTransferFunc = new QPushButton(tr("Manually define transfer function")); + btnDefineTransferFunc->setEnabled(false); + connect(btnDefineTransferFunc,SIGNAL(clicked()),this,SLOT(slotDefineTransferFunc())); + + btnCalcSchematic = new QPushButton(tr("Calculate and copy to clipboard")); + connect(btnCalcSchematic,SIGNAL(clicked()),SLOT(slotCalcSchematic())); + + lblResult = new QLabel(tr("Calculation console")); + txtResult = new QTextEdit; + + + lblSch = new QLabel(tr("Filter topology")); + lblResp = new QLabel(tr("Filter type:")); + cbxResponse = new QComboBox; + QStringList lst3; + lst3<addItems(lst3); + connect(cbxResponse,SIGNAL(currentIndexChanged(int)),this,SLOT(slotUpdateResponse())); + connect(cbxResponse,SIGNAL(currentIndexChanged(int)),this,SLOT(slotUpdateSchematic())); + + cbxFilterType = new QComboBox; + QStringList lst; + lst<addItems(lst); + connect(cbxFilterType,SIGNAL(currentIndexChanged(int)),this,SLOT(slotUpdateSchematic())); + this->slotSwitchParameters(); + + lblAFR = new QLabel(tr("General amplitude frequency response")); + lblTopology = new QLabel(tr("Filter topology preview (one stage)")); + + QString s1 = ":/images/AFR.svg"; + QSvgRenderer *ren = new QSvgRenderer(s1); + QSize sz = ren->defaultSize(); + sz *= 1.1; + delete ren; + imgAFR = new QSvgWidget(s1); + imgAFR->setFixedSize(sz); + imgAFR->show(); + + s1 = ":/images/cauer.svg"; + ren = new QSvgRenderer(s1); + sz = ren->defaultSize(); + sz *= 0.65; + delete ren; + + sch_pic = new QSvgWidget(s1); + sch_pic->setFixedSize(sz); + sch_pic->show(); + + top = new QHBoxLayout; + left = new QVBoxLayout; + center = new QVBoxLayout; + right = new QVBoxLayout; + + //left->addWidget(lblInputData); + left->addWidget(lblA1); + left->addWidget(edtA1); + left->addWidget(lblA2); + left->addWidget(edtA2); + left->addWidget(lblF1); + left->addWidget(edtF1); + left->addWidget(lblF2); + left->addWidget(edtF2); + left->addWidget(lblRpl1); + left->addWidget(edtPassbRpl); + left->addWidget(lblKv); + left->addWidget(edtKv); + left->addWidget(lblOrder); + left->addWidget(edtOrder); + QHBoxLayout *l3 = new QHBoxLayout; + l3->addWidget(lblTyp); + l3->addWidget(cbxFilterFunc); + left->addLayout(l3); + + left->addWidget(btnDefineTransferFunc); + + QHBoxLayout *l1 = new QHBoxLayout; + l1->addWidget(lblResp); + l1->addWidget(cbxResponse); + left->addLayout(l1); + QHBoxLayout *l2 = new QHBoxLayout; + l2->addWidget(lblSch); + l2->addWidget(cbxFilterType); + left->addLayout(l2); + left->addWidget(btnCalcSchematic); + left->addStretch(); + + right->addWidget(lblAFR); + right->addWidget(imgAFR); + right->addWidget(lblTopology); + right->addWidget(sch_pic); + right->addStretch(); + + top->addLayout(left); + top->addLayout(center); + top->addLayout(right); + + top1 = new QVBoxLayout; + top1->addLayout(top); + QSplitter *sp1 = new QSplitter; + top1->addWidget(sp1); + txtResult->setReadOnly(true); + top1->addWidget(txtResult); + txtResult->setMinimumHeight(180); + + zenter = new QWidget; + this->setCentralWidget(zenter); + zenter->setLayout(top1); +} + +QucsActiveFilter::~QucsActiveFilter() +{ + +} + + +void QucsActiveFilter::slotCalcSchematic() +{ + + FilterParam par; + par.Ap = edtA1->text().toFloat(); + par.As = edtA2->text().toFloat(); + par.Rp = edtPassbRpl->text().toFloat(); + par.Fc = edtF1->text().toFloat(); + par.Fs = edtF2->text().toFloat(); + float G = edtKv->text().toFloat(); + par.Kv = pow(10,G/20.0); + + QStringList lst; + Filter::FilterFunc ffunc; + + switch (cbxFilterFunc->currentIndex()) { + case 0 : ffunc = Filter::Butterworth; + break; + case 1 : ffunc = Filter::Chebyshev; + break; + case 2 : ffunc = Filter::InvChebyshev; + break; + case 3 : ffunc = Filter::Cauer; + break; + case 4 : ffunc = Filter::Bessel; + par.order = edtOrder->text().toInt(); + break; + case 5 : ffunc = Filter::User; + break; + default: ffunc = Filter::NoFunc; + break; + } + + + + switch (cbxResponse->currentIndex()) { + case 0 : ftyp = Filter::LowPass; + break; + case 1 : ftyp = Filter::HighPass; + break; + case 2 : ftyp = Filter::BandPass; + break; + case 3 : ftyp = Filter::BandStop; + break; + default: ftyp = Filter::NoFilter; + break; + } + + QString s; + bool ok = false; + + switch (cbxFilterType->currentIndex()) { + case 0 : { + if (((ffunc==Filter::InvChebyshev)||(ffunc==Filter::Cauer))) { + SchCauer cauer(ffunc,ftyp,par); + ok = cauer.calcFilter(); + cauer.createPolesZerosList(lst); + cauer.createPartList(lst); + txtResult->setText(lst.join("\n")); + if (ok) { + cauer.createSchematic(s); + } else { + errorMessage(tr("Unable to implement filter with such parameters and topology \n" + "Change parapeters and/or topology and try again!")); + } + } else { + errorMessage(tr("Unable to use Cauer section for Chebyshev or Butterworth \n" + "frequency response. Try to use another topology.")); + } + } + + break; + case 1 : { + if (!((ffunc==Filter::InvChebyshev)||(ffunc==Filter::Cauer))) { + MFBfilter mfb(ffunc,ftyp,par); + if (ffunc==Filter::User) { + mfb.set_TrFunc(coeffA,coeffB); + } + ok = mfb.calcFilter(); + mfb.createPolesZerosList(lst); + mfb.createPartList(lst); + txtResult->setText(lst.join("\n")); + if (ok) { + mfb.createSchematic(s); + } else { + errorMessage(tr("Unable to implement filter with such parameters and topology \n" + "Change parapeters and/or topology and try again!")); + } + } else { + errorMessage(tr("Unable to use MFB filter for Cauer or Inverse Chebyshev \n" + "frequency response. Try to use another topology.")); + } + } + break; + case 2 : { + SallenKey sk(ffunc,ftyp,par); + if (ffunc==Filter::User) { + sk.set_TrFunc(coeffA,coeffB); + } + ok = sk.calcFilter(); + sk.createPolesZerosList(lst); + sk.createPartList(lst); + txtResult->setText(lst.join("\n")); + if (ok) { + sk.createSchematic(s); + } else { + errorMessage(tr("Unable to implement filter with such parameters and topology \n" + "Change parapeters and/or topology and try again!")); + } + } + break; + case 3 : errorMessage(tr("Function will be implemented in future version")); + break; + default: break; + } + + if (ok) { + txtResult->append(tr("\nFilter calculation was sucessfull")); + } else { + txtResult->append(tr("\nFilter calculation terminated with error")); + } + + QClipboard *cb = QApplication::clipboard(); + cb->setText(s); + +} + +void QucsActiveFilter::slotUpdateResponse() +{ + QString s = ":/images/AFR.svg"; + + switch (cbxResponse->currentIndex()) { + case 0 : + s = ":/images/AFR.svg"; + ftyp = Filter::LowPass; + break; + case 1 : s = ":/images/high-pass.svg"; + ftyp = Filter::HighPass; + break; + case 2 : ftyp = Filter::BandPass; + break; + case 3 : ftyp = Filter::BandStop; + break; + default: ftyp = Filter::NoFilter; + break; + } + + QSvgRenderer *ren = new QSvgRenderer(s); + QSize sz = ren->defaultSize(); + sz *= 1.1; + delete ren; + + imgAFR->load(s); + imgAFR->setFixedSize(sz); +} + +void QucsActiveFilter::slotUpdateSchematic() +{ + slotUpdateResponse(); + QString s; + switch (cbxFilterType->currentIndex()) { + case 0 : s = ":images/cauer.svg"; + break; + case 1 : if (ftyp==Filter::HighPass) { + s = ":/images/mfb-highpass.svg"; + } else if (ftyp==Filter::LowPass) { + s = ":/images/mfb-lowpass.svg"; + } + break; + case 2 : if (ftyp==Filter::HighPass) { + s = ":/images/sk-highpass.svg"; + } else if (ftyp==Filter::LowPass) { + s = ":/images/sk-lowpass.svg"; + } + break; + case 3 : s = ":/images/mfb-lowpass.svg"; + break; + default: + break; + } + + QSvgRenderer *ren = new QSvgRenderer(s); + QSize sz = ren->defaultSize(); + sz *= 0.65; + delete ren; + + sch_pic->load(s); + sch_pic->setFixedSize(sz); +} + +void QucsActiveFilter::slotSwitchParameters() +{ + if (cbxFilterFunc->currentIndex()==0) { + edtA1->setEnabled(true); + edtPassbRpl->setEnabled(false); + } else { + edtA1->setEnabled(false); + edtPassbRpl->setEnabled(true); + } + + if ((cbxFilterFunc->currentIndex()==3)||(cbxFilterFunc->currentIndex()==2)) { + cbxFilterType->setCurrentIndex(0); + cbxFilterType->setDisabled(true); + } else { + cbxFilterType->setDisabled(false); + } + + if ((cbxFilterFunc->currentIndex())==4) { + edtOrder->setEnabled(true); + } else { + edtOrder->setEnabled(false); + } + + if ((cbxFilterFunc->currentIndex()==5)||(cbxFilterFunc->currentIndex()==4)) { + btnDefineTransferFunc->setEnabled(true); + edtF2->setEnabled(false); + edtPassbRpl->setEnabled(false); + edtA1->setEnabled(false); + edtA2->setEnabled(false); + edtKv->setEnabled(false); + } else { + btnDefineTransferFunc->setEnabled(false); + edtF2->setEnabled(true); + edtPassbRpl->setEnabled(true); + edtA1->setEnabled(true); + edtA2->setEnabled(true); + edtKv->setEnabled(true); + } +} + +void QucsActiveFilter::slotDefineTransferFunc() +{ + + TransferFuncDialog *trfuncdlg = new TransferFuncDialog(coeffA,coeffB,this); + if (trfuncdlg->exec()) { + trfuncdlg->getCoeffs(coeffA,coeffB); + } + delete trfuncdlg; +} + +void QucsActiveFilter::errorMessage(QString str) +{ + QMessageBox* msg = new QMessageBox(QMessageBox::Critical,tr("Active filter design"), + str, + QMessageBox::Ok); + msg->exec(); + delete msg; +} diff --git a/qucs/qucsactivefilter/qucsactivefilter.h b/qucs/qucsactivefilter/qucsactivefilter.h new file mode 100644 index 0000000000..b848c36172 --- /dev/null +++ b/qucs/qucsactivefilter/qucsactivefilter.h @@ -0,0 +1,95 @@ +#ifndef FILTERSINTEZ_H +#define FILTERSINTEZ_H + +#include +#include +#include +#include +#include "filter.h" + +class QucsActiveFilter : public QMainWindow +{ + Q_OBJECT + +private: + QLabel *lblInputData; + QLabel *lblA1; + QLabel *lblA2; + QLabel *lblF1; + QLabel *lblF2; + QLabel *lblTyp; + QLabel *lblKv; + QLabel *lblRpl1; + QLabel *lblRpl2; + QLabel *lblResp; + + QLabel *lblAFR; + QLabel *lblTopology; + QLabel *lblOrder; + + QLineEdit *edtA1; // passband attenuation A1 + QLineEdit *edtA2; // stopband attenuation A2 + QLineEdit *edtF1; // passband cutoff frequency F1 + QLineEdit *edtF2; // stopband cutoff frequency F2 + QLineEdit *edtKv; + QLineEdit *edtPassbRpl; + QLineEdit *edtStopbRpl; + QLineEdit *edtOrder; + + QComboBox *cbxFilterFunc; + QPushButton *btnCalcSchematic; + QPushButton *btnDefineTransferFunc; + + QLabel *lblResult; + QTextEdit *txtResult; + + QSvgWidget *imgAFR; + QLabel *lblSch; + + /*QRadioButton *btnLowPass; + QRadioButton *btnHighPass; + QRadioButton *btnBandPass; + QRadioButton *btnBandStop;*/ + + QComboBox *cbxFilterType; + QComboBox *cbxResponse; + + QPushButton *btnElements; + //QPushButton *btnPassive; + + QHBoxLayout *top; + QVBoxLayout *left; + QVBoxLayout *center; + QVBoxLayout *right; + QVBoxLayout *top1; + + QSvgWidget *sch_pic; + + QWidget *zenter; + + void errorMessage(QString s); + + QVector< std::complex > Poles; + + float Fc; + int Nfil; + + QVector coeffB; + QVector coeffA; + + Filter::FType ftyp; + +private slots: + + void slotUpdateSchematic(); + void slotUpdateResponse(); + void slotCalcSchematic(); + void slotSwitchParameters(); + void slotDefineTransferFunc(); + +public: + QucsActiveFilter(QWidget *parent = 0); + ~QucsActiveFilter(); +}; + +#endif // FILTERSINTEZ_H diff --git a/qucs/qucsactivefilter/qucsactivefilter.qrc b/qucs/qucsactivefilter/qucsactivefilter.qrc new file mode 100644 index 0000000000..8d3ecb2ab4 --- /dev/null +++ b/qucs/qucsactivefilter/qucsactivefilter.qrc @@ -0,0 +1,12 @@ + + + sk-highpass.svg + sk-lowpass.svg + mfb-lowpass.svg + mfb-highpass.svg + AFR.svg + cauer.svg + high-pass.svg + trfunc.png + + diff --git a/qucs/qucsactivefilter/sallenkey.cpp b/qucs/qucsactivefilter/sallenkey.cpp new file mode 100644 index 0000000000..b6cdc5ec5b --- /dev/null +++ b/qucs/qucsactivefilter/sallenkey.cpp @@ -0,0 +1,265 @@ +#include "sallenkey.h" +#include + +SallenKey::SallenKey(Filter::FilterFunc ffunc_, Filter::FType type_, FilterParam par) : + Filter(ffunc_, type_, par) +{ + Nr1 = 4; + Nc1 = 2; + Nop1 = 1; +} + + +void SallenKey::calcLowPass() +{ + float R1,R2,R3,R4,C1,C2; + float Wc = 2*M_PI*Fc; + float Nst = order/2 + order%2; + float Kv1 = pow(Kv,1.0/Nst); + qDebug()<calcFirstOrder(); +} + + +void SallenKey::calcHighPass() +{ + float R1,R2,R3,R4,C1; + float Wc = 2*M_PI*Fc; + + float Nst = order/2 + order%2; + float Kv1 = pow(Kv,1.0/Nst); + + for (int k=1; k <= order/2; k++) { + + float re = Poles.at(k-1).real(); + float im = Poles.at(k-1).imag(); + float B = -2.0*re; + float C = re*re + im*im; + + qDebug()<\n").arg((10.0*Fc)/1000.0); + s += "<.DC DC1 1 280 410 0 61 0 0 \"26.85\" 0 \"0.001\" 0 \"1 pA\" 0 \"1 uV\" 0 \"no\" 0 \"150\" 0 \"no\" 0 \"none\" 0 \"CroutLU\" 0>\n"; + s += "\n"; + s += QString("\n").arg(20+dx); + s += QString("\n").arg(20+dx); + for (int i=1; i<=N2ord; i++) { + stage = Sections.at(i-1); + qDebug()<\n").arg(stage.N).arg(370+dx); + s += QString("\n").arg(270+dx); + s += QString("\n").arg(320+dx); + s += QString("\n").arg(3+(i-1)*N_R).arg(320+dx).arg(stage.R3,0,'f',3); + s += QString("\n").arg(2+(i-1)*N_C).arg(200+dx).arg(C2,0,'f',3).arg(suffix2); + s += QString("\n").arg(1+(i-1)*N_C).arg(100+dx).arg(C1,0,'f',3).arg(suffix1); + s += QString("\n").arg(2+(i-1)*N_R).arg(270+dx).arg(stage.R2,0,'f',3); + s += QString("\n").arg(1+(i-1)*N_R).arg(330+dx).arg(stage.R1,0,'f',3); + s += QString("\n").arg(4+(i-1)*N_R).arg(410+dx).arg(stage.R4,0,'f',3); + dx += 510; + } + + if (N1stOrd!=0) { + qDebug()<\n").arg(20+dx).arg(20+dx); + s += QString("<%1 190 %2 190 \"in\" %3 160 18 \"\">\n").arg(20+dx).arg(70+dx).arg(70+dx); + for (int i=1; i<=N2ord; i++) { + if (i!=1) { + s += QString("<%1 190 %2 190 \"\" 0 0 0 \"\">\n").arg(dx-20).arg(70+dx); + s += QString("<%1 190 %2 160 \"\" 0 0 0 \"\">\n").arg(dx-20).arg(dx-20); + s += QString("<%1 160 %2 160 \"\" 0 0 0 \"\">\n").arg(dx-20).arg(dx-50); + } + s += QString("<%1 70 %2 70 \"\" 0 0 0 \"\">\n").arg(360+dx).arg(460+dx); + if ((2*i)==order) { + s += QString("<%1 70 %2 160 \"out\" %3 90 51 \"\">\n").arg(460+dx).arg(460+dx).arg(490+dx); + } else { + s += QString("<%1 70 %2 160 \"\" 0 0 0 \"\">\n").arg(460+dx).arg(460+dx); + } + s += QString("<%1 160 %2 160 \"\" 0 0 0 \"\">\n").arg(410+dx).arg(460+dx); + s += QString("<%1 260 %2 260 \"\" 0 0 0 \"\">\n").arg(440+dx).arg(460+dx); + s += QString("<%1 160 %2 260 \"\" 0 0 0 \"\">\n").arg(460+dx).arg(460+dx); + s += QString("<%1 190 %2 210 \"\" 0 0 0 \"\">\n").arg(270+dx).arg(270+dx); + s += QString("<%1 190 %2 190 \"\" 0 0 0 \"\">\n").arg(230+dx).arg(270+dx); + s += QString("<%1 190 %2 190 \"\" 0 0 0 \"\">\n").arg(130+dx).arg(150+dx); + s += QString("<%1 190 %2 190 \"\" 0 0 0 \"\">\n").arg(150+dx).arg(170+dx); + s += QString("<%1 70 %2 190 \"\" 0 0 0 \"\">\n").arg(150+dx).arg(150+dx); + s += QString("<%1 70 %2 70 \"\" 0 0 0 \"\">\n").arg(150+dx).arg(300+dx); + s += QString("<%1 140 %2 190 \"\" 0 0 0 \"\">\n").arg(270+dx).arg(270+dx); + s += QString("<%1 140 %2 140 \"\" 0 0 0 \"\">\n").arg(270+dx).arg(340+dx); + s += QString("<%1 180 %2 260 \"\" 0 0 0 \"\">\n").arg(320+dx).arg(320+dx); + s += QString("<%1 180 %2 180 \"\" 0 0 0 \"\">\n").arg(320+dx).arg(340+dx); + s += QString("<%1 260 %2 260 \"\" 0 0 0 \"\">\n").arg(320+dx).arg(380+dx); + s += QString("<%1 260 %2 310 \"\" 0 0 0 \"\">\n").arg(320+dx).arg(320+dx); + dx += 510; + } + + if (N1stOrd!=0) { + createFirstOrderWires(s,dx,160); + } + + s += "\n"; +} + +void SallenKey::createLowPassSchematic(QString &s) +{ + int const N_R=4; // number of resisitors in 2-order Sallen-Key stage + int const N_C=2; // number of capacitors in 2-order Sallen-Key stage + RC_elements stage; + int dx = 0; + int N2ord = order/2; // number of 2-nd order stages + int N1stOrd = order%2; // number of 1-st order stages + + s += "\n"; + s += "\n"; + s += QString("<.AC AC1 1 30 410 0 61 0 0 \"lin\" 1 \"1 Hz\" 1 \"%1 kHz\" 1 \"501\" 1 \"no\" 0>\n").arg((10.0*Fc)/1000.0); + s += "<.DC DC1 1 280 410 0 61 0 0 \"26.85\" 0 \"0.001\" 0 \"1 pA\" 0 \"1 uV\" 0 \"no\" 0 \"150\" 0 \"no\" 0 \"none\" 0 \"CroutLU\" 0>\n"; + s += "\n"; + s += QString("\n").arg(20+dx); + s += QString("\n").arg(20+dx); + for (int i=1; i<=N2ord; i++) { + stage = Sections.at(i-1); + qDebug()<\n").arg(stage.N).arg(370+dx); + s += QString("\n").arg(270+dx); + s += QString("\n").arg(320+dx); + s += QString("\n").arg(2+(i-1)*N_C).arg(330+dx).arg(C2,0,'f',3).arg(suffix2); + s += QString("\n").arg(1+(i-1)*N_C).arg(270+dx).arg(C1,0,'f',3).arg(suffix1); + s += QString("\n").arg(2+(i-1)*N_R).arg(200+dx).arg(stage.R2,0,'f',3); + s += QString("\n").arg(1+(i-1)*N_R).arg(100+dx).arg(stage.R1,0,'f',3); + s += QString("\n").arg(3+(i-1)*N_R).arg(320+dx).arg(stage.R3,0,'f',3); + s += QString("\n").arg(4+(i-1)*N_R).arg(410+dx).arg(stage.R4,0,'f',3); + dx += 510; + } + + if (N1stOrd!=0) { + createFirstOrderComponentsLPF(s,Sections.last(),dx); + } + + s += "\n"; + s += "\n"; + dx = 0; + s += QString("<%1 190 %2 230 \"\" 0 0 0 \"\">\n").arg(20+dx).arg(20+dx); + s += QString("<%1 190 %2 190 \"in\" %3 160 18 \"\">\n").arg(20+dx).arg(70+dx).arg(70+dx); + for (int i=1; i<=N2ord; i++) { + if (i!=1) { + s += QString("<%1 190 %2 190 \"\" 0 0 0 \"\">\n").arg(dx-20).arg(70+dx); + s += QString("<%1 190 %2 160 \"\" 0 0 0 \"\">\n").arg(dx-20).arg(dx-20); + s += QString("<%1 160 %2 160 \"\" 0 0 0 \"\">\n").arg(dx-20).arg(dx-50); + } + s += QString("<%1 70 %2 70 \"\" 0 0 0 \"\">\n").arg(360+dx).arg(460+dx); + if ((2*i)==order) { + s += QString("<%1 70 %2 160 \"out\" %3 90 51 \"\">\n").arg(460+dx).arg(460+dx).arg(490+dx); + } else { + s += QString("<%1 70 %2 160 \"\" 0 0 0 \"\">\n").arg(460+dx).arg(460+dx); + } + s += QString("<%1 160 %2 160 \"\" 0 0 0 \"\">\n").arg(410+dx).arg(460+dx); + s += QString("<%1 260 %2 260 \"\" 0 0 0 \"\">\n").arg(440+dx).arg(460+dx); + s += QString("<%1 160 %2 260 \"\" 0 0 0 \"\">\n").arg(460+dx).arg(460+dx); + s += QString("<%1 190 %2 210 \"\" 0 0 0 \"\">\n").arg(270+dx).arg(270+dx); + s += QString("<%1 190 %2 190 \"\" 0 0 0 \"\">\n").arg(230+dx).arg(270+dx); + s += QString("<%1 190 %2 190 \"\" 0 0 0 \"\">\n").arg(130+dx).arg(150+dx); + s += QString("<%1 190 %2 190 \"\" 0 0 0 \"\">\n").arg(150+dx).arg(170+dx); + s += QString("<%1 70 %2 190 \"\" 0 0 0 \"\">\n").arg(150+dx).arg(150+dx); + s += QString("<%1 70 %2 70 \"\" 0 0 0 \"\">\n").arg(150+dx).arg(300+dx); + s += QString("<%1 140 %2 190 \"\" 0 0 0 \"\">\n").arg(270+dx).arg(270+dx); + s += QString("<%1 140 %2 140 \"\" 0 0 0 \"\">\n").arg(270+dx).arg(340+dx); + s += QString("<%1 180 %2 260 \"\" 0 0 0 \"\">\n").arg(320+dx).arg(320+dx); + s += QString("<%1 180 %2 180 \"\" 0 0 0 \"\">\n").arg(320+dx).arg(340+dx); + s += QString("<%1 260 %2 260 \"\" 0 0 0 \"\">\n").arg(320+dx).arg(380+dx); + s += QString("<%1 260 %2 310 \"\" 0 0 0 \"\">\n").arg(320+dx).arg(320+dx); + dx += 510; + } + + if (N1stOrd!=0) { + createFirstOrderWires(s,dx,160); + } + + s += "\n"; +} diff --git a/qucs/qucsactivefilter/sallenkey.h b/qucs/qucsactivefilter/sallenkey.h new file mode 100644 index 0000000000..fe59f3c3c7 --- /dev/null +++ b/qucs/qucsactivefilter/sallenkey.h @@ -0,0 +1,27 @@ +#ifndef SALLENKEY_H +#define SALLENKEY_H + +#include +#include +#include +#include "filter.h" + + +class SallenKey : public Filter +{ + +protected: + + void calcHighPass(); + void calcLowPass(); + void createHighPassSchematic(QString &s); + void createLowPassSchematic(QString &s); + +public: + SallenKey(Filter::FilterFunc ffunc_, Filter::FType type_, FilterParam par); + + //void createSchematic(QString &s); + +}; + +#endif // SALLENKEY_H diff --git a/qucs/qucsactivefilter/schcauer.cpp b/qucs/qucsactivefilter/schcauer.cpp new file mode 100644 index 0000000000..097f7efe69 --- /dev/null +++ b/qucs/qucsactivefilter/schcauer.cpp @@ -0,0 +1,211 @@ +#include "schcauer.h" + +SchCauer::SchCauer(Filter::FilterFunc ffunc_, Filter::FType type_, FilterParam par) : + Filter(ffunc_, type_, par) +{ + Nr1 = 5; + Nop1 = 3; + Nc1 = 2; +} + +void SchCauer::calcLowPass() +{ + float R1,R2,R3,R4,R5,C1,C2; + float Wc = 2*M_PI*Fc; + float Nst = order/2 + order%2; + float Kv1 = pow(Kv,1.0/Nst); + //qDebug()<calcFirstOrder(); +} + + +void SchCauer::calcHighPass() +{ + float R1,R2,R3,R4,R5,C1,C2; + float Wc = 2*M_PI*Fc; + float Nst = order/2 + order%2; + float Kv1 = pow(Kv,1.0/Nst); + //qDebug()<calcFirstOrder(); +} + + +void SchCauer::createLowPassSchematic(QString &s) +{ + createGenericSchematic(s); +} + +void SchCauer::createHighPassSchematic(QString &s) +{ + createGenericSchematic(s); +} + +void SchCauer::createGenericSchematic(QString &s) +{ + RC_elements stage; + int dx = 0; + int N2ord = order/2; // number of 2-nd order stages + int N1stOrd = order%2; // number of 1-st order stages + + s += "\n"; + s += "\n"; + s += "\n"; + s += "<.DC DC1 1 40 510 0 61 0 0 \"26.85\" 0 \"0.001\" 0 \"1 pA\" 0 \"1 uV\" 0 \"no\" 0 \"150\" 0 \"no\" 0 \"none\" 0 \"CroutLU\" 0>\n"; + s += "\n"; + s += "<.AC AC1 1 320 510 0 61 0 0 \"lin\" 1 \"1 Hz\" 1 \"10 kHz\" 1 \"5001\" 1 \"no\" 0>\n"; + s += "\n"; + + for (int i=1; i<=N2ord; i++) { + stage = Sections.at(i-1); + qDebug()<\n").arg(1+(i-1)*Nop1).arg(270+dx); + s += QString("\n").arg(2+(i-1)*Nop1).arg(300+dx); + s += QString("\n").arg(3+(i-1)*Nop1).arg(560+dx); + s += QString("\n").arg(1+(i-1)*Nc1).arg(330+dx).arg(C1,0,'f',3).arg(suffix1); + s += QString("\n").arg(2+(i-1)*Nc1).arg(450+dx).arg(C2,0,'f',3).arg(suffix2); + s += QString("\n").arg(1+(i-1)*Nr1).arg(180+dx).arg(stage.R1,0,'f',3); + s += QString("\n").arg(2+(i-1)*Nr1).arg(410+dx).arg(stage.R2,0,'f',3); + s += QString("\n").arg(3+(i-1)*Nr1).arg(440+dx).arg(stage.R3,0,'f',3); + s += QString("\n").arg(4+(i-1)*Nr1).arg(360+dx).arg(stage.R4,0,'f',3); + s += QString("\n").arg(5+(i-1)*Nr1).arg(190+dx).arg(stage.R5,0,'f',3); + s += QString("\n").arg(240+dx); + s += QString("\n").arg(250+dx); + + dx += 580; + } + + if (N1stOrd!=0) { + if (ftype==Filter::LowPass) { + createFirstOrderComponentsLPF(s,Sections.last(),dx+80); + } else if (ftype==Filter::HighPass) { + createFirstOrderComponentsHPF(s,Sections.last(),dx+80); + } + + } + + s += "\n"; + s += "\n"; + dx = 0; + s += "<80 220 140 220 \"in\" 120 170 0 \"\">\n"; + s += "<80 220 80 260 \"\" 0 0 0 \"\">\n"; + + for (int i=1; i<=N2ord; i++) { + if (i!=1) { + s += QString("<%1 260 %2 260 \"\" 0 0 0 \"\">\n").arg(dx+30).arg(140+dx); + } + s += QString("<%1 370 %2 370 \"\" 0 0 0 \"\">\n").arg(500+dx).arg(610+dx); + s += QString("<%1 220 %2 220 \"\" 0 0 0 \"\">\n").arg(210+dx).arg(220+dx); + s += QString("<%1 280 %2 370 \"\" 0 0 0 \"\">\n").arg(500+dx).arg(500+dx); + s += QString("<%1 280 %2 280 \"\" 0 0 0 \"\">\n").arg(500+dx).arg(530+dx); + s += QString("<%1 260 %2 260 \"\" 0 0 0 \"\">\n").arg(600+dx).arg(610+dx); + s += QString("<%1 260 %2 370 \"\" 0 0 0 \"\">\n").arg(610+dx).arg(610+dx); + s += QString("<%1 240 %2 240 \"\" 0 0 0 \"\">\n").arg(440+dx).arg(480+dx); + s += QString("<%1 400 %2 400 \"\" 0 0 0 \"\">\n").arg(340+dx).arg(420+dx); + s += QString("<%1 380 %2 380 \"\" 0 0 0 \"\">\n").arg(220+dx).arg(260+dx); + s += QString("<%1 380 %2 380 \"\" 0 0 0 \"\">\n").arg(260+dx).arg(270+dx); + s += QString("<%1 150 %2 220 \"\" 0 0 0 \"\">\n").arg(220+dx).arg(220+dx); + s += QString("<%1 150 %2 150 \"\" 0 0 0 \"\">\n").arg(220+dx).arg(300+dx); + s += QString("<%1 240 %2 240 \"\" 0 0 0 \"\">\n").arg(310+dx).arg(380+dx); + s += QString("<%1 260 %2 290 \"\" 0 0 0 \"\">\n").arg(240+dx).arg(240+dx); + s += QString("<%1 220 %2 220 \"\" 0 0 0 \"\">\n").arg(220+dx).arg(240+dx); + s += QString("<%1 420 %2 420 \"\" 0 0 0 \"\">\n").arg(250+dx).arg(270+dx); + s += QString("<%1 420 %2 440 \"\" 0 0 0 \"\">\n").arg(250+dx).arg(250+dx); + if ((2*i)==order) { + s += QString("<%1 110 %2 260 \"out\" %3 160 101 \"\">\n").arg(610+dx).arg(610+dx).arg(540+dx); + } else { + s += QString("<%1 110 %2 260 \"\" 0 0 0 \"\">\n").arg(610+dx).arg(610+dx); + } + s += QString("<%1 110 %2 110 \"\" 0 0 0 \"\">\n").arg(470+dx).arg(610+dx); + s += QString("<%1 110 %2 150 \"\" 0 0 0 \"\">\n").arg(220+dx).arg(220+dx); + s += QString("<%1 110 %2 110 \"\" 0 0 0 \"\">\n").arg(220+dx).arg(410+dx); + s += QString("<%1 220 %2 220 \"\" 0 0 0 \"\">\n").arg(140+dx).arg(150+dx); + s += QString("<%1 220 %2 260 \"\" 0 0 0 \"\">\n").arg(140+dx).arg(140+dx); + s += QString("<%1 260 %2 380 \"\" 0 0 0 \"\">\n").arg(140+dx).arg(140+dx); + s += QString("<%1 380 %2 380 \"\" 0 0 0 \"\">\n").arg(140+dx).arg(160+dx); + s += QString("<%1 240 %2 240 \"\" 0 0 0 \"\">\n").arg(480+dx).arg(530+dx); + s += QString("<%1 240 %2 400 \"\" 0 0 0 \"\">\n").arg(480+dx).arg(480+dx); + s += QString("<%1 320 %2 400 \"\" 0 0 0 \"\">\n").arg(420+dx).arg(420+dx); + s += QString("<%1 320 %2 320 \"\" 0 0 0 \"\">\n").arg(390+dx).arg(420+dx); + s += QString("<%1 320 %2 380 \"\" 0 0 0 \"\">\n").arg(260+dx).arg(260+dx); + s += QString("<%1 320 %2 320 \"\" 0 0 0 \"\">\n").arg(260+dx).arg(330+dx); + s += QString("<%1 150 %2 150 \"\" 0 0 0 \"\">\n").arg(360+dx).arg(380+dx); + s += QString("<%1 150 %2 240 \"\" 0 0 0 \"\">\n").arg(380+dx).arg(380+dx); + + dx +=580; + } + + if (N1stOrd!=0) { + createFirstOrderWires(s,dx+80,260); + } + + s += "\n"; +} diff --git a/qucs/qucsactivefilter/schcauer.h b/qucs/qucsactivefilter/schcauer.h new file mode 100644 index 0000000000..689e721c1d --- /dev/null +++ b/qucs/qucsactivefilter/schcauer.h @@ -0,0 +1,26 @@ +#ifndef SCHCAUER_H +#define SCHCAUER_H + +#include +#include +#include +#include "filter.h" + +class SchCauer : public Filter // Cauer 2-order section +{ + +private: + void createGenericSchematic(QString &s); + +protected: + + void calcHighPass(); + void calcLowPass(); + void createHighPassSchematic(QString &s); + void createLowPassSchematic(QString &s); + +public: + SchCauer(Filter::FilterFunc ffunc_, Filter::FType type_, FilterParam par); +}; + +#endif // SCHCAUER_H diff --git a/qucs/qucsactivefilter/sk-highpass.svg b/qucs/qucsactivefilter/sk-highpass.svg new file mode 100644 index 0000000000..0b52a24109 --- /dev/null +++ b/qucs/qucsactivefilter/sk-highpass.svg @@ -0,0 +1,1129 @@ + + + + + + image/svg+xml + + + + + + Qt Svg Document + Generated with Qt + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + C1 + + + + + + + + + + + C2 + + + + + + + + + + + R1 + + + + + + + + + + + R2 + + + + + + + + + + + R4 + + + + + + + + + + + R3 + + + + + + + + + + + + + + + + + + + + + + + + + + + V1 + + + + + + + + + + + + + + + + + + OP1 + + + + + + + output + + + + + + + + + + + + + + + + + + + + + + + + + + + input + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/qucs/qucsactivefilter/sk-lowpass.svg b/qucs/qucsactivefilter/sk-lowpass.svg new file mode 100644 index 0000000000..c9ae3d2194 --- /dev/null +++ b/qucs/qucsactivefilter/sk-lowpass.svg @@ -0,0 +1,1129 @@ + + + + + + image/svg+xml + + + + + + Qt Svg Document + Generated with Qt + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + R4 + + + + + + + + + + + R3 + + + + + + + + + + + + + + + + + + + + + + + + + + + V1 + + + + + + + + + + + + + + + + + + OP1 + + + + + + + + + + + R1 + + + + + + + + + + + R2 + + + + + + + + + + + C2 + + + + + + + + + + + C1 + + + + + + + + + + + + + + + + + input + + + + + + + + + + + + + + + + + output + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/qucs/qucsactivefilter/transferfuncdialog.cpp b/qucs/qucsactivefilter/transferfuncdialog.cpp new file mode 100644 index 0000000000..056a6d20cd --- /dev/null +++ b/qucs/qucsactivefilter/transferfuncdialog.cpp @@ -0,0 +1,139 @@ +#include "transferfuncdialog.h" + +TransferFuncDialog::TransferFuncDialog(QVector &a, QVector &b, QWidget *parent) : + QDialog(parent) +{ + this->setWindowTitle(tr("Define filter transfer function")); + + imgTrfuncEq = new QLabel; + imgTrfuncEq->setPixmap(QPixmap(":images/trfunc.png")); + //imgTrfuncEq->setScaledContents(true); + + + + lblB = new QLabel(tr("Numenator b[i]=")); + lblA = new QLabel(tr("Denomenator a[i]=")); + + QStringList indexes; + for (int i=0;i<50;i++) { + indexes<setColumnCount(head1.count()); + tblA->setRowCount(50); + tblA->setHorizontalHeaderLabels(head1); + tblA->setVerticalHeaderLabels(indexes); + tblA->setFixedWidth(150); + + if (!a.isEmpty()) { + long double num; + int i = a.count()-1; + + foreach (num,a) { + QTableWidgetItem *it = new QTableWidgetItem(QString::number((double)num)); + tblA->setItem(i,0,it); + i--; + } + } + + + tblB = new QTableWidget; + QStringList head2; + head2<setColumnCount(head2.count()); + tblB->setRowCount(50); + tblB->setHorizontalHeaderLabels(head2); + tblB->setVerticalHeaderLabels(indexes); + tblB->setFixedWidth(150); + + if (!b.isEmpty()) { + long double num; + int i = b.count()-1; + + foreach (num,b) { + QTableWidgetItem *it = new QTableWidgetItem(QString::number((double)num)); + tblB->setItem(i,0,it); + i--; + } + } + + btnAccept = new QPushButton(tr("Accept")); + connect(btnAccept,SIGNAL(clicked()),this,SLOT(accept())); + connect(btnAccept,SIGNAL(clicked()),this,SLOT(slotCheckCoeffs())); + btnCancel = new QPushButton(tr("Cancel")); + connect(btnCancel,SIGNAL(clicked()),this,SLOT(reject())); + + low1 = new QVBoxLayout; + low1->addWidget(lblB); + low1->addWidget(tblB); + + low2 = new QVBoxLayout; + low2->addWidget(lblA); + low2->addWidget(tblA); + + low3 = new QHBoxLayout; + low3->addWidget(btnAccept); + low3->addWidget(btnCancel); + + top = new QHBoxLayout; + top->addLayout(low1); + top->addLayout(low2); + + top1 = new QVBoxLayout; + top1->addWidget(imgTrfuncEq); + top1->addLayout(top); + top1->addLayout(low3); + + + this->setLayout(top1); + +} + +void TransferFuncDialog::getCoeffs(QVector &a, QVector &b) +{ + a.clear(); + b.clear(); + + // bool a0 = false; + + for (int i=tblA->rowCount()-1;i>=0;i--) { + QTableWidgetItem *itm = tblA->item(i,0); + if (itm!=0) { + + //if (!a0) a0 = true; + + QString str = itm->text(); + //if ((str.isEmpty())) break; + bool ok; + long double n = (long double) str.toDouble(&ok); + if (ok) a.append(n); + } + //else if (a0) a.append(0.0); + } + + + //bool b0 = true; + + for (int i=tblB->rowCount()-1;i>=0;i--) { + QTableWidgetItem *itm = tblB->item(i,0); + if (itm!=0) { + + //if (!b0) b0 = true; + + QString str = itm->text(); + //if ((str.isEmpty())) break; + bool ok; + long double n = (long double) str.toDouble(&ok); + if (ok) b.append(n); + } + //else if (b0) b.append(0.0); + } +} + +void TransferFuncDialog::slotCheckCoeffs() +{ + +} diff --git a/qucs/qucsactivefilter/transferfuncdialog.h b/qucs/qucsactivefilter/transferfuncdialog.h new file mode 100644 index 0000000000..f91a8fa869 --- /dev/null +++ b/qucs/qucsactivefilter/transferfuncdialog.h @@ -0,0 +1,41 @@ +#ifndef TRANSFERFUNCDIALOG_H +#define TRANSFERFUNCDIALOG_H + +#include +#include + +class TransferFuncDialog : public QDialog +{ + Q_OBJECT + +private: + QLabel *lblA; + QLabel *lblB; + QLabel *imgTrfuncEq; + QTableWidget *tblA; + QTableWidget *tblB; + + QPushButton *btnAccept; + QPushButton *btnCancel; + + QHBoxLayout *top; + QVBoxLayout *top1; + QVBoxLayout *low1; + QVBoxLayout *low2; + QHBoxLayout *low3; + +public: + explicit TransferFuncDialog(QVector &a, QVector &b, QWidget *parent = 0); + + void getCoeffs(QVector &a, QVector &b); + +signals: + +private slots: + void slotCheckCoeffs(); + +public slots: + +}; + +#endif // TRANSFERFUNCDIALOG_H diff --git a/qucs/qucsactivefilter/trfunc.png b/qucs/qucsactivefilter/trfunc.png new file mode 100644 index 0000000000..2ecbb4e143 Binary files /dev/null and b/qucs/qucsactivefilter/trfunc.png differ